xref: /openbmc/linux/drivers/misc/mei/client.c (revision 1d9013f0)
19ca9050bSTomas Winkler /*
29ca9050bSTomas Winkler  *
39ca9050bSTomas Winkler  * Intel Management Engine Interface (Intel MEI) Linux driver
49ca9050bSTomas Winkler  * Copyright (c) 2003-2012, Intel Corporation.
59ca9050bSTomas Winkler  *
69ca9050bSTomas Winkler  * This program is free software; you can redistribute it and/or modify it
79ca9050bSTomas Winkler  * under the terms and conditions of the GNU General Public License,
89ca9050bSTomas Winkler  * version 2, as published by the Free Software Foundation.
99ca9050bSTomas Winkler  *
109ca9050bSTomas Winkler  * This program is distributed in the hope it will be useful, but WITHOUT
119ca9050bSTomas Winkler  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
129ca9050bSTomas Winkler  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
139ca9050bSTomas Winkler  * more details.
149ca9050bSTomas Winkler  *
159ca9050bSTomas Winkler  */
169ca9050bSTomas Winkler 
179ca9050bSTomas Winkler #include <linux/sched.h>
189ca9050bSTomas Winkler #include <linux/wait.h>
199ca9050bSTomas Winkler #include <linux/delay.h>
201f180359STomas Winkler #include <linux/slab.h>
2104bb139aSTomas Winkler #include <linux/pm_runtime.h>
229ca9050bSTomas Winkler 
239ca9050bSTomas Winkler #include <linux/mei.h>
249ca9050bSTomas Winkler 
259ca9050bSTomas Winkler #include "mei_dev.h"
269ca9050bSTomas Winkler #include "hbm.h"
2790e0b5f1STomas Winkler #include "client.h"
2890e0b5f1STomas Winkler 
2990e0b5f1STomas Winkler /**
3079563db9STomas Winkler  * mei_me_cl_init - initialize me client
3179563db9STomas Winkler  *
3279563db9STomas Winkler  * @me_cl: me client
3379563db9STomas Winkler  */
3479563db9STomas Winkler void mei_me_cl_init(struct mei_me_client *me_cl)
3579563db9STomas Winkler {
3679563db9STomas Winkler 	INIT_LIST_HEAD(&me_cl->list);
3779563db9STomas Winkler 	kref_init(&me_cl->refcnt);
3879563db9STomas Winkler }
3979563db9STomas Winkler 
4079563db9STomas Winkler /**
4179563db9STomas Winkler  * mei_me_cl_get - increases me client refcount
4279563db9STomas Winkler  *
4379563db9STomas Winkler  * @me_cl: me client
4479563db9STomas Winkler  *
4579563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
4679563db9STomas Winkler  *
4779563db9STomas Winkler  * Return: me client or NULL
4879563db9STomas Winkler  */
4979563db9STomas Winkler struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
5079563db9STomas Winkler {
51b7d88514STomas Winkler 	if (me_cl && kref_get_unless_zero(&me_cl->refcnt))
5279563db9STomas Winkler 		return me_cl;
53b7d88514STomas Winkler 
54b7d88514STomas Winkler 	return NULL;
5579563db9STomas Winkler }
5679563db9STomas Winkler 
5779563db9STomas Winkler /**
58b7d88514STomas Winkler  * mei_me_cl_release - free me client
5979563db9STomas Winkler  *
6079563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
6179563db9STomas Winkler  *
6279563db9STomas Winkler  * @ref: me_client refcount
6379563db9STomas Winkler  */
6479563db9STomas Winkler static void mei_me_cl_release(struct kref *ref)
6579563db9STomas Winkler {
6679563db9STomas Winkler 	struct mei_me_client *me_cl =
6779563db9STomas Winkler 		container_of(ref, struct mei_me_client, refcnt);
68b7d88514STomas Winkler 
6979563db9STomas Winkler 	kfree(me_cl);
7079563db9STomas Winkler }
71b7d88514STomas Winkler 
7279563db9STomas Winkler /**
7379563db9STomas Winkler  * mei_me_cl_put - decrease me client refcount and free client if necessary
7479563db9STomas Winkler  *
7579563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
7679563db9STomas Winkler  *
7779563db9STomas Winkler  * @me_cl: me client
7879563db9STomas Winkler  */
7979563db9STomas Winkler void mei_me_cl_put(struct mei_me_client *me_cl)
8079563db9STomas Winkler {
8179563db9STomas Winkler 	if (me_cl)
8279563db9STomas Winkler 		kref_put(&me_cl->refcnt, mei_me_cl_release);
8379563db9STomas Winkler }
8479563db9STomas Winkler 
8579563db9STomas Winkler /**
86b7d88514STomas Winkler  * __mei_me_cl_del  - delete me client form the list and decrease
87b7d88514STomas Winkler  *     reference counter
88b7d88514STomas Winkler  *
89b7d88514STomas Winkler  * @dev: mei device
90b7d88514STomas Winkler  * @me_cl: me client
91b7d88514STomas Winkler  *
92b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
93b7d88514STomas Winkler  */
94b7d88514STomas Winkler static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
95b7d88514STomas Winkler {
96b7d88514STomas Winkler 	if (!me_cl)
97b7d88514STomas Winkler 		return;
98b7d88514STomas Winkler 
99b7d88514STomas Winkler 	list_del(&me_cl->list);
100b7d88514STomas Winkler 	mei_me_cl_put(me_cl);
101b7d88514STomas Winkler }
102b7d88514STomas Winkler 
103b7d88514STomas Winkler /**
104b7d88514STomas Winkler  * mei_me_cl_add - add me client to the list
105b7d88514STomas Winkler  *
106b7d88514STomas Winkler  * @dev: mei device
107b7d88514STomas Winkler  * @me_cl: me client
108b7d88514STomas Winkler  */
109b7d88514STomas Winkler void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl)
110b7d88514STomas Winkler {
111b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
112b7d88514STomas Winkler 	list_add(&me_cl->list, &dev->me_clients);
113b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
114b7d88514STomas Winkler }
115b7d88514STomas Winkler 
116b7d88514STomas Winkler /**
117b7d88514STomas Winkler  * __mei_me_cl_by_uuid - locate me client by uuid
118b7d88514STomas Winkler  *	increases ref count
119b7d88514STomas Winkler  *
120b7d88514STomas Winkler  * @dev: mei device
121b7d88514STomas Winkler  * @uuid: me client uuid
122b7d88514STomas Winkler  *
123b7d88514STomas Winkler  * Return: me client or NULL if not found
124b7d88514STomas Winkler  *
125b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
126b7d88514STomas Winkler  */
127b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev,
128b7d88514STomas Winkler 					const uuid_le *uuid)
129b7d88514STomas Winkler {
130b7d88514STomas Winkler 	struct mei_me_client *me_cl;
131b7d88514STomas Winkler 	const uuid_le *pn;
132b7d88514STomas Winkler 
133b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
134b7d88514STomas Winkler 
135b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
136b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
137b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0)
138b7d88514STomas Winkler 			return mei_me_cl_get(me_cl);
139b7d88514STomas Winkler 	}
140b7d88514STomas Winkler 
141b7d88514STomas Winkler 	return NULL;
142b7d88514STomas Winkler }
143b7d88514STomas Winkler 
144b7d88514STomas Winkler /**
145a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid - locate me client by uuid
14679563db9STomas Winkler  *	increases ref count
14790e0b5f1STomas Winkler  *
14890e0b5f1STomas Winkler  * @dev: mei device
149a8605ea2SAlexander Usyskin  * @uuid: me client uuid
150a27a76d3SAlexander Usyskin  *
151a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
152b7d88514STomas Winkler  *
153b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
15490e0b5f1STomas Winkler  */
155b7d88514STomas Winkler struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev,
156d320832fSTomas Winkler 					const uuid_le *uuid)
15790e0b5f1STomas Winkler {
1585ca2d388STomas Winkler 	struct mei_me_client *me_cl;
15990e0b5f1STomas Winkler 
160b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
161b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
162b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
16390e0b5f1STomas Winkler 
164b7d88514STomas Winkler 	return me_cl;
16590e0b5f1STomas Winkler }
16690e0b5f1STomas Winkler 
16790e0b5f1STomas Winkler /**
168a8605ea2SAlexander Usyskin  * mei_me_cl_by_id - locate me client by client id
16979563db9STomas Winkler  *	increases ref count
17090e0b5f1STomas Winkler  *
17190e0b5f1STomas Winkler  * @dev: the device structure
17290e0b5f1STomas Winkler  * @client_id: me client id
17390e0b5f1STomas Winkler  *
174a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
175b7d88514STomas Winkler  *
176b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
17790e0b5f1STomas Winkler  */
178d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
17990e0b5f1STomas Winkler {
180a27a76d3SAlexander Usyskin 
181b7d88514STomas Winkler 	struct mei_me_client *__me_cl, *me_cl = NULL;
182a27a76d3SAlexander Usyskin 
183b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
184b7d88514STomas Winkler 	list_for_each_entry(__me_cl, &dev->me_clients, list) {
185b7d88514STomas Winkler 		if (__me_cl->client_id == client_id) {
186b7d88514STomas Winkler 			me_cl = mei_me_cl_get(__me_cl);
187b7d88514STomas Winkler 			break;
188b7d88514STomas Winkler 		}
189b7d88514STomas Winkler 	}
190b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
191b7d88514STomas Winkler 
192b7d88514STomas Winkler 	return me_cl;
193b7d88514STomas Winkler }
194b7d88514STomas Winkler 
195b7d88514STomas Winkler /**
196b7d88514STomas Winkler  * __mei_me_cl_by_uuid_id - locate me client by client id and uuid
197b7d88514STomas Winkler  *	increases ref count
198b7d88514STomas Winkler  *
199b7d88514STomas Winkler  * @dev: the device structure
200b7d88514STomas Winkler  * @uuid: me client uuid
201b7d88514STomas Winkler  * @client_id: me client id
202b7d88514STomas Winkler  *
203b7d88514STomas Winkler  * Return: me client or null if not found
204b7d88514STomas Winkler  *
205b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
206b7d88514STomas Winkler  */
207b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev,
208b7d88514STomas Winkler 					   const uuid_le *uuid, u8 client_id)
209b7d88514STomas Winkler {
210b7d88514STomas Winkler 	struct mei_me_client *me_cl;
211b7d88514STomas Winkler 	const uuid_le *pn;
212b7d88514STomas Winkler 
213b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
214b7d88514STomas Winkler 
215b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
216b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
217b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0 &&
218b7d88514STomas Winkler 		    me_cl->client_id == client_id)
21979563db9STomas Winkler 			return mei_me_cl_get(me_cl);
220b7d88514STomas Winkler 	}
22179563db9STomas Winkler 
222d320832fSTomas Winkler 	return NULL;
22390e0b5f1STomas Winkler }
2249ca9050bSTomas Winkler 
225b7d88514STomas Winkler 
226a8605ea2SAlexander Usyskin /**
227a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid_id - locate me client by client id and uuid
22879563db9STomas Winkler  *	increases ref count
229a8605ea2SAlexander Usyskin  *
230a8605ea2SAlexander Usyskin  * @dev: the device structure
231a8605ea2SAlexander Usyskin  * @uuid: me client uuid
232a8605ea2SAlexander Usyskin  * @client_id: me client id
233a8605ea2SAlexander Usyskin  *
234b7d88514STomas Winkler  * Return: me client or null if not found
235a8605ea2SAlexander Usyskin  */
236d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
237d880f329STomas Winkler 					   const uuid_le *uuid, u8 client_id)
238d880f329STomas Winkler {
239d880f329STomas Winkler 	struct mei_me_client *me_cl;
240d880f329STomas Winkler 
241b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
242b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id);
243b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
24479563db9STomas Winkler 
245b7d88514STomas Winkler 	return me_cl;
246d880f329STomas Winkler }
247d880f329STomas Winkler 
24825ca6472STomas Winkler /**
24979563db9STomas Winkler  * mei_me_cl_rm_by_uuid - remove all me clients matching uuid
25025ca6472STomas Winkler  *
25125ca6472STomas Winkler  * @dev: the device structure
25225ca6472STomas Winkler  * @uuid: me client uuid
25379563db9STomas Winkler  *
25479563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
25525ca6472STomas Winkler  */
25679563db9STomas Winkler void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
25725ca6472STomas Winkler {
258b7d88514STomas Winkler 	struct mei_me_client *me_cl;
25925ca6472STomas Winkler 
26079563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl\n", uuid);
261b7d88514STomas Winkler 
262b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
263b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
264b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
265b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
26679563db9STomas Winkler }
26779563db9STomas Winkler 
26879563db9STomas Winkler /**
26979563db9STomas Winkler  * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id
27079563db9STomas Winkler  *
27179563db9STomas Winkler  * @dev: the device structure
27279563db9STomas Winkler  * @uuid: me client uuid
27379563db9STomas Winkler  * @id: me client id
27479563db9STomas Winkler  *
27579563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
27679563db9STomas Winkler  */
27779563db9STomas Winkler void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
27879563db9STomas Winkler {
279b7d88514STomas Winkler 	struct mei_me_client *me_cl;
28079563db9STomas Winkler 
28179563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
282b7d88514STomas Winkler 
283b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
284b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id);
285b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
286b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
28725ca6472STomas Winkler }
28879563db9STomas Winkler 
28979563db9STomas Winkler /**
29079563db9STomas Winkler  * mei_me_cl_rm_all - remove all me clients
29179563db9STomas Winkler  *
29279563db9STomas Winkler  * @dev: the device structure
29379563db9STomas Winkler  *
29479563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
29579563db9STomas Winkler  */
29679563db9STomas Winkler void mei_me_cl_rm_all(struct mei_device *dev)
29779563db9STomas Winkler {
29879563db9STomas Winkler 	struct mei_me_client *me_cl, *next;
29979563db9STomas Winkler 
300b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
30179563db9STomas Winkler 	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
302b7d88514STomas Winkler 		__mei_me_cl_del(dev, me_cl);
303b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
30425ca6472STomas Winkler }
30525ca6472STomas Winkler 
3069ca9050bSTomas Winkler /**
307cc99ecfdSTomas Winkler  * mei_cl_cmp_id - tells if the clients are the same
308cc99ecfdSTomas Winkler  *
309cc99ecfdSTomas Winkler  * @cl1: host client 1
310cc99ecfdSTomas Winkler  * @cl2: host client 2
311cc99ecfdSTomas Winkler  *
312a8605ea2SAlexander Usyskin  * Return: true  - if the clients has same host and me ids
313cc99ecfdSTomas Winkler  *         false - otherwise
314cc99ecfdSTomas Winkler  */
315cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
316cc99ecfdSTomas Winkler 				const struct mei_cl *cl2)
317cc99ecfdSTomas Winkler {
318cc99ecfdSTomas Winkler 	return cl1 && cl2 &&
319cc99ecfdSTomas Winkler 		(cl1->host_client_id == cl2->host_client_id) &&
320cc99ecfdSTomas Winkler 		(cl1->me_client_id == cl2->me_client_id);
321cc99ecfdSTomas Winkler }
322cc99ecfdSTomas Winkler 
323cc99ecfdSTomas Winkler /**
3249ca9050bSTomas Winkler  * mei_io_cb_free - free mei_cb_private related memory
3259ca9050bSTomas Winkler  *
3269ca9050bSTomas Winkler  * @cb: mei callback struct
3279ca9050bSTomas Winkler  */
3289ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb)
3299ca9050bSTomas Winkler {
3309ca9050bSTomas Winkler 	if (cb == NULL)
3319ca9050bSTomas Winkler 		return;
3329ca9050bSTomas Winkler 
333928fa666STomas Winkler 	list_del(&cb->list);
3345db7514dSTomas Winkler 	kfree(cb->buf.data);
3359ca9050bSTomas Winkler 	kfree(cb);
3369ca9050bSTomas Winkler }
3379ca9050bSTomas Winkler 
3389ca9050bSTomas Winkler /**
3399ca9050bSTomas Winkler  * mei_io_cb_init - allocate and initialize io callback
3409ca9050bSTomas Winkler  *
341a8605ea2SAlexander Usyskin  * @cl: mei client
342bca67d68STomas Winkler  * @type: operation type
343393b148fSMasanari Iida  * @fp: pointer to file structure
3449ca9050bSTomas Winkler  *
345a8605ea2SAlexander Usyskin  * Return: mei_cl_cb pointer or NULL;
3469ca9050bSTomas Winkler  */
347bca67d68STomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
348bca67d68STomas Winkler 				 struct file *fp)
3499ca9050bSTomas Winkler {
3509ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
3519ca9050bSTomas Winkler 
3529ca9050bSTomas Winkler 	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
3539ca9050bSTomas Winkler 	if (!cb)
3549ca9050bSTomas Winkler 		return NULL;
3559ca9050bSTomas Winkler 
356928fa666STomas Winkler 	INIT_LIST_HEAD(&cb->list);
3579ca9050bSTomas Winkler 	cb->file_object = fp;
3589ca9050bSTomas Winkler 	cb->cl = cl;
3599ca9050bSTomas Winkler 	cb->buf_idx = 0;
360bca67d68STomas Winkler 	cb->fop_type = type;
3619ca9050bSTomas Winkler 	return cb;
3629ca9050bSTomas Winkler }
3639ca9050bSTomas Winkler 
3649ca9050bSTomas Winkler /**
365928fa666STomas Winkler  * __mei_io_list_flush - removes and frees cbs belonging to cl.
366928fa666STomas Winkler  *
367928fa666STomas Winkler  * @list:  an instance of our list structure
368928fa666STomas Winkler  * @cl:    host client, can be NULL for flushing the whole list
369928fa666STomas Winkler  * @free:  whether to free the cbs
370928fa666STomas Winkler  */
371928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list,
372928fa666STomas Winkler 				struct mei_cl *cl, bool free)
373928fa666STomas Winkler {
374928fa666STomas Winkler 	struct mei_cl_cb *cb, *next;
375928fa666STomas Winkler 
376928fa666STomas Winkler 	/* enable removing everything if no cl is specified */
377928fa666STomas Winkler 	list_for_each_entry_safe(cb, next, &list->list, list) {
378928fa666STomas Winkler 		if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
379928fa666STomas Winkler 			list_del_init(&cb->list);
380928fa666STomas Winkler 			if (free)
381928fa666STomas Winkler 				mei_io_cb_free(cb);
382928fa666STomas Winkler 		}
383928fa666STomas Winkler 	}
384928fa666STomas Winkler }
385928fa666STomas Winkler 
386928fa666STomas Winkler /**
387928fa666STomas Winkler  * mei_io_list_flush - removes list entry belonging to cl.
388928fa666STomas Winkler  *
389928fa666STomas Winkler  * @list:  An instance of our list structure
390928fa666STomas Winkler  * @cl: host client
391928fa666STomas Winkler  */
392928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
393928fa666STomas Winkler {
394928fa666STomas Winkler 	__mei_io_list_flush(list, cl, false);
395928fa666STomas Winkler }
396928fa666STomas Winkler 
397928fa666STomas Winkler /**
398928fa666STomas Winkler  * mei_io_list_free - removes cb belonging to cl and free them
399928fa666STomas Winkler  *
400928fa666STomas Winkler  * @list:  An instance of our list structure
401928fa666STomas Winkler  * @cl: host client
402928fa666STomas Winkler  */
403928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
404928fa666STomas Winkler {
405928fa666STomas Winkler 	__mei_io_list_flush(list, cl, true);
406928fa666STomas Winkler }
407928fa666STomas Winkler 
408928fa666STomas Winkler /**
4095db7514dSTomas Winkler  * mei_io_cb_alloc_buf - allocate callback buffer
4109ca9050bSTomas Winkler  *
411393b148fSMasanari Iida  * @cb: io callback structure
412393b148fSMasanari Iida  * @length: size of the buffer
4139ca9050bSTomas Winkler  *
414a8605ea2SAlexander Usyskin  * Return: 0 on success
4159ca9050bSTomas Winkler  *         -EINVAL if cb is NULL
4169ca9050bSTomas Winkler  *         -ENOMEM if allocation failed
4179ca9050bSTomas Winkler  */
4185db7514dSTomas Winkler int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
4199ca9050bSTomas Winkler {
4209ca9050bSTomas Winkler 	if (!cb)
4219ca9050bSTomas Winkler 		return -EINVAL;
4229ca9050bSTomas Winkler 
4239ca9050bSTomas Winkler 	if (length == 0)
4249ca9050bSTomas Winkler 		return 0;
4259ca9050bSTomas Winkler 
4265db7514dSTomas Winkler 	cb->buf.data = kmalloc(length, GFP_KERNEL);
4275db7514dSTomas Winkler 	if (!cb->buf.data)
4289ca9050bSTomas Winkler 		return -ENOMEM;
4295db7514dSTomas Winkler 	cb->buf.size = length;
4309ca9050bSTomas Winkler 	return 0;
4319ca9050bSTomas Winkler }
4329ca9050bSTomas Winkler 
4339ca9050bSTomas Winkler /**
434bca67d68STomas Winkler  * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
435bca67d68STomas Winkler  *
436bca67d68STomas Winkler  * @cl: host client
437bca67d68STomas Winkler  * @length: size of the buffer
438bca67d68STomas Winkler  * @type: operation type
439bca67d68STomas Winkler  * @fp: associated file pointer (might be NULL)
440bca67d68STomas Winkler  *
441bca67d68STomas Winkler  * Return: cb on success and NULL on failure
442bca67d68STomas Winkler  */
443bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
444bca67d68STomas Winkler 				  enum mei_cb_file_ops type, struct file *fp)
445bca67d68STomas Winkler {
446bca67d68STomas Winkler 	struct mei_cl_cb *cb;
447bca67d68STomas Winkler 
448bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, type, fp);
449bca67d68STomas Winkler 	if (!cb)
450bca67d68STomas Winkler 		return NULL;
451bca67d68STomas Winkler 
452bca67d68STomas Winkler 	if (mei_io_cb_alloc_buf(cb, length)) {
453bca67d68STomas Winkler 		mei_io_cb_free(cb);
454bca67d68STomas Winkler 		return NULL;
455bca67d68STomas Winkler 	}
456bca67d68STomas Winkler 
457bca67d68STomas Winkler 	return cb;
458bca67d68STomas Winkler }
459bca67d68STomas Winkler 
460bca67d68STomas Winkler /**
461a9bed610STomas Winkler  * mei_cl_read_cb - find this cl's callback in the read list
462a9bed610STomas Winkler  *     for a specific file
463a9bed610STomas Winkler  *
464a9bed610STomas Winkler  * @cl: host client
465a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
466a9bed610STomas Winkler  *
467a9bed610STomas Winkler  * Return: cb on success, NULL if cb is not found
468a9bed610STomas Winkler  */
469a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
470a9bed610STomas Winkler {
471a9bed610STomas Winkler 	struct mei_cl_cb *cb;
472a9bed610STomas Winkler 
473a9bed610STomas Winkler 	list_for_each_entry(cb, &cl->rd_completed, list)
474a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
475a9bed610STomas Winkler 			return cb;
476a9bed610STomas Winkler 
477a9bed610STomas Winkler 	return NULL;
478a9bed610STomas Winkler }
479a9bed610STomas Winkler 
480a9bed610STomas Winkler /**
481a9bed610STomas Winkler  * mei_cl_read_cb_flush - free client's read pending and completed cbs
482a9bed610STomas Winkler  *   for a specific file
483a9bed610STomas Winkler  *
484a9bed610STomas Winkler  * @cl: host client
485a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
486a9bed610STomas Winkler  */
487a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
488a9bed610STomas Winkler {
489a9bed610STomas Winkler 	struct mei_cl_cb *cb, *next;
490a9bed610STomas Winkler 
491a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
492a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
493a9bed610STomas Winkler 			mei_io_cb_free(cb);
494a9bed610STomas Winkler 
495a9bed610STomas Winkler 
496a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
497a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
498a9bed610STomas Winkler 			mei_io_cb_free(cb);
499a9bed610STomas Winkler }
500a9bed610STomas Winkler 
501a9bed610STomas Winkler /**
5029ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
5039ca9050bSTomas Winkler  *
5049ca9050bSTomas Winkler  * @cl: host client
505a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
506ce23139cSAlexander Usyskin  *
507ce23139cSAlexander Usyskin  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
5089ca9050bSTomas Winkler  */
509a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
5109ca9050bSTomas Winkler {
511c0abffbdSAlexander Usyskin 	struct mei_device *dev;
512c0abffbdSAlexander Usyskin 
51390e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5149ca9050bSTomas Winkler 		return -EINVAL;
5159ca9050bSTomas Winkler 
516c0abffbdSAlexander Usyskin 	dev = cl->dev;
517c0abffbdSAlexander Usyskin 
518c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
519cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_list, cl);
520cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_waiting_list, cl);
5219ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
5229ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
5239ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
5249ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
525a9bed610STomas Winkler 
526a9bed610STomas Winkler 	mei_cl_read_cb_flush(cl, fp);
527a9bed610STomas Winkler 
5289ca9050bSTomas Winkler 	return 0;
5299ca9050bSTomas Winkler }
5309ca9050bSTomas Winkler 
5319ca9050bSTomas Winkler 
5329ca9050bSTomas Winkler /**
53383ce0741SAlexander Usyskin  * mei_cl_init - initializes cl.
5349ca9050bSTomas Winkler  *
5359ca9050bSTomas Winkler  * @cl: host client to be initialized
5369ca9050bSTomas Winkler  * @dev: mei device
5379ca9050bSTomas Winkler  */
5389ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
5399ca9050bSTomas Winkler {
5409ca9050bSTomas Winkler 	memset(cl, 0, sizeof(struct mei_cl));
5419ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
5429ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
5439ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
544a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_completed);
545a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_pending);
5469ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
547a7b71bc0SSamuel Ortiz 	INIT_LIST_HEAD(&cl->device_link);
5489ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
5499ca9050bSTomas Winkler 	cl->dev = dev;
5509ca9050bSTomas Winkler }
5519ca9050bSTomas Winkler 
5529ca9050bSTomas Winkler /**
5539ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
5549ca9050bSTomas Winkler  *
5559ca9050bSTomas Winkler  * @dev: mei device
556a8605ea2SAlexander Usyskin  * Return:  The allocated file or NULL on failure
5579ca9050bSTomas Winkler  */
5589ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
5599ca9050bSTomas Winkler {
5609ca9050bSTomas Winkler 	struct mei_cl *cl;
5619ca9050bSTomas Winkler 
5629ca9050bSTomas Winkler 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
5639ca9050bSTomas Winkler 	if (!cl)
5649ca9050bSTomas Winkler 		return NULL;
5659ca9050bSTomas Winkler 
5669ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
5679ca9050bSTomas Winkler 
5689ca9050bSTomas Winkler 	return cl;
5699ca9050bSTomas Winkler }
5709ca9050bSTomas Winkler 
57190e0b5f1STomas Winkler /**
5723908be6fSAlexander Usyskin  * mei_cl_link - allocate host id in the host map
5739ca9050bSTomas Winkler  *
5743908be6fSAlexander Usyskin  * @cl: host client
57503b8d341STomas Winkler  * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
576393b148fSMasanari Iida  *
577a8605ea2SAlexander Usyskin  * Return: 0 on success
5789ca9050bSTomas Winkler  *	-EINVAL on incorrect values
57903b8d341STomas Winkler  *	-EMFILE if open count exceeded.
5809ca9050bSTomas Winkler  */
581781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id)
5829ca9050bSTomas Winkler {
58390e0b5f1STomas Winkler 	struct mei_device *dev;
58422f96a0eSTomas Winkler 	long open_handle_count;
5859ca9050bSTomas Winkler 
586781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5879ca9050bSTomas Winkler 		return -EINVAL;
5889ca9050bSTomas Winkler 
58990e0b5f1STomas Winkler 	dev = cl->dev;
59090e0b5f1STomas Winkler 
59183ce0741SAlexander Usyskin 	/* If Id is not assigned get one*/
592781d0d89STomas Winkler 	if (id == MEI_HOST_CLIENT_ID_ANY)
593781d0d89STomas Winkler 		id = find_first_zero_bit(dev->host_clients_map,
594781d0d89STomas Winkler 					MEI_CLIENTS_MAX);
5959ca9050bSTomas Winkler 
596781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
5972bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
598e036cc57STomas Winkler 		return -EMFILE;
599e036cc57STomas Winkler 	}
600e036cc57STomas Winkler 
60122f96a0eSTomas Winkler 	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
60222f96a0eSTomas Winkler 	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6032bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
604e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
605e036cc57STomas Winkler 		return -EMFILE;
6069ca9050bSTomas Winkler 	}
607781d0d89STomas Winkler 
608781d0d89STomas Winkler 	dev->open_handle_count++;
609781d0d89STomas Winkler 
610781d0d89STomas Winkler 	cl->host_client_id = id;
611781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
612781d0d89STomas Winkler 
613781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
614781d0d89STomas Winkler 
615781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
616781d0d89STomas Winkler 
617c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
618781d0d89STomas Winkler 	return 0;
619781d0d89STomas Winkler }
620781d0d89STomas Winkler 
6219ca9050bSTomas Winkler /**
62290e0b5f1STomas Winkler  * mei_cl_unlink - remove me_cl from the list
6239ca9050bSTomas Winkler  *
624393b148fSMasanari Iida  * @cl: host client
625ce23139cSAlexander Usyskin  *
626ce23139cSAlexander Usyskin  * Return: always 0
6279ca9050bSTomas Winkler  */
62890e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6299ca9050bSTomas Winkler {
63090e0b5f1STomas Winkler 	struct mei_device *dev;
63190e0b5f1STomas Winkler 
632781d0d89STomas Winkler 	/* don't shout on error exit path */
633781d0d89STomas Winkler 	if (!cl)
634781d0d89STomas Winkler 		return 0;
635781d0d89STomas Winkler 
6368e9a4a9aSTomas Winkler 	/* wd and amthif might not be initialized */
6378e9a4a9aSTomas Winkler 	if (!cl->dev)
6388e9a4a9aSTomas Winkler 		return 0;
63990e0b5f1STomas Winkler 
64090e0b5f1STomas Winkler 	dev = cl->dev;
64190e0b5f1STomas Winkler 
642a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
643a14c44d8STomas Winkler 
64422f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
64522f96a0eSTomas Winkler 		dev->open_handle_count--;
64622f96a0eSTomas Winkler 
64722f96a0eSTomas Winkler 	/* never clear the 0 bit */
64822f96a0eSTomas Winkler 	if (cl->host_client_id)
64922f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
65022f96a0eSTomas Winkler 
65122f96a0eSTomas Winkler 	list_del_init(&cl->link);
65222f96a0eSTomas Winkler 
65322f96a0eSTomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
65422f96a0eSTomas Winkler 
65590e0b5f1STomas Winkler 	return 0;
6569ca9050bSTomas Winkler }
6579ca9050bSTomas Winkler 
6589ca9050bSTomas Winkler 
6599ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work)
6609ca9050bSTomas Winkler {
661b7d88514STomas Winkler 	struct mei_device *dev =
662b7d88514STomas Winkler 		container_of(work, struct mei_device, init_work);
6635ca2d388STomas Winkler 	struct mei_me_client *me_cl;
6649ca9050bSTomas Winkler 
6659ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
6669ca9050bSTomas Winkler 
6679ca9050bSTomas Winkler 
668b7d88514STomas Winkler 	me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
669b7d88514STomas Winkler 	if (me_cl)
6709ca9050bSTomas Winkler 		mei_amthif_host_init(dev);
671b43baf69STomas Winkler 	mei_me_cl_put(me_cl);
672b7d88514STomas Winkler 
673b7d88514STomas Winkler 	me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
674b7d88514STomas Winkler 	if (me_cl)
6759ca9050bSTomas Winkler 		mei_wd_host_init(dev);
676b43baf69STomas Winkler 	mei_me_cl_put(me_cl);
677b7d88514STomas Winkler 
678b7d88514STomas Winkler 	me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
679b7d88514STomas Winkler 	if (me_cl)
68059fcd7c6SSamuel Ortiz 		mei_nfc_host_init(dev);
681b43baf69STomas Winkler 	mei_me_cl_put(me_cl);
68259fcd7c6SSamuel Ortiz 
6839ca9050bSTomas Winkler 
6849ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
6856adb8efbSTomas Winkler 	dev->reset_count = 0;
6869ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
68704bb139aSTomas Winkler 
6882bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
6892bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
6902bf94cabSTomas Winkler 	pm_runtime_autosuspend(dev->dev);
6919ca9050bSTomas Winkler }
6929ca9050bSTomas Winkler 
6936aae48ffSTomas Winkler /**
694a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
6956aae48ffSTomas Winkler  *
6966aae48ffSTomas Winkler  * @dev: the device structure
697a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
6986aae48ffSTomas Winkler  */
6996aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
7006aae48ffSTomas Winkler {
70104bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
70204bb139aSTomas Winkler 	    dev->pg_event == MEI_PG_EVENT_WAIT) {
7032bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
70404bb139aSTomas Winkler 		return false;
70504bb139aSTomas Winkler 	}
70604bb139aSTomas Winkler 
7076aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
7082bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
7096aae48ffSTomas Winkler 		return false;
7106aae48ffSTomas Winkler 	}
7116aae48ffSTomas Winkler 
7126aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7136aae48ffSTomas Winkler 
7146aae48ffSTomas Winkler 	return true;
7156aae48ffSTomas Winkler }
7169ca9050bSTomas Winkler 
7179ca9050bSTomas Winkler /**
71883ce0741SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
7199ca9050bSTomas Winkler  *
72090e0b5f1STomas Winkler  * @cl: host client
7219ca9050bSTomas Winkler  *
7229ca9050bSTomas Winkler  * Locking: called under "dev->device_lock" lock
7239ca9050bSTomas Winkler  *
724a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
7259ca9050bSTomas Winkler  */
72690e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl)
7279ca9050bSTomas Winkler {
72890e0b5f1STomas Winkler 	struct mei_device *dev;
7299ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
730fe2f17ebSAlexander Usyskin 	int rets;
7319ca9050bSTomas Winkler 
73290e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
7339ca9050bSTomas Winkler 		return -ENODEV;
7349ca9050bSTomas Winkler 
73590e0b5f1STomas Winkler 	dev = cl->dev;
73690e0b5f1STomas Winkler 
737c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
738c0abffbdSAlexander Usyskin 
7399ca9050bSTomas Winkler 	if (cl->state != MEI_FILE_DISCONNECTING)
7409ca9050bSTomas Winkler 		return 0;
7419ca9050bSTomas Winkler 
7422bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
74304bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
7442bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
74504bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
74604bb139aSTomas Winkler 		return rets;
74704bb139aSTomas Winkler 	}
74804bb139aSTomas Winkler 
749bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
750bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
751bca67d68STomas Winkler 	if (rets)
75204bb139aSTomas Winkler 		goto free;
7535a8373fbSTomas Winkler 
7546aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
7559ca9050bSTomas Winkler 		if (mei_hbm_cl_disconnect_req(dev, cl)) {
7569ca9050bSTomas Winkler 			rets = -ENODEV;
757c0abffbdSAlexander Usyskin 			cl_err(dev, cl, "failed to disconnect.\n");
7589ca9050bSTomas Winkler 			goto free;
7599ca9050bSTomas Winkler 		}
76022b987a3SAlexander Usyskin 		cl->timer_count = MEI_CONNECT_TIMEOUT;
7619ca9050bSTomas Winkler 		mdelay(10); /* Wait for hardware disconnection ready */
7629ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
7639ca9050bSTomas Winkler 	} else {
764c0abffbdSAlexander Usyskin 		cl_dbg(dev, cl, "add disconnect cb to control write list\n");
7659ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
7669ca9050bSTomas Winkler 
7679ca9050bSTomas Winkler 	}
7689ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
7699ca9050bSTomas Winkler 
77012f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
7719ca9050bSTomas Winkler 			MEI_FILE_DISCONNECTED == cl->state,
7729ca9050bSTomas Winkler 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
7739ca9050bSTomas Winkler 
7749ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
775fe2f17ebSAlexander Usyskin 
7769ca9050bSTomas Winkler 	if (MEI_FILE_DISCONNECTED == cl->state) {
7779ca9050bSTomas Winkler 		rets = 0;
778c0abffbdSAlexander Usyskin 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
7799ca9050bSTomas Winkler 	} else {
780fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
781fe2f17ebSAlexander Usyskin 		rets = -ETIME;
7829ca9050bSTomas Winkler 	}
7839ca9050bSTomas Winkler 
7849ca9050bSTomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7859ca9050bSTomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
7869ca9050bSTomas Winkler free:
78704bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
7882bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
7892bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
79004bb139aSTomas Winkler 
7919ca9050bSTomas Winkler 	mei_io_cb_free(cb);
7929ca9050bSTomas Winkler 	return rets;
7939ca9050bSTomas Winkler }
7949ca9050bSTomas Winkler 
7959ca9050bSTomas Winkler 
7969ca9050bSTomas Winkler /**
79790e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
79890e0b5f1STomas Winkler  *    client with the same me client id is connecting
7999ca9050bSTomas Winkler  *
8009ca9050bSTomas Winkler  * @cl: private data of the file object
8019ca9050bSTomas Winkler  *
802a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
8039ca9050bSTomas Winkler  */
80490e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl)
8059ca9050bSTomas Winkler {
80690e0b5f1STomas Winkler 	struct mei_device *dev;
80731f88f57STomas Winkler 	struct mei_cl *ocl; /* the other client */
8089ca9050bSTomas Winkler 
80990e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
81090e0b5f1STomas Winkler 		return false;
81190e0b5f1STomas Winkler 
81290e0b5f1STomas Winkler 	dev = cl->dev;
81390e0b5f1STomas Winkler 
81431f88f57STomas Winkler 	list_for_each_entry(ocl, &dev->file_list, link) {
81531f88f57STomas Winkler 		if (ocl->state == MEI_FILE_CONNECTING &&
81631f88f57STomas Winkler 		    ocl != cl &&
81731f88f57STomas Winkler 		    cl->me_client_id == ocl->me_client_id)
81890e0b5f1STomas Winkler 			return true;
8199ca9050bSTomas Winkler 
8209ca9050bSTomas Winkler 	}
82190e0b5f1STomas Winkler 
82290e0b5f1STomas Winkler 	return false;
8239ca9050bSTomas Winkler }
8249ca9050bSTomas Winkler 
8259ca9050bSTomas Winkler /**
82683ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
8279f81abdaSTomas Winkler  *
8289f81abdaSTomas Winkler  * @cl: host client
829a8605ea2SAlexander Usyskin  * @file: pointer to file structure
8309f81abdaSTomas Winkler  *
8319f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
8329f81abdaSTomas Winkler  *
833a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
8349f81abdaSTomas Winkler  */
8359f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file)
8369f81abdaSTomas Winkler {
8379f81abdaSTomas Winkler 	struct mei_device *dev;
8389f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
8399f81abdaSTomas Winkler 	int rets;
8409f81abdaSTomas Winkler 
8419f81abdaSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
8429f81abdaSTomas Winkler 		return -ENODEV;
8439f81abdaSTomas Winkler 
8449f81abdaSTomas Winkler 	dev = cl->dev;
8459f81abdaSTomas Winkler 
8462bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
84704bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
8482bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
84904bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
85004bb139aSTomas Winkler 		return rets;
85104bb139aSTomas Winkler 	}
85204bb139aSTomas Winkler 
853bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
854bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
855bca67d68STomas Winkler 	if (rets)
8569f81abdaSTomas Winkler 		goto out;
8579f81abdaSTomas Winkler 
8586aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
8596aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
860e4d8270eSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTING;
8619f81abdaSTomas Winkler 		if (mei_hbm_cl_connect_req(dev, cl)) {
8629f81abdaSTomas Winkler 			rets = -ENODEV;
8639f81abdaSTomas Winkler 			goto out;
8649f81abdaSTomas Winkler 		}
8659f81abdaSTomas Winkler 		cl->timer_count = MEI_CONNECT_TIMEOUT;
8669f81abdaSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
8679f81abdaSTomas Winkler 	} else {
86873ab4232SAlexander Usyskin 		cl->state = MEI_FILE_INITIALIZING;
8699f81abdaSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
8709f81abdaSTomas Winkler 	}
8719f81abdaSTomas Winkler 
8729f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
87312f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
8749f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
8759f81abdaSTomas Winkler 			 cl->state == MEI_FILE_DISCONNECTED),
876206ecfc2SFrode Isaksen 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
8779f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
8789f81abdaSTomas Winkler 
8799f81abdaSTomas Winkler 	if (cl->state != MEI_FILE_CONNECTED) {
8803e37ebb7SAlexander Usyskin 		cl->state = MEI_FILE_DISCONNECTED;
881285e2996SAlexander Usyskin 		/* something went really wrong */
882285e2996SAlexander Usyskin 		if (!cl->status)
883285e2996SAlexander Usyskin 			cl->status = -EFAULT;
8849f81abdaSTomas Winkler 
8859f81abdaSTomas Winkler 		mei_io_list_flush(&dev->ctrl_rd_list, cl);
8869f81abdaSTomas Winkler 		mei_io_list_flush(&dev->ctrl_wr_list, cl);
8879f81abdaSTomas Winkler 	}
8889f81abdaSTomas Winkler 
8899f81abdaSTomas Winkler 	rets = cl->status;
8909f81abdaSTomas Winkler 
8919f81abdaSTomas Winkler out:
89204bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
8932bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
8942bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
89504bb139aSTomas Winkler 
8969f81abdaSTomas Winkler 	mei_io_cb_free(cb);
8979f81abdaSTomas Winkler 	return rets;
8989f81abdaSTomas Winkler }
8999f81abdaSTomas Winkler 
9009f81abdaSTomas Winkler /**
90103b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
90203b8d341STomas Winkler  *
90303b8d341STomas Winkler  * @dev: the device structure
90403b8d341STomas Winkler  * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
90503b8d341STomas Winkler  *
90603b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
90703b8d341STomas Winkler  */
90803b8d341STomas Winkler struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id)
90903b8d341STomas Winkler {
91003b8d341STomas Winkler 	struct mei_cl *cl;
91103b8d341STomas Winkler 	int ret;
91203b8d341STomas Winkler 
91303b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
91403b8d341STomas Winkler 	if (!cl) {
91503b8d341STomas Winkler 		ret = -ENOMEM;
91603b8d341STomas Winkler 		goto err;
91703b8d341STomas Winkler 	}
91803b8d341STomas Winkler 
91903b8d341STomas Winkler 	ret = mei_cl_link(cl, id);
92003b8d341STomas Winkler 	if (ret)
92103b8d341STomas Winkler 		goto err;
92203b8d341STomas Winkler 
92303b8d341STomas Winkler 	return cl;
92403b8d341STomas Winkler err:
92503b8d341STomas Winkler 	kfree(cl);
92603b8d341STomas Winkler 	return ERR_PTR(ret);
92703b8d341STomas Winkler }
92803b8d341STomas Winkler 
92903b8d341STomas Winkler 
93003b8d341STomas Winkler 
93103b8d341STomas Winkler /**
93290e0b5f1STomas Winkler  * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
9339ca9050bSTomas Winkler  *
9349ca9050bSTomas Winkler  * @cl: private data of the file object
9359ca9050bSTomas Winkler  *
936a8605ea2SAlexander Usyskin  * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
9379ca9050bSTomas Winkler  *	-ENOENT if mei_cl is not present
9389ca9050bSTomas Winkler  *	-EINVAL if single_recv_buf == 0
9399ca9050bSTomas Winkler  */
94090e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
9419ca9050bSTomas Winkler {
94290e0b5f1STomas Winkler 	struct mei_device *dev;
94312d00665SAlexander Usyskin 	struct mei_me_client *me_cl;
94479563db9STomas Winkler 	int rets = 0;
9459ca9050bSTomas Winkler 
94690e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
94790e0b5f1STomas Winkler 		return -EINVAL;
94890e0b5f1STomas Winkler 
94990e0b5f1STomas Winkler 	dev = cl->dev;
95090e0b5f1STomas Winkler 
9519ca9050bSTomas Winkler 	if (cl->mei_flow_ctrl_creds > 0)
9529ca9050bSTomas Winkler 		return 1;
9539ca9050bSTomas Winkler 
9542e5df413STomas Winkler 	me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
955d320832fSTomas Winkler 	if (!me_cl) {
95612d00665SAlexander Usyskin 		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
957d320832fSTomas Winkler 		return -ENOENT;
95812d00665SAlexander Usyskin 	}
95912d00665SAlexander Usyskin 
96079563db9STomas Winkler 	if (me_cl->mei_flow_ctrl_creds > 0) {
96179563db9STomas Winkler 		rets = 1;
9629ca9050bSTomas Winkler 		if (WARN_ON(me_cl->props.single_recv_buf == 0))
96379563db9STomas Winkler 			rets = -EINVAL;
96412d00665SAlexander Usyskin 	}
96579563db9STomas Winkler 	mei_me_cl_put(me_cl);
96679563db9STomas Winkler 	return rets;
9679ca9050bSTomas Winkler }
9689ca9050bSTomas Winkler 
9699ca9050bSTomas Winkler /**
97090e0b5f1STomas Winkler  * mei_cl_flow_ctrl_reduce - reduces flow_control.
9719ca9050bSTomas Winkler  *
9729ca9050bSTomas Winkler  * @cl: private data of the file object
973393b148fSMasanari Iida  *
974a8605ea2SAlexander Usyskin  * Return:
9759ca9050bSTomas Winkler  *	0 on success
9769ca9050bSTomas Winkler  *	-ENOENT when me client is not found
9779ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
9789ca9050bSTomas Winkler  */
97990e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
9809ca9050bSTomas Winkler {
98190e0b5f1STomas Winkler 	struct mei_device *dev;
98212d00665SAlexander Usyskin 	struct mei_me_client *me_cl;
98379563db9STomas Winkler 	int rets;
9849ca9050bSTomas Winkler 
98590e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
98690e0b5f1STomas Winkler 		return -EINVAL;
98790e0b5f1STomas Winkler 
98890e0b5f1STomas Winkler 	dev = cl->dev;
98990e0b5f1STomas Winkler 
9902e5df413STomas Winkler 	me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
991d320832fSTomas Winkler 	if (!me_cl) {
99212d00665SAlexander Usyskin 		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
993d320832fSTomas Winkler 		return -ENOENT;
99412d00665SAlexander Usyskin 	}
9959ca9050bSTomas Winkler 
996d320832fSTomas Winkler 	if (me_cl->props.single_recv_buf) {
99779563db9STomas Winkler 		if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) {
99879563db9STomas Winkler 			rets = -EINVAL;
99979563db9STomas Winkler 			goto out;
100079563db9STomas Winkler 		}
100112d00665SAlexander Usyskin 		me_cl->mei_flow_ctrl_creds--;
10029ca9050bSTomas Winkler 	} else {
100379563db9STomas Winkler 		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) {
100479563db9STomas Winkler 			rets = -EINVAL;
100579563db9STomas Winkler 			goto out;
100679563db9STomas Winkler 		}
10079ca9050bSTomas Winkler 		cl->mei_flow_ctrl_creds--;
10089ca9050bSTomas Winkler 	}
100979563db9STomas Winkler 	rets = 0;
101079563db9STomas Winkler out:
101179563db9STomas Winkler 	mei_me_cl_put(me_cl);
101279563db9STomas Winkler 	return rets;
10139ca9050bSTomas Winkler }
10149ca9050bSTomas Winkler 
10159ca9050bSTomas Winkler /**
1016393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
10179ca9050bSTomas Winkler  *
101890e0b5f1STomas Winkler  * @cl: host client
1019ce23139cSAlexander Usyskin  * @length: number of bytes to read
1020bca67d68STomas Winkler  * @fp: pointer to file structure
10219ca9050bSTomas Winkler  *
1022a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
10239ca9050bSTomas Winkler  */
1024bca67d68STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
10259ca9050bSTomas Winkler {
102690e0b5f1STomas Winkler 	struct mei_device *dev;
10279ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
1028d320832fSTomas Winkler 	struct mei_me_client *me_cl;
10299ca9050bSTomas Winkler 	int rets;
10309ca9050bSTomas Winkler 
103190e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
103290e0b5f1STomas Winkler 		return -ENODEV;
103390e0b5f1STomas Winkler 
103490e0b5f1STomas Winkler 	dev = cl->dev;
103590e0b5f1STomas Winkler 
1036b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
10379ca9050bSTomas Winkler 		return -ENODEV;
10389ca9050bSTomas Winkler 
1039a9bed610STomas Winkler 	/* HW currently supports only one pending read */
1040a9bed610STomas Winkler 	if (!list_empty(&cl->rd_pending))
10419ca9050bSTomas Winkler 		return -EBUSY;
1042a9bed610STomas Winkler 
1043d880f329STomas Winkler 	me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
1044d320832fSTomas Winkler 	if (!me_cl) {
1045c0abffbdSAlexander Usyskin 		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
10467ca96aa2SAlexander Usyskin 		return  -ENOTTY;
10479ca9050bSTomas Winkler 	}
104879563db9STomas Winkler 	/* always allocate at least client max message */
104979563db9STomas Winkler 	length = max_t(size_t, length, me_cl->props.max_msg_length);
105079563db9STomas Winkler 	mei_me_cl_put(me_cl);
10519ca9050bSTomas Winkler 
10522bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
105304bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
10542bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
105504bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
105604bb139aSTomas Winkler 		return rets;
105704bb139aSTomas Winkler 	}
105804bb139aSTomas Winkler 
1059bca67d68STomas Winkler 	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
1060bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
10619ca9050bSTomas Winkler 	if (rets)
106204bb139aSTomas Winkler 		goto out;
10639ca9050bSTomas Winkler 
10646aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
106586113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
106686113500SAlexander Usyskin 		if (rets < 0)
106704bb139aSTomas Winkler 			goto out;
106804bb139aSTomas Winkler 
1069a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_pending);
10709ca9050bSTomas Winkler 	} else {
10719ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
10729ca9050bSTomas Winkler 	}
1073accb884bSChao Bi 
107404bb139aSTomas Winkler out:
107504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
10762bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
10772bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
107804bb139aSTomas Winkler 
107904bb139aSTomas Winkler 	if (rets)
10809ca9050bSTomas Winkler 		mei_io_cb_free(cb);
108104bb139aSTomas Winkler 
10829ca9050bSTomas Winkler 	return rets;
10839ca9050bSTomas Winkler }
10849ca9050bSTomas Winkler 
1085074b4c01STomas Winkler /**
10869d098192STomas Winkler  * mei_cl_irq_write - write a message to device
108721767546STomas Winkler  *	from the interrupt thread context
108821767546STomas Winkler  *
108921767546STomas Winkler  * @cl: client
109021767546STomas Winkler  * @cb: callback block.
109121767546STomas Winkler  * @cmpl_list: complete list.
109221767546STomas Winkler  *
1093a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
109421767546STomas Winkler  */
10959d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
10969d098192STomas Winkler 		     struct mei_cl_cb *cmpl_list)
109721767546STomas Winkler {
1098136698e5STomas Winkler 	struct mei_device *dev;
1099136698e5STomas Winkler 	struct mei_msg_data *buf;
110021767546STomas Winkler 	struct mei_msg_hdr mei_hdr;
1101136698e5STomas Winkler 	size_t len;
1102136698e5STomas Winkler 	u32 msg_slots;
11039d098192STomas Winkler 	int slots;
11042ebf8c94STomas Winkler 	int rets;
110521767546STomas Winkler 
1106136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1107136698e5STomas Winkler 		return -ENODEV;
1108136698e5STomas Winkler 
1109136698e5STomas Winkler 	dev = cl->dev;
1110136698e5STomas Winkler 
11115db7514dSTomas Winkler 	buf = &cb->buf;
1112136698e5STomas Winkler 
1113136698e5STomas Winkler 	rets = mei_cl_flow_ctrl_creds(cl);
1114136698e5STomas Winkler 	if (rets < 0)
1115136698e5STomas Winkler 		return rets;
1116136698e5STomas Winkler 
1117136698e5STomas Winkler 	if (rets == 0) {
1118136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1119136698e5STomas Winkler 		return 0;
1120136698e5STomas Winkler 	}
1121136698e5STomas Winkler 
11229d098192STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1123136698e5STomas Winkler 	len = buf->size - cb->buf_idx;
1124136698e5STomas Winkler 	msg_slots = mei_data2slots(len);
1125136698e5STomas Winkler 
112621767546STomas Winkler 	mei_hdr.host_addr = cl->host_client_id;
112721767546STomas Winkler 	mei_hdr.me_addr = cl->me_client_id;
112821767546STomas Winkler 	mei_hdr.reserved = 0;
1129479327fcSTomas Winkler 	mei_hdr.internal = cb->internal;
113021767546STomas Winkler 
11319d098192STomas Winkler 	if (slots >= msg_slots) {
113221767546STomas Winkler 		mei_hdr.length = len;
113321767546STomas Winkler 		mei_hdr.msg_complete = 1;
113421767546STomas Winkler 	/* Split the message only if we can write the whole host buffer */
11359d098192STomas Winkler 	} else if (slots == dev->hbuf_depth) {
11369d098192STomas Winkler 		msg_slots = slots;
11379d098192STomas Winkler 		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
113821767546STomas Winkler 		mei_hdr.length = len;
113921767546STomas Winkler 		mei_hdr.msg_complete = 0;
114021767546STomas Winkler 	} else {
114121767546STomas Winkler 		/* wait for next time the host buffer is empty */
114221767546STomas Winkler 		return 0;
114321767546STomas Winkler 	}
114421767546STomas Winkler 
1145c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
11465db7514dSTomas Winkler 			cb->buf.size, cb->buf_idx);
114721767546STomas Winkler 
1148136698e5STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
11492ebf8c94STomas Winkler 	if (rets) {
11502ebf8c94STomas Winkler 		cl->status = rets;
115121767546STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
11522ebf8c94STomas Winkler 		return rets;
115321767546STomas Winkler 	}
115421767546STomas Winkler 
115521767546STomas Winkler 	cl->status = 0;
11564dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
115721767546STomas Winkler 	cb->buf_idx += mei_hdr.length;
11588660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
11594dfaa9f7STomas Winkler 
116021767546STomas Winkler 	if (mei_hdr.msg_complete) {
116121767546STomas Winkler 		if (mei_cl_flow_ctrl_reduce(cl))
11622ebf8c94STomas Winkler 			return -EIO;
116321767546STomas Winkler 		list_move_tail(&cb->list, &dev->write_waiting_list.list);
116421767546STomas Winkler 	}
116521767546STomas Winkler 
116621767546STomas Winkler 	return 0;
116721767546STomas Winkler }
116821767546STomas Winkler 
116921767546STomas Winkler /**
11704234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1171a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
11724234a6deSTomas Winkler  *
11734234a6deSTomas Winkler  * @cl: host client
1174a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
1175ce23139cSAlexander Usyskin  * @blocking: block until completed
11764234a6deSTomas Winkler  *
1177a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
11784234a6deSTomas Winkler  */
11794234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
11804234a6deSTomas Winkler {
11814234a6deSTomas Winkler 	struct mei_device *dev;
11824234a6deSTomas Winkler 	struct mei_msg_data *buf;
11834234a6deSTomas Winkler 	struct mei_msg_hdr mei_hdr;
11844234a6deSTomas Winkler 	int rets;
11854234a6deSTomas Winkler 
11864234a6deSTomas Winkler 
11874234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
11884234a6deSTomas Winkler 		return -ENODEV;
11894234a6deSTomas Winkler 
11904234a6deSTomas Winkler 	if (WARN_ON(!cb))
11914234a6deSTomas Winkler 		return -EINVAL;
11924234a6deSTomas Winkler 
11934234a6deSTomas Winkler 	dev = cl->dev;
11944234a6deSTomas Winkler 
11954234a6deSTomas Winkler 
11965db7514dSTomas Winkler 	buf = &cb->buf;
11974234a6deSTomas Winkler 
11980a01e974SAlexander Usyskin 	cl_dbg(dev, cl, "size=%d\n", buf->size);
11994234a6deSTomas Winkler 
12002bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
120104bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
12022bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
120304bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
120404bb139aSTomas Winkler 		return rets;
120504bb139aSTomas Winkler 	}
12064234a6deSTomas Winkler 
12076aae48ffSTomas Winkler 	cb->buf_idx = 0;
12086aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
12096aae48ffSTomas Winkler 
12106aae48ffSTomas Winkler 	mei_hdr.host_addr = cl->host_client_id;
12116aae48ffSTomas Winkler 	mei_hdr.me_addr = cl->me_client_id;
12126aae48ffSTomas Winkler 	mei_hdr.reserved = 0;
12136aae48ffSTomas Winkler 	mei_hdr.msg_complete = 0;
12146aae48ffSTomas Winkler 	mei_hdr.internal = cb->internal;
12154234a6deSTomas Winkler 
12164234a6deSTomas Winkler 	rets = mei_cl_flow_ctrl_creds(cl);
12174234a6deSTomas Winkler 	if (rets < 0)
12184234a6deSTomas Winkler 		goto err;
12194234a6deSTomas Winkler 
12206aae48ffSTomas Winkler 	if (rets == 0) {
12216aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
12224234a6deSTomas Winkler 		rets = buf->size;
12234234a6deSTomas Winkler 		goto out;
12244234a6deSTomas Winkler 	}
12256aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
12266aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
12276aae48ffSTomas Winkler 		rets = buf->size;
12286aae48ffSTomas Winkler 		goto out;
12296aae48ffSTomas Winkler 	}
12304234a6deSTomas Winkler 
12314234a6deSTomas Winkler 	/* Check for a maximum length */
12324234a6deSTomas Winkler 	if (buf->size > mei_hbuf_max_len(dev)) {
12334234a6deSTomas Winkler 		mei_hdr.length = mei_hbuf_max_len(dev);
12344234a6deSTomas Winkler 		mei_hdr.msg_complete = 0;
12354234a6deSTomas Winkler 	} else {
12364234a6deSTomas Winkler 		mei_hdr.length = buf->size;
12374234a6deSTomas Winkler 		mei_hdr.msg_complete = 1;
12384234a6deSTomas Winkler 	}
12394234a6deSTomas Winkler 
12402ebf8c94STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data);
12412ebf8c94STomas Winkler 	if (rets)
12424234a6deSTomas Winkler 		goto err;
12434234a6deSTomas Winkler 
12444234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
12454234a6deSTomas Winkler 	cb->buf_idx = mei_hdr.length;
12468660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
12474234a6deSTomas Winkler 
12484234a6deSTomas Winkler out:
12494234a6deSTomas Winkler 	if (mei_hdr.msg_complete) {
12507ca96aa2SAlexander Usyskin 		rets = mei_cl_flow_ctrl_reduce(cl);
12517ca96aa2SAlexander Usyskin 		if (rets < 0)
12524234a6deSTomas Winkler 			goto err;
12537ca96aa2SAlexander Usyskin 
12544234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_waiting_list.list);
12554234a6deSTomas Winkler 	} else {
12564234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_list.list);
12574234a6deSTomas Winkler 	}
12584234a6deSTomas Winkler 
12594234a6deSTomas Winkler 
12604234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
12614234a6deSTomas Winkler 
12624234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
12637ca96aa2SAlexander Usyskin 		rets = wait_event_interruptible(cl->tx_wait,
12647ca96aa2SAlexander Usyskin 				cl->writing_state == MEI_WRITE_COMPLETE);
12657ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
12667ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
12677ca96aa2SAlexander Usyskin 		if (rets) {
12684234a6deSTomas Winkler 			if (signal_pending(current))
12694234a6deSTomas Winkler 				rets = -EINTR;
12707ca96aa2SAlexander Usyskin 			goto err;
12714234a6deSTomas Winkler 		}
12724234a6deSTomas Winkler 	}
12737ca96aa2SAlexander Usyskin 
12747ca96aa2SAlexander Usyskin 	rets = buf->size;
12754234a6deSTomas Winkler err:
127604bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
12772bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
12782bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
127904bb139aSTomas Winkler 
12804234a6deSTomas Winkler 	return rets;
12814234a6deSTomas Winkler }
12824234a6deSTomas Winkler 
12834234a6deSTomas Winkler 
1284db086fa9STomas Winkler /**
1285db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
1286db086fa9STomas Winkler  *
1287db086fa9STomas Winkler  * @cl: private data of the file object.
1288db086fa9STomas Winkler  * @cb: callback block.
1289db086fa9STomas Winkler  */
1290db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
1291db086fa9STomas Winkler {
1292db086fa9STomas Winkler 	if (cb->fop_type == MEI_FOP_WRITE) {
1293db086fa9STomas Winkler 		mei_io_cb_free(cb);
1294db086fa9STomas Winkler 		cb = NULL;
1295db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
1296db086fa9STomas Winkler 		if (waitqueue_active(&cl->tx_wait))
1297db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
1298db086fa9STomas Winkler 
1299a9bed610STomas Winkler 	} else if (cb->fop_type == MEI_FOP_READ) {
1300a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_completed);
1301db086fa9STomas Winkler 		if (waitqueue_active(&cl->rx_wait))
13021d9013f0STomas Winkler 			wake_up_interruptible_all(&cl->rx_wait);
1303db086fa9STomas Winkler 		else
1304db086fa9STomas Winkler 			mei_cl_bus_rx_event(cl);
1305db086fa9STomas Winkler 
1306db086fa9STomas Winkler 	}
1307db086fa9STomas Winkler }
1308db086fa9STomas Winkler 
13094234a6deSTomas Winkler 
13104234a6deSTomas Winkler /**
1311074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
1312074b4c01STomas Winkler  *
1313a8605ea2SAlexander Usyskin  * @dev: mei device
1314074b4c01STomas Winkler  */
1315074b4c01STomas Winkler 
1316074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
1317074b4c01STomas Winkler {
131831f88f57STomas Winkler 	struct mei_cl *cl;
1319074b4c01STomas Winkler 
132031f88f57STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link) {
1321074b4c01STomas Winkler 		cl->state = MEI_FILE_DISCONNECTED;
1322074b4c01STomas Winkler 		cl->mei_flow_ctrl_creds = 0;
1323074b4c01STomas Winkler 		cl->timer_count = 0;
1324074b4c01STomas Winkler 	}
1325074b4c01STomas Winkler }
1326074b4c01STomas Winkler 
1327074b4c01STomas Winkler 
1328074b4c01STomas Winkler /**
13295290801cSTomas Winkler  * mei_cl_all_wakeup  - wake up all readers and writers they can be interrupted
1330074b4c01STomas Winkler  *
1331a8605ea2SAlexander Usyskin  * @dev: mei device
1332074b4c01STomas Winkler  */
13335290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev)
1334074b4c01STomas Winkler {
133531f88f57STomas Winkler 	struct mei_cl *cl;
133692db1555STomas Winkler 
133731f88f57STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link) {
1338074b4c01STomas Winkler 		if (waitqueue_active(&cl->rx_wait)) {
1339c0abffbdSAlexander Usyskin 			cl_dbg(dev, cl, "Waking up reading client!\n");
1340074b4c01STomas Winkler 			wake_up_interruptible(&cl->rx_wait);
1341074b4c01STomas Winkler 		}
13425290801cSTomas Winkler 		if (waitqueue_active(&cl->tx_wait)) {
1343c0abffbdSAlexander Usyskin 			cl_dbg(dev, cl, "Waking up writing client!\n");
13445290801cSTomas Winkler 			wake_up_interruptible(&cl->tx_wait);
13455290801cSTomas Winkler 		}
1346074b4c01STomas Winkler 	}
1347074b4c01STomas Winkler }
1348074b4c01STomas Winkler 
1349074b4c01STomas Winkler /**
1350074b4c01STomas Winkler  * mei_cl_all_write_clear - clear all pending writes
1351a8605ea2SAlexander Usyskin  *
1352a8605ea2SAlexander Usyskin  * @dev: mei device
1353074b4c01STomas Winkler  */
1354074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev)
1355074b4c01STomas Winkler {
1356cc99ecfdSTomas Winkler 	mei_io_list_free(&dev->write_list, NULL);
1357cc99ecfdSTomas Winkler 	mei_io_list_free(&dev->write_waiting_list, NULL);
1358074b4c01STomas Winkler }
1359074b4c01STomas Winkler 
1360074b4c01STomas Winkler 
1361