client.c revision 5456796b1a2aedd2d6345944b73ac41aeb8cb589
19ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/*
29ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
39ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * Intel Management Engine Interface (Intel MEI) Linux driver
49ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * Copyright (c) 2003-2012, Intel Corporation.
59ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
69ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * This program is free software; you can redistribute it and/or modify it
79ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * under the terms and conditions of the GNU General Public License,
89ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * version 2, as published by the Free Software Foundation.
99ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
109ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * This program is distributed in the hope it will be useful, but WITHOUT
119ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
129ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * more details.
149ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
159ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
169ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include <linux/pci.h>
189ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include <linux/sched.h>
199ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include <linux/wait.h>
209ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include <linux/delay.h>
2104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler#include <linux/pm_runtime.h>
229ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
239ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include <linux/mei.h>
249ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
259ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include "mei_dev.h"
269ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler#include "hbm.h"
2790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler#include "client.h"
2890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
2990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler/**
3090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_me_cl_by_uuid - locate index of me client
3190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *
3290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * @dev: mei device
33a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin *
34a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin * Locking: called under "dev->device_lock" lock
35a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin *
3690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * returns me client index or -ENOENT if not found
3790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler */
3890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
3990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler{
40a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin	int i;
4190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
4290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	for (i = 0; i < dev->me_clients_num; ++i)
4390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		if (uuid_le_cmp(*uuid,
44a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin				dev->me_clients[i].props.protocol_name) == 0)
45a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin			return i;
4690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
47a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin	return -ENOENT;
4890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler}
4990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
5090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
5190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler/**
5290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_me_cl_by_id return index to me_clients for client_id
5390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *
5490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * @dev: the device structure
5590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * @client_id: me client id
5690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *
5790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * Locking: called under "dev->device_lock" lock
5890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *
5990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * returns index on success, -ENOENT on failure.
6090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler */
6190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
6290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
6390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler{
6490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	int i;
65a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin
6690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	for (i = 0; i < dev->me_clients_num; i++)
6790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		if (dev->me_clients[i].client_id == client_id)
68a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin			return i;
6990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
70a27a76d3c07de08a0d0d298b6bc280c5b820e997Alexander Usyskin	return -ENOENT;
7190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler}
729ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
739ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
749ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
75cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * mei_cl_cmp_id - tells if the clients are the same
769ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
77cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @cl1: host client 1
78cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @cl2: host client 2
79cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler *
80cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * returns true  - if the clients has same host and me ids
81cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler *         false - otherwise
82cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler */
83cc99ecfdac01215594c73907726b12f251c21e20Tomas Winklerstatic inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
84cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler				const struct mei_cl *cl2)
85cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler{
86cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	return cl1 && cl2 &&
87cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler		(cl1->host_client_id == cl2->host_client_id) &&
88cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler		(cl1->me_client_id == cl2->me_client_id);
89cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler}
90cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler
91cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler/**
92cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * mei_io_list_flush - removes cbs belonging to cl.
93cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler *
94cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @list:  an instance of our list structure
95cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @cl:    host client, can be NULL for flushing the whole list
96cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @free:  whether to free the cbs
979ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
98cc99ecfdac01215594c73907726b12f251c21e20Tomas Winklerstatic void __mei_io_list_flush(struct mei_cl_cb *list,
99cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler				struct mei_cl *cl, bool free)
1009ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
1019ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl_cb *cb;
1029ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl_cb *next;
1039ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
104cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	/* enable removing everything if no cl is specified */
1059ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	list_for_each_entry_safe(cb, next, &list->list, list) {
106cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler		if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) {
1079ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			list_del(&cb->list);
108cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler			if (free)
109cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler				mei_io_cb_free(cb);
110cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler		}
1119ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
1129ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
1139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1149ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
115cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * mei_io_list_flush - removes list entry belonging to cl.
116cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler *
117cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @list:  An instance of our list structure
118cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @cl: host client
119cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler */
1205456796b1a2aedd2d6345944b73ac41aeb8cb589Alexander Usyskinvoid mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
121cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler{
122cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	__mei_io_list_flush(list, cl, false);
123cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler}
124cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler
125cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler
126cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler/**
127cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * mei_io_list_free - removes cb belonging to cl and free them
128cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler *
129cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @list:  An instance of our list structure
130cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler * @cl: host client
131cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler */
132cc99ecfdac01215594c73907726b12f251c21e20Tomas Winklerstatic inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
133cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler{
134cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	__mei_io_list_flush(list, cl, true);
135cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler}
136cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler
137cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler/**
1389ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * mei_io_cb_free - free mei_cb_private related memory
1399ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
1409ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cb: mei callback struct
1419ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
1429ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklervoid mei_io_cb_free(struct mei_cl_cb *cb)
1439ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
1449ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (cb == NULL)
1459ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return;
1469ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1479ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	kfree(cb->request_buffer.data);
1489ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	kfree(cb->response_buffer.data);
1499ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	kfree(cb);
1509ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
1519ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1529ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
1539ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * mei_io_cb_init - allocate and initialize io callback
1549ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
1559ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl - mei client
156393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @fp: pointer to file structure
1579ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
1589ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns mei_cl_cb pointer or NULL;
1599ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
1609ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerstruct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
1619ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
1629ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl_cb *cb;
1639ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1649ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
1659ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cb)
1669ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return NULL;
1679ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1689ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_init(cb);
1699ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1709ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->file_object = fp;
1719ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->cl = cl;
1729ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->buf_idx = 0;
1739ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return cb;
1749ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
1759ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1769ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
1779ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * mei_io_cb_alloc_req_buf - allocate request buffer
1789ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
179393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @cb: io callback structure
180393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @length: size of the buffer
1819ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
1829ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns 0 on success
1839ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *         -EINVAL if cb is NULL
1849ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *         -ENOMEM if allocation failed
1859ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
1869ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerint mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
1879ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
1889ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cb)
1899ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -EINVAL;
1909ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1919ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (length == 0)
1929ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return 0;
1939ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
1949ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
1959ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cb->request_buffer.data)
1969ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -ENOMEM;
1979ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->request_buffer.size = length;
1989ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return 0;
1999ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
2009ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
20183ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * mei_io_cb_alloc_resp_buf - allocate response buffer
2029ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
203393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @cb: io callback structure
204393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @length: size of the buffer
2059ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
2069ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns 0 on success
2079ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *         -EINVAL if cb is NULL
2089ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *         -ENOMEM if allocation failed
2099ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
2109ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerint mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
2119ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
2129ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cb)
2139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -EINVAL;
2149ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2159ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (length == 0)
2169ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return 0;
2179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2189ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->response_buffer.data = kmalloc(length, GFP_KERNEL);
2199ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cb->response_buffer.data)
2209ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -ENOMEM;
2219ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->response_buffer.size = length;
2229ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return 0;
2239ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
2249ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2259ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2269ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2279ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
2289ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl.
2299ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
2309ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl: host client
2319ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
2329ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerint mei_cl_flush_queues(struct mei_cl *cl)
2339ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
234c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	struct mei_device *dev;
235c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin
23690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
2379ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -EINVAL;
2389ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
239c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	dev = cl->dev;
240c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin
241c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
2429ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&cl->dev->read_list, cl);
243cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	mei_io_list_free(&cl->dev->write_list, cl);
244cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	mei_io_list_free(&cl->dev->write_waiting_list, cl);
2459ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
2469ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
2479ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
2489ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
2499ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return 0;
2509ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
2519ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2529ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2539ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
25483ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * mei_cl_init - initializes cl.
2559ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
2569ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl: host client to be initialized
2579ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @dev: mei device
2589ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
2599ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklervoid mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
2609ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
2619ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	memset(cl, 0, sizeof(struct mei_cl));
2629ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	init_waitqueue_head(&cl->wait);
2639ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	init_waitqueue_head(&cl->rx_wait);
2649ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	init_waitqueue_head(&cl->tx_wait);
2659ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	INIT_LIST_HEAD(&cl->link);
266a7b71bc043aded9da4cf51f85271e0779161fe22Samuel Ortiz	INIT_LIST_HEAD(&cl->device_link);
2679ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cl->reading_state = MEI_IDLE;
2689ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cl->writing_state = MEI_IDLE;
2699ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cl->dev = dev;
2709ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
2719ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2729ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
2739ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * mei_cl_allocate - allocates cl  structure and sets it up.
2749ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
2759ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @dev: mei device
2769ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns  The allocated file or NULL on failure
2779ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
2789ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerstruct mei_cl *mei_cl_allocate(struct mei_device *dev)
2799ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
2809ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl *cl;
2819ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2829ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
2839ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!cl)
2849ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return NULL;
2859ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2869ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_cl_init(cl, dev);
2879ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
2889ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return cl;
2899ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
2909ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
29190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler/**
29290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_cl_find_read_cb - find this cl's callback in the read list
29390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *
294393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @cl: host client
295393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida *
29690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * returns cb on success, NULL on error
29790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler */
29890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerstruct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
29990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler{
30090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev = cl->dev;
30131f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	struct mei_cl_cb *cb;
30290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
30331f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	list_for_each_entry(cb, &dev->read_list.list, list)
30490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		if (mei_cl_cmp_id(cl, cb->cl))
30590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler			return cb;
30690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	return NULL;
30790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler}
30890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
30983ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin/** mei_cl_link: allocate host id in the host map
3109ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
311781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler * @cl - host client
31283ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * @id - fixed host id or -1 for generic one
313393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida *
314781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler * returns 0 on success
3159ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-EINVAL on incorrect values
3169ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-ENONET if client not found
3179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
318781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winklerint mei_cl_link(struct mei_cl *cl, int id)
3199ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
32090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
32122f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	long open_handle_count;
3229ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
323781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
3249ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -EINVAL;
3259ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
32690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
32790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
32883ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin	/* If Id is not assigned get one*/
329781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	if (id == MEI_HOST_CLIENT_ID_ANY)
330781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler		id = find_first_zero_bit(dev->host_clients_map,
331781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler					MEI_CLIENTS_MAX);
3329ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
333781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	if (id >= MEI_CLIENTS_MAX) {
33483ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin		dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
335e036cc5727eb6d471442d2a9218990aa11215400Tomas Winkler		return -EMFILE;
336e036cc5727eb6d471442d2a9218990aa11215400Tomas Winkler	}
337e036cc5727eb6d471442d2a9218990aa11215400Tomas Winkler
33822f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
33922f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
34083ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin		dev_err(&dev->pdev->dev, "open_handle_count exceeded %d",
341e036cc5727eb6d471442d2a9218990aa11215400Tomas Winkler			MEI_MAX_OPEN_HANDLE_COUNT);
342e036cc5727eb6d471442d2a9218990aa11215400Tomas Winkler		return -EMFILE;
3439ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
3449ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
345781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	dev->open_handle_count++;
346781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
347781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	cl->host_client_id = id;
348781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	list_add_tail(&cl->link, &dev->file_list);
349781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
350781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	set_bit(id, dev->host_clients_map);
351781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
352781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	cl->state = MEI_FILE_INITIALIZING;
353781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
354c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	cl_dbg(dev, cl, "link cl\n");
355781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	return 0;
3569ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
357781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
3589ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
35990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_cl_unlink - remove me_cl from the list
3609ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
361393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * @cl: host client
3629ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
36390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_cl_unlink(struct mei_cl *cl)
3649ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
36590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
36690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
367781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	/* don't shout on error exit path */
368781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler	if (!cl)
369781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler		return 0;
370781d0d89224bbbc438c2c0360cfd4822bb35d280Tomas Winkler
3718e9a4a9a5c8e8765417d54ed6917c7e1e4d09f4dTomas Winkler	/* wd and amthif might not be initialized */
3728e9a4a9a5c8e8765417d54ed6917c7e1e4d09f4dTomas Winkler	if (!cl->dev)
3738e9a4a9a5c8e8765417d54ed6917c7e1e4d09f4dTomas Winkler		return 0;
37490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
37590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
37690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
377a14c44d82fcff280fd1138574d4480b2bdd40216Tomas Winkler	cl_dbg(dev, cl, "unlink client");
378a14c44d82fcff280fd1138574d4480b2bdd40216Tomas Winkler
37922f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	if (dev->open_handle_count > 0)
38022f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler		dev->open_handle_count--;
38122f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler
38222f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	/* never clear the 0 bit */
38322f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	if (cl->host_client_id)
38422f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler		clear_bit(cl->host_client_id, dev->host_clients_map);
38522f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler
38622f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	list_del_init(&cl->link);
38722f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler
38822f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler	cl->state = MEI_FILE_INITIALIZING;
38922f96a0eb6c62b570621d77dacbf2589a6de2997Tomas Winkler
39090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	return 0;
3919ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
3929ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
3939ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
3949ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklervoid mei_host_client_init(struct work_struct *work)
3959ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
3969ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_device *dev = container_of(work,
3979ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler					      struct mei_device, init_work);
3989ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_client_properties *client_props;
3999ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	int i;
4009ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4019ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mutex_lock(&dev->device_lock);
4029ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4039ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	for (i = 0; i < dev->me_clients_num; i++) {
4049ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		client_props = &dev->me_clients[i].props;
4059ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4061a1aca42c989051dce34d49b4e04a25dafe01d74Tomas Winkler		if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid))
4079ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			mei_amthif_host_init(dev);
4089ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
4099ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			mei_wd_host_init(dev);
41059fcd7c63abf0340f551f487264b67ff5f7a0b86Samuel Ortiz		else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid))
41159fcd7c63abf0340f551f487264b67ff5f7a0b86Samuel Ortiz			mei_nfc_host_init(dev);
41259fcd7c63abf0340f551f487264b67ff5f7a0b86Samuel Ortiz
4139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
4149ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4159ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	dev->dev_state = MEI_DEV_ENABLED;
4166adb8efb024a7e413b93b22848fc13395b1a438aTomas Winkler	dev->reset_count = 0;
4179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4189ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mutex_unlock(&dev->device_lock);
41904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
42004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_mark_last_busy(&dev->pdev->dev);
42104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n");
42204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_autosuspend(&dev->pdev->dev);
4239ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
4249ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4256aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler/**
4266aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler * mei_hbuf_acquire: try to acquire host buffer
4276aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler *
4286aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler * @dev: the device structure
4296aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler * returns true if host buffer was acquired
4306aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler */
4316aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winklerbool mei_hbuf_acquire(struct mei_device *dev)
4326aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler{
43304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (mei_pg_state(dev) == MEI_PG_ON ||
43404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	    dev->pg_event == MEI_PG_EVENT_WAIT) {
43504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		dev_dbg(&dev->pdev->dev, "device is in pg\n");
43604bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		return false;
43704bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
43804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
4396aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (!dev->hbuf_is_ready) {
4406aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
4416aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		return false;
4426aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	}
4436aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler
4446aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	dev->hbuf_is_ready = false;
4456aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler
4466aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	return true;
4476aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler}
4489ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4499ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
45083ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * mei_cl_disconnect - disconnect host client from the me one
4519ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
45290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * @cl: host client
4539ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
4549ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * Locking: called under "dev->device_lock" lock
4559ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
4569ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns 0 on success, <0 on failure.
4579ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
45890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_cl_disconnect(struct mei_cl *cl)
4599ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
46090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
4619ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl_cb *cb;
462fe2f17eb3da38ac0d5a00c511255bf3a33d16d24Alexander Usyskin	int rets;
4639ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
46490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
4659ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -ENODEV;
4669ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
46790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
46890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
469c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	cl_dbg(dev, cl, "disconnecting");
470c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin
4719ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (cl->state != MEI_FILE_DISCONNECTING)
4729ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return 0;
4739ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
47404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	rets = pm_runtime_get(&dev->pdev->dev);
47504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (rets < 0 && rets != -EINPROGRESS) {
47604bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		pm_runtime_put_noidle(&dev->pdev->dev);
47704bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		cl_err(dev, cl, "rpm: get failed %d\n", rets);
47804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		return rets;
47904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
48004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
4819ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb = mei_io_cb_init(cl, NULL);
48204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (!cb) {
48304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		rets = -ENOMEM;
48404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		goto free;
48504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
4869ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
4879ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->fop_type = MEI_FOP_CLOSE;
4886aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (mei_hbuf_acquire(dev)) {
4899ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		if (mei_hbm_cl_disconnect_req(dev, cl)) {
4909ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			rets = -ENODEV;
491c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin			cl_err(dev, cl, "failed to disconnect.\n");
4929ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			goto free;
4939ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		}
49422b987a325701223f9a37db700c6eb20b9924c6fAlexander Usyskin		cl->timer_count = MEI_CONNECT_TIMEOUT;
4959ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		mdelay(10); /* Wait for hardware disconnection ready */
4969ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
4979ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	} else {
498c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin		cl_dbg(dev, cl, "add disconnect cb to control write list\n");
4999ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
5009ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5019ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
5029ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mutex_unlock(&dev->device_lock);
5039ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
504fe2f17eb3da38ac0d5a00c511255bf3a33d16d24Alexander Usyskin	wait_event_timeout(dev->wait_recvd_msg,
5059ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			MEI_FILE_DISCONNECTED == cl->state,
5069ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
5079ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5089ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mutex_lock(&dev->device_lock);
509fe2f17eb3da38ac0d5a00c511255bf3a33d16d24Alexander Usyskin
5109ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (MEI_FILE_DISCONNECTED == cl->state) {
5119ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		rets = 0;
512c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
5139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	} else {
514fe2f17eb3da38ac0d5a00c511255bf3a33d16d24Alexander Usyskin		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
515fe2f17eb3da38ac0d5a00c511255bf3a33d16d24Alexander Usyskin		rets = -ETIME;
5169ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
5179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5189ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&dev->ctrl_rd_list, cl);
5199ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_list_flush(&dev->ctrl_wr_list, cl);
5209ca9050b3df690d9d44e39424ab2a531120af936Tomas Winklerfree:
52104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	cl_dbg(dev, cl, "rpm: autosuspend\n");
52204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_mark_last_busy(&dev->pdev->dev);
52304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_put_autosuspend(&dev->pdev->dev);
52404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
5259ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	mei_io_cb_free(cb);
5269ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return rets;
5279ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
5289ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5299ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5309ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
53190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_cl_is_other_connecting - checks if other
53290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler *    client with the same me client id is connecting
5339ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
5349ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl: private data of the file object
5359ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
53683ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * returns true if other client is connected, false - otherwise.
5379ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
53890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerbool mei_cl_is_other_connecting(struct mei_cl *cl)
5399ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
54090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
54131f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	struct mei_cl *ocl; /* the other client */
5429ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
54390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
54490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		return false;
54590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
54690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
54790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
54831f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	list_for_each_entry(ocl, &dev->file_list, link) {
54931f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler		if (ocl->state == MEI_FILE_CONNECTING &&
55031f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler		    ocl != cl &&
55131f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler		    cl->me_client_id == ocl->me_client_id)
55290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler			return true;
5539ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5549ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
55590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
55690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	return false;
5579ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
5589ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
5599ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
56083ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * mei_cl_connect - connect host client to the me one
5619f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler *
5629f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler * @cl: host client
5639f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler *
5649f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler * Locking: called under "dev->device_lock" lock
5659f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler *
5669f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler * returns 0 on success, <0 on failure.
5679f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler */
5689f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winklerint mei_cl_connect(struct mei_cl *cl, struct file *file)
5699f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler{
5709f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	struct mei_device *dev;
5719f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	struct mei_cl_cb *cb;
5729f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	int rets;
5739f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
5749f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	if (WARN_ON(!cl || !cl->dev))
5759f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		return -ENODEV;
5769f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
5779f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	dev = cl->dev;
5789f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
57904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	rets = pm_runtime_get(&dev->pdev->dev);
58004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (rets < 0 && rets != -EINPROGRESS) {
58104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		pm_runtime_put_noidle(&dev->pdev->dev);
58204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		cl_err(dev, cl, "rpm: get failed %d\n", rets);
58304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		return rets;
58404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
58504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
5869f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	cb = mei_io_cb_init(cl, file);
5879f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	if (!cb) {
5889f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		rets = -ENOMEM;
5899f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		goto out;
5909f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	}
5919f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
59202a7eecc6ee565f5f3af836d56fe25bafcc49c98Tomas Winkler	cb->fop_type = MEI_FOP_CONNECT;
5939f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
5946aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	/* run hbuf acquire last so we don't have to undo */
5956aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
596e4d8270e604c3202131bac607969605ac397b893Alexander Usyskin		cl->state = MEI_FILE_CONNECTING;
5979f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		if (mei_hbm_cl_connect_req(dev, cl)) {
5989f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler			rets = -ENODEV;
5999f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler			goto out;
6009f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		}
6019f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		cl->timer_count = MEI_CONNECT_TIMEOUT;
6029f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
6039f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	} else {
60473ab4232388b7a08f17c8d08141ff2099fa0b161Alexander Usyskin		cl->state = MEI_FILE_INITIALIZING;
6059f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
6069f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	}
6079f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6089f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	mutex_unlock(&dev->device_lock);
609285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin	wait_event_timeout(dev->wait_recvd_msg,
610285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin			(cl->state == MEI_FILE_CONNECTED ||
611285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin			 cl->state == MEI_FILE_DISCONNECTED),
612285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
6139f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	mutex_lock(&dev->device_lock);
6149f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6159f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	if (cl->state != MEI_FILE_CONNECTED) {
6163e37ebb7183f0c4eb92a88c60657ac319c01b3e9Alexander Usyskin		cl->state = MEI_FILE_DISCONNECTED;
617285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin		/* something went really wrong */
618285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin		if (!cl->status)
619285e2996655b7bbfb5eb83076a7d7e6f03e2f5c2Alexander Usyskin			cl->status = -EFAULT;
6209f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6219f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		mei_io_list_flush(&dev->ctrl_rd_list, cl);
6229f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler		mei_io_list_flush(&dev->ctrl_wr_list, cl);
6239f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	}
6249f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6259f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	rets = cl->status;
6269f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6279f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winklerout:
62804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	cl_dbg(dev, cl, "rpm: autosuspend\n");
62904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_mark_last_busy(&dev->pdev->dev);
63004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_put_autosuspend(&dev->pdev->dev);
63104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
6329f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	mei_io_cb_free(cb);
6339f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler	return rets;
6349f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler}
6359f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler
6369f81abdac3629629a246fdc9e2a7c01ffd52ce8aTomas Winkler/**
63790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
6389ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
6399ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl: private data of the file object
6409ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
6419ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
6429ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-ENOENT if mei_cl is not present
6439ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-EINVAL if single_recv_buf == 0
6449ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
64590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_cl_flow_ctrl_creds(struct mei_cl *cl)
6469ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
64790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
64812d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	struct mei_me_client *me_cl;
64912d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	int id;
6509ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
65190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
65290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		return -EINVAL;
65390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
65490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
65590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
6569ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (!dev->me_clients_num)
6579ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return 0;
6589ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
6599ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (cl->mei_flow_ctrl_creds > 0)
6609ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return 1;
6619ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
66212d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	id = mei_me_cl_by_id(dev, cl->me_client_id);
66312d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	if (id < 0) {
66412d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
66512d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		return id;
6669ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
66712d0066526f386538de80b4d86d2008461b36674Alexander Usyskin
66812d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	me_cl = &dev->me_clients[id];
66912d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	if (me_cl->mei_flow_ctrl_creds) {
67012d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		if (WARN_ON(me_cl->props.single_recv_buf == 0))
67112d0066526f386538de80b4d86d2008461b36674Alexander Usyskin			return -EINVAL;
67212d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		return 1;
67312d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	}
67412d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	return 0;
6759ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
6769ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
6779ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
67890e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control.
6799ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
6809ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @cl: private data of the file object
681393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida *
6829ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * @returns
6839ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	0 on success
6849ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-ENOENT when me client is not found
6859ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *	-EINVAL when ctrl credits are <= 0
6869ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
68790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winklerint mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
6889ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
68990e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
69012d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	struct mei_me_client *me_cl;
69112d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	int id;
6929ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
69390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
69490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		return -EINVAL;
69590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
69690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
69790e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
69812d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	id = mei_me_cl_by_id(dev, cl->me_client_id);
69912d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	if (id < 0) {
70012d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
70112d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		return id;
70212d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	}
7039ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
70412d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	me_cl = &dev->me_clients[id];
70512d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	if (me_cl->props.single_recv_buf != 0) {
70612d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
70712d0066526f386538de80b4d86d2008461b36674Alexander Usyskin			return -EINVAL;
70812d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		me_cl->mei_flow_ctrl_creds--;
70912d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	} else {
71012d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
71112d0066526f386538de80b4d86d2008461b36674Alexander Usyskin			return -EINVAL;
71212d0066526f386538de80b4d86d2008461b36674Alexander Usyskin		cl->mei_flow_ctrl_creds--;
7139ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
71412d0066526f386538de80b4d86d2008461b36674Alexander Usyskin	return 0;
7159ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
7169ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
7179ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler/**
718393b148f9d0e70cfcb0096985bb0f0742802929eMasanari Iida * mei_cl_read_start - the start read client message function.
7199ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
72090e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler * @cl: host client
7219ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler *
7229ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler * returns 0 on success, <0 on failure.
7239ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler */
724fcb136e1ac5774909e0d85189f721b8dfa800e0fTomas Winklerint mei_cl_read_start(struct mei_cl *cl, size_t length)
7259ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler{
72690e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	struct mei_device *dev;
7279ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	struct mei_cl_cb *cb;
7289ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	int rets;
7299ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	int i;
7309ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
73190e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
73290e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler		return -ENODEV;
73390e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
73490e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler	dev = cl->dev;
73590e0b5f18569bdd03c5ddd1d8c99946f42af77b8Tomas Winkler
736b950ac1dabfcbf97b99f26fa75f86087e1960aefTomas Winkler	if (!mei_cl_is_connected(cl))
7379ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -ENODEV;
7389ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
739d91aaed30a938c5daae2641e6758dfab8727862eTomas Winkler	if (cl->read_cb) {
740c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin		cl_dbg(dev, cl, "read is pending.\n");
7419ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		return -EBUSY;
7429ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
7439ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	i = mei_me_cl_by_id(dev, cl->me_client_id);
7449ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (i < 0) {
745c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
7467ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		return  -ENOTTY;
7479ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
7489ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
74904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	rets = pm_runtime_get(&dev->pdev->dev);
75004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (rets < 0 && rets != -EINPROGRESS) {
75104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		pm_runtime_put_noidle(&dev->pdev->dev);
75204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		cl_err(dev, cl, "rpm: get failed %d\n", rets);
75304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		return rets;
75404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
75504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
7569ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb = mei_io_cb_init(cl, NULL);
75704bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (!cb) {
75804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		rets = -ENOMEM;
75904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		goto out;
76004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
7619ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
762fcb136e1ac5774909e0d85189f721b8dfa800e0fTomas Winkler	/* always allocate at least client max message */
763fcb136e1ac5774909e0d85189f721b8dfa800e0fTomas Winkler	length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
764fcb136e1ac5774909e0d85189f721b8dfa800e0fTomas Winkler	rets = mei_io_cb_alloc_resp_buf(cb, length);
7659ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	if (rets)
76604bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		goto out;
7679ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
7689ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	cb->fop_type = MEI_FOP_READ;
7696aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (mei_hbuf_acquire(dev)) {
77086113500c060bccb0f08bdcadcecc0bd267fd25aAlexander Usyskin		rets = mei_hbm_cl_flow_control_req(dev, cl);
77186113500c060bccb0f08bdcadcecc0bd267fd25aAlexander Usyskin		if (rets < 0)
77204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler			goto out;
77304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
7749ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		list_add_tail(&cb->list, &dev->read_list.list);
7759ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	} else {
7769ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
7779ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	}
778accb884b32e82f943340688c9cd30290531e73e0Chao Bi
779accb884b32e82f943340688c9cd30290531e73e0Chao Bi	cl->read_cb = cb;
780accb884b32e82f943340688c9cd30290531e73e0Chao Bi
78104bb139a071fef549892718f8965a7c61b1924e0Tomas Winklerout:
78204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	cl_dbg(dev, cl, "rpm: autosuspend\n");
78304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_mark_last_busy(&dev->pdev->dev);
78404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_put_autosuspend(&dev->pdev->dev);
78504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
78604bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (rets)
78704bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		mei_io_cb_free(cb);
78804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
7899ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler	return rets;
7909ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler}
7919ca9050b3df690d9d44e39424ab2a531120af936Tomas Winkler
792074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler/**
7939d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler * mei_cl_irq_write - write a message to device
79421767546e955c3c1705387ca4548db812382fe08Tomas Winkler *	from the interrupt thread context
79521767546e955c3c1705387ca4548db812382fe08Tomas Winkler *
79621767546e955c3c1705387ca4548db812382fe08Tomas Winkler * @cl: client
79721767546e955c3c1705387ca4548db812382fe08Tomas Winkler * @cb: callback block.
79821767546e955c3c1705387ca4548db812382fe08Tomas Winkler * @cmpl_list: complete list.
79921767546e955c3c1705387ca4548db812382fe08Tomas Winkler *
80021767546e955c3c1705387ca4548db812382fe08Tomas Winkler * returns 0, OK; otherwise error.
80121767546e955c3c1705387ca4548db812382fe08Tomas Winkler */
8029d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winklerint mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
8039d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler		     struct mei_cl_cb *cmpl_list)
80421767546e955c3c1705387ca4548db812382fe08Tomas Winkler{
805136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	struct mei_device *dev;
806136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	struct mei_msg_data *buf;
80721767546e955c3c1705387ca4548db812382fe08Tomas Winkler	struct mei_msg_hdr mei_hdr;
808136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	size_t len;
809136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	u32 msg_slots;
8109d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler	int slots;
8112ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler	int rets;
81221767546e955c3c1705387ca4548db812382fe08Tomas Winkler
813136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	if (WARN_ON(!cl || !cl->dev))
814136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler		return -ENODEV;
815136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
816136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	dev = cl->dev;
817136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
818136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	buf = &cb->request_buffer;
819136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
820136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	rets = mei_cl_flow_ctrl_creds(cl);
821136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	if (rets < 0)
822136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler		return rets;
823136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
824136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	if (rets == 0) {
82504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
826136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler		return 0;
827136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	}
828136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
8299d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler	slots = mei_hbuf_empty_slots(dev);
830136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	len = buf->size - cb->buf_idx;
831136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	msg_slots = mei_data2slots(len);
832136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler
83321767546e955c3c1705387ca4548db812382fe08Tomas Winkler	mei_hdr.host_addr = cl->host_client_id;
83421767546e955c3c1705387ca4548db812382fe08Tomas Winkler	mei_hdr.me_addr = cl->me_client_id;
83521767546e955c3c1705387ca4548db812382fe08Tomas Winkler	mei_hdr.reserved = 0;
836479327fc42737234a1f76f20010334c99110d256Tomas Winkler	mei_hdr.internal = cb->internal;
83721767546e955c3c1705387ca4548db812382fe08Tomas Winkler
8389d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler	if (slots >= msg_slots) {
83921767546e955c3c1705387ca4548db812382fe08Tomas Winkler		mei_hdr.length = len;
84021767546e955c3c1705387ca4548db812382fe08Tomas Winkler		mei_hdr.msg_complete = 1;
84121767546e955c3c1705387ca4548db812382fe08Tomas Winkler	/* Split the message only if we can write the whole host buffer */
8429d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler	} else if (slots == dev->hbuf_depth) {
8439d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler		msg_slots = slots;
8449d098192c3d45ab6dd90ae87d649950a9ef70ccbTomas Winkler		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
84521767546e955c3c1705387ca4548db812382fe08Tomas Winkler		mei_hdr.length = len;
84621767546e955c3c1705387ca4548db812382fe08Tomas Winkler		mei_hdr.msg_complete = 0;
84721767546e955c3c1705387ca4548db812382fe08Tomas Winkler	} else {
84821767546e955c3c1705387ca4548db812382fe08Tomas Winkler		/* wait for next time the host buffer is empty */
84921767546e955c3c1705387ca4548db812382fe08Tomas Winkler		return 0;
85021767546e955c3c1705387ca4548db812382fe08Tomas Winkler	}
85121767546e955c3c1705387ca4548db812382fe08Tomas Winkler
852c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
85321767546e955c3c1705387ca4548db812382fe08Tomas Winkler			cb->request_buffer.size, cb->buf_idx);
85421767546e955c3c1705387ca4548db812382fe08Tomas Winkler
855136698e535cd1ce59e436cc084b41370fd8f1effTomas Winkler	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
8562ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler	if (rets) {
8572ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler		cl->status = rets;
85821767546e955c3c1705387ca4548db812382fe08Tomas Winkler		list_move_tail(&cb->list, &cmpl_list->list);
8592ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler		return rets;
86021767546e955c3c1705387ca4548db812382fe08Tomas Winkler	}
86121767546e955c3c1705387ca4548db812382fe08Tomas Winkler
86221767546e955c3c1705387ca4548db812382fe08Tomas Winkler	cl->status = 0;
8634dfaa9f7020b1ff4bf87899f4797d2efd76e80fdTomas Winkler	cl->writing_state = MEI_WRITING;
86421767546e955c3c1705387ca4548db812382fe08Tomas Winkler	cb->buf_idx += mei_hdr.length;
8654dfaa9f7020b1ff4bf87899f4797d2efd76e80fdTomas Winkler
86621767546e955c3c1705387ca4548db812382fe08Tomas Winkler	if (mei_hdr.msg_complete) {
86721767546e955c3c1705387ca4548db812382fe08Tomas Winkler		if (mei_cl_flow_ctrl_reduce(cl))
8682ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler			return -EIO;
86921767546e955c3c1705387ca4548db812382fe08Tomas Winkler		list_move_tail(&cb->list, &dev->write_waiting_list.list);
87021767546e955c3c1705387ca4548db812382fe08Tomas Winkler	}
87121767546e955c3c1705387ca4548db812382fe08Tomas Winkler
87221767546e955c3c1705387ca4548db812382fe08Tomas Winkler	return 0;
87321767546e955c3c1705387ca4548db812382fe08Tomas Winkler}
87421767546e955c3c1705387ca4548db812382fe08Tomas Winkler
87521767546e955c3c1705387ca4548db812382fe08Tomas Winkler/**
8764234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler * mei_cl_write - submit a write cb to mei device
8774234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	assumes device_lock is locked
8784234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler *
8794234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler * @cl: host client
8804234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler * @cl: write callback with filled data
8814234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler *
88283ce07411dc2316aaaf95a0f193fa2fd76e2e739Alexander Usyskin * returns number of bytes sent on success, <0 on failure.
8834234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler */
8844234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winklerint mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
8854234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler{
8864234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	struct mei_device *dev;
8874234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	struct mei_msg_data *buf;
8884234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	struct mei_msg_hdr mei_hdr;
8894234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	int rets;
8904234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
8914234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
8924234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (WARN_ON(!cl || !cl->dev))
8934234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		return -ENODEV;
8944234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
8954234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (WARN_ON(!cb))
8964234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		return -EINVAL;
8974234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
8984234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	dev = cl->dev;
8994234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9004234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9014234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	buf = &cb->request_buffer;
9024234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
903c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin	cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
9044234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
90504bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	rets = pm_runtime_get(&dev->pdev->dev);
90604bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	if (rets < 0 && rets != -EINPROGRESS) {
90704bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		pm_runtime_put_noidle(&dev->pdev->dev);
90804bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		cl_err(dev, cl, "rpm: get failed %d\n", rets);
90904bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler		return rets;
91004bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	}
9114234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9124234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	cb->fop_type = MEI_FOP_WRITE;
9136aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	cb->buf_idx = 0;
9146aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	cl->writing_state = MEI_IDLE;
9156aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler
9166aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	mei_hdr.host_addr = cl->host_client_id;
9176aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	mei_hdr.me_addr = cl->me_client_id;
9186aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	mei_hdr.reserved = 0;
9196aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	mei_hdr.msg_complete = 0;
9206aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	mei_hdr.internal = cb->internal;
9214234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9224234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	rets = mei_cl_flow_ctrl_creds(cl);
9234234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (rets < 0)
9244234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		goto err;
9254234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9266aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (rets == 0) {
9276aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
9286aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		rets = buf->size;
9296aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		goto out;
9306aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	}
9316aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler	if (!mei_hbuf_acquire(dev)) {
9326aae48ff18f2fcfb533d2b448ecae16d1de006c1Tomas Winkler		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
9334234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		rets = buf->size;
9344234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		goto out;
9354234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	}
9364234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9374234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	/* Check for a maximum length */
9384234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (buf->size > mei_hbuf_max_len(dev)) {
9394234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mei_hdr.length = mei_hbuf_max_len(dev);
9404234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mei_hdr.msg_complete = 0;
9414234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	} else {
9424234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mei_hdr.length = buf->size;
9434234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mei_hdr.msg_complete = 1;
9444234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	}
9454234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9462ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler	rets = mei_write_message(dev, &mei_hdr, buf->data);
9472ebf8c94d431078d93599ba56efa58bf850078a1Tomas Winkler	if (rets)
9484234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		goto err;
9494234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9504234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	cl->writing_state = MEI_WRITING;
9514234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	cb->buf_idx = mei_hdr.length;
9524234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9534234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winklerout:
9544234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (mei_hdr.msg_complete) {
9557ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		rets = mei_cl_flow_ctrl_reduce(cl);
9567ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		if (rets < 0)
9574234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler			goto err;
9587ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin
9594234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		list_add_tail(&cb->list, &dev->write_waiting_list.list);
9604234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	} else {
9614234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		list_add_tail(&cb->list, &dev->write_list.list);
9624234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	}
9634234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9644234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9654234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
9664234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9674234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mutex_unlock(&dev->device_lock);
9687ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		rets = wait_event_interruptible(cl->tx_wait,
9697ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin				cl->writing_state == MEI_WRITE_COMPLETE);
9704234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler		mutex_lock(&dev->device_lock);
9717ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		/* wait_event_interruptible returns -ERESTARTSYS */
9727ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		if (rets) {
9737ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin			if (signal_pending(current))
9747ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin				rets = -EINTR;
9757ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin			goto err;
9767ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin		}
9774234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	}
9787ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin
9797ca96aa278f8b9983184e318b06a0ed9ad0297b8Alexander Usyskin	rets = buf->size;
9804234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winklererr:
98104bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	cl_dbg(dev, cl, "rpm: autosuspend\n");
98204bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_mark_last_busy(&dev->pdev->dev);
98304bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler	pm_runtime_put_autosuspend(&dev->pdev->dev);
98404bb139a071fef549892718f8965a7c61b1924e0Tomas Winkler
9854234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler	return rets;
9864234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler}
9874234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
9884234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
989db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler/**
990db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler * mei_cl_complete - processes completed operation for a client
991db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler *
992db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler * @cl: private data of the file object.
993db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler * @cb: callback block.
994db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler */
995db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winklervoid mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
996db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler{
997db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler	if (cb->fop_type == MEI_FOP_WRITE) {
998db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		mei_io_cb_free(cb);
999db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		cb = NULL;
1000db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		cl->writing_state = MEI_WRITE_COMPLETE;
1001db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		if (waitqueue_active(&cl->tx_wait))
1002db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler			wake_up_interruptible(&cl->tx_wait);
1003db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler
1004db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler	} else if (cb->fop_type == MEI_FOP_READ &&
1005db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler			MEI_READING == cl->reading_state) {
1006db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		cl->reading_state = MEI_READ_COMPLETE;
1007db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		if (waitqueue_active(&cl->rx_wait))
1008db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler			wake_up_interruptible(&cl->rx_wait);
1009db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler		else
1010db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler			mei_cl_bus_rx_event(cl);
1011db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler
1012db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler	}
1013db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler}
1014db086fa926e57e1bd70e8c41235d230b3caa5e99Tomas Winkler
10154234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler
10164234a6deb5ab04e50cfd6d72761345727bd2de21Tomas Winkler/**
1017074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients
1018074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler *
1019074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler * @dev - mei device
1020074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler */
1021074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1022074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winklervoid mei_cl_all_disconnect(struct mei_device *dev)
1023074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler{
102431f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	struct mei_cl *cl;
1025074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
102631f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	list_for_each_entry(cl, &dev->file_list, link) {
1027074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler		cl->state = MEI_FILE_DISCONNECTED;
1028074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler		cl->mei_flow_ctrl_creds = 0;
1029074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler		cl->timer_count = 0;
1030074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler	}
1031074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler}
1032074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1033074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1034074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler/**
10355290801c23231c8e192943d3beb01fdbeb536395Tomas Winkler * mei_cl_all_wakeup  - wake up all readers and writers they can be interrupted
1036074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler *
1037074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler * @dev  - mei device
1038074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler */
10395290801c23231c8e192943d3beb01fdbeb536395Tomas Winklervoid mei_cl_all_wakeup(struct mei_device *dev)
1040074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler{
104131f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	struct mei_cl *cl;
104231f88f5739e966cb3c524083e2d19b423ece3585Tomas Winkler	list_for_each_entry(cl, &dev->file_list, link) {
1043074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler		if (waitqueue_active(&cl->rx_wait)) {
1044c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin			cl_dbg(dev, cl, "Waking up reading client!\n");
1045074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler			wake_up_interruptible(&cl->rx_wait);
1046074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler		}
10475290801c23231c8e192943d3beb01fdbeb536395Tomas Winkler		if (waitqueue_active(&cl->tx_wait)) {
1048c0abffbd982ccf9460187206a074e52cb23e8be3Alexander Usyskin			cl_dbg(dev, cl, "Waking up writing client!\n");
10495290801c23231c8e192943d3beb01fdbeb536395Tomas Winkler			wake_up_interruptible(&cl->tx_wait);
10505290801c23231c8e192943d3beb01fdbeb536395Tomas Winkler		}
1051074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler	}
1052074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler}
1053074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1054074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler/**
1055074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler * mei_cl_all_write_clear - clear all pending writes
1056074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1057074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler * @dev - mei device
1058074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler */
1059074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winklervoid mei_cl_all_write_clear(struct mei_device *dev)
1060074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler{
1061cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	mei_io_list_free(&dev->write_list, NULL);
1062cc99ecfdac01215594c73907726b12f251c21e20Tomas Winkler	mei_io_list_free(&dev->write_waiting_list, NULL);
1063074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler}
1064074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1065074b4c01abb68c6767612a01f41e9b4ed93d5fb8Tomas Winkler
1066