xref: /openbmc/linux/drivers/misc/mei/client.c (revision 13cf9885)
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 /**
86d49ed64aSAlexander Usyskin  * __mei_me_cl_del  - delete me client from 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 
99d49ed64aSAlexander Usyskin 	list_del_init(&me_cl->list);
100b7d88514STomas Winkler 	mei_me_cl_put(me_cl);
101b7d88514STomas Winkler }
102b7d88514STomas Winkler 
103b7d88514STomas Winkler /**
104d49ed64aSAlexander Usyskin  * mei_me_cl_del - delete me client from the list and decrease
105d49ed64aSAlexander Usyskin  *     reference counter
106d49ed64aSAlexander Usyskin  *
107d49ed64aSAlexander Usyskin  * @dev: mei device
108d49ed64aSAlexander Usyskin  * @me_cl: me client
109d49ed64aSAlexander Usyskin  */
110d49ed64aSAlexander Usyskin void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
111d49ed64aSAlexander Usyskin {
112d49ed64aSAlexander Usyskin 	down_write(&dev->me_clients_rwsem);
113d49ed64aSAlexander Usyskin 	__mei_me_cl_del(dev, me_cl);
114d49ed64aSAlexander Usyskin 	up_write(&dev->me_clients_rwsem);
115d49ed64aSAlexander Usyskin }
116d49ed64aSAlexander Usyskin 
117d49ed64aSAlexander Usyskin /**
118b7d88514STomas Winkler  * mei_me_cl_add - add me client to the list
119b7d88514STomas Winkler  *
120b7d88514STomas Winkler  * @dev: mei device
121b7d88514STomas Winkler  * @me_cl: me client
122b7d88514STomas Winkler  */
123b7d88514STomas Winkler void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl)
124b7d88514STomas Winkler {
125b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
126b7d88514STomas Winkler 	list_add(&me_cl->list, &dev->me_clients);
127b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
128b7d88514STomas Winkler }
129b7d88514STomas Winkler 
130b7d88514STomas Winkler /**
131b7d88514STomas Winkler  * __mei_me_cl_by_uuid - locate me client by uuid
132b7d88514STomas Winkler  *	increases ref count
133b7d88514STomas Winkler  *
134b7d88514STomas Winkler  * @dev: mei device
135b7d88514STomas Winkler  * @uuid: me client uuid
136b7d88514STomas Winkler  *
137b7d88514STomas Winkler  * Return: me client or NULL if not found
138b7d88514STomas Winkler  *
139b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
140b7d88514STomas Winkler  */
141b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev,
142b7d88514STomas Winkler 					const uuid_le *uuid)
143b7d88514STomas Winkler {
144b7d88514STomas Winkler 	struct mei_me_client *me_cl;
145b7d88514STomas Winkler 	const uuid_le *pn;
146b7d88514STomas Winkler 
147b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
148b7d88514STomas Winkler 
149b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
150b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
151b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0)
152b7d88514STomas Winkler 			return mei_me_cl_get(me_cl);
153b7d88514STomas Winkler 	}
154b7d88514STomas Winkler 
155b7d88514STomas Winkler 	return NULL;
156b7d88514STomas Winkler }
157b7d88514STomas Winkler 
158b7d88514STomas Winkler /**
159a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid - locate me client by uuid
16079563db9STomas Winkler  *	increases ref count
16190e0b5f1STomas Winkler  *
16290e0b5f1STomas Winkler  * @dev: mei device
163a8605ea2SAlexander Usyskin  * @uuid: me client uuid
164a27a76d3SAlexander Usyskin  *
165a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
166b7d88514STomas Winkler  *
167b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
16890e0b5f1STomas Winkler  */
169b7d88514STomas Winkler struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev,
170d320832fSTomas Winkler 					const uuid_le *uuid)
17190e0b5f1STomas Winkler {
1725ca2d388STomas Winkler 	struct mei_me_client *me_cl;
17390e0b5f1STomas Winkler 
174b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
175b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
176b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
17790e0b5f1STomas Winkler 
178b7d88514STomas Winkler 	return me_cl;
17990e0b5f1STomas Winkler }
18090e0b5f1STomas Winkler 
18190e0b5f1STomas Winkler /**
182a8605ea2SAlexander Usyskin  * mei_me_cl_by_id - locate me client by client id
18379563db9STomas Winkler  *	increases ref count
18490e0b5f1STomas Winkler  *
18590e0b5f1STomas Winkler  * @dev: the device structure
18690e0b5f1STomas Winkler  * @client_id: me client id
18790e0b5f1STomas Winkler  *
188a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
189b7d88514STomas Winkler  *
190b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
19190e0b5f1STomas Winkler  */
192d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
19390e0b5f1STomas Winkler {
194a27a76d3SAlexander Usyskin 
195b7d88514STomas Winkler 	struct mei_me_client *__me_cl, *me_cl = NULL;
196a27a76d3SAlexander Usyskin 
197b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
198b7d88514STomas Winkler 	list_for_each_entry(__me_cl, &dev->me_clients, list) {
199b7d88514STomas Winkler 		if (__me_cl->client_id == client_id) {
200b7d88514STomas Winkler 			me_cl = mei_me_cl_get(__me_cl);
201b7d88514STomas Winkler 			break;
202b7d88514STomas Winkler 		}
203b7d88514STomas Winkler 	}
204b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
205b7d88514STomas Winkler 
206b7d88514STomas Winkler 	return me_cl;
207b7d88514STomas Winkler }
208b7d88514STomas Winkler 
209b7d88514STomas Winkler /**
210b7d88514STomas Winkler  * __mei_me_cl_by_uuid_id - locate me client by client id and uuid
211b7d88514STomas Winkler  *	increases ref count
212b7d88514STomas Winkler  *
213b7d88514STomas Winkler  * @dev: the device structure
214b7d88514STomas Winkler  * @uuid: me client uuid
215b7d88514STomas Winkler  * @client_id: me client id
216b7d88514STomas Winkler  *
217b7d88514STomas Winkler  * Return: me client or null if not found
218b7d88514STomas Winkler  *
219b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
220b7d88514STomas Winkler  */
221b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev,
222b7d88514STomas Winkler 					   const uuid_le *uuid, u8 client_id)
223b7d88514STomas Winkler {
224b7d88514STomas Winkler 	struct mei_me_client *me_cl;
225b7d88514STomas Winkler 	const uuid_le *pn;
226b7d88514STomas Winkler 
227b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
228b7d88514STomas Winkler 
229b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
230b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
231b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0 &&
232b7d88514STomas Winkler 		    me_cl->client_id == client_id)
23379563db9STomas Winkler 			return mei_me_cl_get(me_cl);
234b7d88514STomas Winkler 	}
23579563db9STomas Winkler 
236d320832fSTomas Winkler 	return NULL;
23790e0b5f1STomas Winkler }
2389ca9050bSTomas Winkler 
239b7d88514STomas Winkler 
240a8605ea2SAlexander Usyskin /**
241a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid_id - locate me client by client id and uuid
24279563db9STomas Winkler  *	increases ref count
243a8605ea2SAlexander Usyskin  *
244a8605ea2SAlexander Usyskin  * @dev: the device structure
245a8605ea2SAlexander Usyskin  * @uuid: me client uuid
246a8605ea2SAlexander Usyskin  * @client_id: me client id
247a8605ea2SAlexander Usyskin  *
248b7d88514STomas Winkler  * Return: me client or null if not found
249a8605ea2SAlexander Usyskin  */
250d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
251d880f329STomas Winkler 					   const uuid_le *uuid, u8 client_id)
252d880f329STomas Winkler {
253d880f329STomas Winkler 	struct mei_me_client *me_cl;
254d880f329STomas Winkler 
255b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
256b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id);
257b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
25879563db9STomas Winkler 
259b7d88514STomas Winkler 	return me_cl;
260d880f329STomas Winkler }
261d880f329STomas Winkler 
26225ca6472STomas Winkler /**
26379563db9STomas Winkler  * mei_me_cl_rm_by_uuid - remove all me clients matching uuid
26425ca6472STomas Winkler  *
26525ca6472STomas Winkler  * @dev: the device structure
26625ca6472STomas Winkler  * @uuid: me client uuid
26779563db9STomas Winkler  *
26879563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
26925ca6472STomas Winkler  */
27079563db9STomas Winkler void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
27125ca6472STomas Winkler {
272b7d88514STomas Winkler 	struct mei_me_client *me_cl;
27325ca6472STomas Winkler 
27479563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl\n", uuid);
275b7d88514STomas Winkler 
276b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
277b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
278b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
279b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
28079563db9STomas Winkler }
28179563db9STomas Winkler 
28279563db9STomas Winkler /**
28379563db9STomas Winkler  * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id
28479563db9STomas Winkler  *
28579563db9STomas Winkler  * @dev: the device structure
28679563db9STomas Winkler  * @uuid: me client uuid
28779563db9STomas Winkler  * @id: me client id
28879563db9STomas Winkler  *
28979563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
29079563db9STomas Winkler  */
29179563db9STomas Winkler void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
29279563db9STomas Winkler {
293b7d88514STomas Winkler 	struct mei_me_client *me_cl;
29479563db9STomas Winkler 
29579563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
296b7d88514STomas Winkler 
297b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
298b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id);
299b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
300b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
30125ca6472STomas Winkler }
30279563db9STomas Winkler 
30379563db9STomas Winkler /**
30479563db9STomas Winkler  * mei_me_cl_rm_all - remove all me clients
30579563db9STomas Winkler  *
30679563db9STomas Winkler  * @dev: the device structure
30779563db9STomas Winkler  *
30879563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
30979563db9STomas Winkler  */
31079563db9STomas Winkler void mei_me_cl_rm_all(struct mei_device *dev)
31179563db9STomas Winkler {
31279563db9STomas Winkler 	struct mei_me_client *me_cl, *next;
31379563db9STomas Winkler 
314b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
31579563db9STomas Winkler 	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
316b7d88514STomas Winkler 		__mei_me_cl_del(dev, me_cl);
317b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
31825ca6472STomas Winkler }
31925ca6472STomas Winkler 
3209ca9050bSTomas Winkler /**
321cc99ecfdSTomas Winkler  * mei_cl_cmp_id - tells if the clients are the same
322cc99ecfdSTomas Winkler  *
323cc99ecfdSTomas Winkler  * @cl1: host client 1
324cc99ecfdSTomas Winkler  * @cl2: host client 2
325cc99ecfdSTomas Winkler  *
326a8605ea2SAlexander Usyskin  * Return: true  - if the clients has same host and me ids
327cc99ecfdSTomas Winkler  *         false - otherwise
328cc99ecfdSTomas Winkler  */
329cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
330cc99ecfdSTomas Winkler 				const struct mei_cl *cl2)
331cc99ecfdSTomas Winkler {
332cc99ecfdSTomas Winkler 	return cl1 && cl2 &&
333cc99ecfdSTomas Winkler 		(cl1->host_client_id == cl2->host_client_id) &&
334d49ed64aSAlexander Usyskin 		(mei_cl_me_id(cl1) == mei_cl_me_id(cl2));
335cc99ecfdSTomas Winkler }
336cc99ecfdSTomas Winkler 
337cc99ecfdSTomas Winkler /**
3389ca9050bSTomas Winkler  * mei_io_cb_free - free mei_cb_private related memory
3399ca9050bSTomas Winkler  *
3409ca9050bSTomas Winkler  * @cb: mei callback struct
3419ca9050bSTomas Winkler  */
3429ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb)
3439ca9050bSTomas Winkler {
3449ca9050bSTomas Winkler 	if (cb == NULL)
3459ca9050bSTomas Winkler 		return;
3469ca9050bSTomas Winkler 
347928fa666STomas Winkler 	list_del(&cb->list);
3485db7514dSTomas Winkler 	kfree(cb->buf.data);
3499ca9050bSTomas Winkler 	kfree(cb);
3509ca9050bSTomas Winkler }
3519ca9050bSTomas Winkler 
3529ca9050bSTomas Winkler /**
3539ca9050bSTomas Winkler  * mei_io_cb_init - allocate and initialize io callback
3549ca9050bSTomas Winkler  *
355a8605ea2SAlexander Usyskin  * @cl: mei client
356bca67d68STomas Winkler  * @type: operation type
357393b148fSMasanari Iida  * @fp: pointer to file structure
3589ca9050bSTomas Winkler  *
359a8605ea2SAlexander Usyskin  * Return: mei_cl_cb pointer or NULL;
3609ca9050bSTomas Winkler  */
361bca67d68STomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
362bca67d68STomas Winkler 				 struct file *fp)
3639ca9050bSTomas Winkler {
3649ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
3659ca9050bSTomas Winkler 
3669ca9050bSTomas Winkler 	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
3679ca9050bSTomas Winkler 	if (!cb)
3689ca9050bSTomas Winkler 		return NULL;
3699ca9050bSTomas Winkler 
370928fa666STomas Winkler 	INIT_LIST_HEAD(&cb->list);
3719ca9050bSTomas Winkler 	cb->file_object = fp;
3729ca9050bSTomas Winkler 	cb->cl = cl;
3739ca9050bSTomas Winkler 	cb->buf_idx = 0;
374bca67d68STomas Winkler 	cb->fop_type = type;
3759ca9050bSTomas Winkler 	return cb;
3769ca9050bSTomas Winkler }
3779ca9050bSTomas Winkler 
3789ca9050bSTomas Winkler /**
379928fa666STomas Winkler  * __mei_io_list_flush - removes and frees cbs belonging to cl.
380928fa666STomas Winkler  *
381928fa666STomas Winkler  * @list:  an instance of our list structure
382928fa666STomas Winkler  * @cl:    host client, can be NULL for flushing the whole list
383928fa666STomas Winkler  * @free:  whether to free the cbs
384928fa666STomas Winkler  */
385928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list,
386928fa666STomas Winkler 				struct mei_cl *cl, bool free)
387928fa666STomas Winkler {
388928fa666STomas Winkler 	struct mei_cl_cb *cb, *next;
389928fa666STomas Winkler 
390928fa666STomas Winkler 	/* enable removing everything if no cl is specified */
391928fa666STomas Winkler 	list_for_each_entry_safe(cb, next, &list->list, list) {
392928fa666STomas Winkler 		if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
393928fa666STomas Winkler 			list_del_init(&cb->list);
394928fa666STomas Winkler 			if (free)
395928fa666STomas Winkler 				mei_io_cb_free(cb);
396928fa666STomas Winkler 		}
397928fa666STomas Winkler 	}
398928fa666STomas Winkler }
399928fa666STomas Winkler 
400928fa666STomas Winkler /**
401928fa666STomas Winkler  * mei_io_list_flush - removes list entry belonging to cl.
402928fa666STomas Winkler  *
403928fa666STomas Winkler  * @list:  An instance of our list structure
404928fa666STomas Winkler  * @cl: host client
405928fa666STomas Winkler  */
406928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
407928fa666STomas Winkler {
408928fa666STomas Winkler 	__mei_io_list_flush(list, cl, false);
409928fa666STomas Winkler }
410928fa666STomas Winkler 
411928fa666STomas Winkler /**
412928fa666STomas Winkler  * mei_io_list_free - removes cb belonging to cl and free them
413928fa666STomas Winkler  *
414928fa666STomas Winkler  * @list:  An instance of our list structure
415928fa666STomas Winkler  * @cl: host client
416928fa666STomas Winkler  */
417928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
418928fa666STomas Winkler {
419928fa666STomas Winkler 	__mei_io_list_flush(list, cl, true);
420928fa666STomas Winkler }
421928fa666STomas Winkler 
422928fa666STomas Winkler /**
4235db7514dSTomas Winkler  * mei_io_cb_alloc_buf - allocate callback buffer
4249ca9050bSTomas Winkler  *
425393b148fSMasanari Iida  * @cb: io callback structure
426393b148fSMasanari Iida  * @length: size of the buffer
4279ca9050bSTomas Winkler  *
428a8605ea2SAlexander Usyskin  * Return: 0 on success
4299ca9050bSTomas Winkler  *         -EINVAL if cb is NULL
4309ca9050bSTomas Winkler  *         -ENOMEM if allocation failed
4319ca9050bSTomas Winkler  */
4325db7514dSTomas Winkler int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
4339ca9050bSTomas Winkler {
4349ca9050bSTomas Winkler 	if (!cb)
4359ca9050bSTomas Winkler 		return -EINVAL;
4369ca9050bSTomas Winkler 
4379ca9050bSTomas Winkler 	if (length == 0)
4389ca9050bSTomas Winkler 		return 0;
4399ca9050bSTomas Winkler 
4405db7514dSTomas Winkler 	cb->buf.data = kmalloc(length, GFP_KERNEL);
4415db7514dSTomas Winkler 	if (!cb->buf.data)
4429ca9050bSTomas Winkler 		return -ENOMEM;
4435db7514dSTomas Winkler 	cb->buf.size = length;
4449ca9050bSTomas Winkler 	return 0;
4459ca9050bSTomas Winkler }
4469ca9050bSTomas Winkler 
4479ca9050bSTomas Winkler /**
448bca67d68STomas Winkler  * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
449bca67d68STomas Winkler  *
450bca67d68STomas Winkler  * @cl: host client
451bca67d68STomas Winkler  * @length: size of the buffer
452bca67d68STomas Winkler  * @type: operation type
453bca67d68STomas Winkler  * @fp: associated file pointer (might be NULL)
454bca67d68STomas Winkler  *
455bca67d68STomas Winkler  * Return: cb on success and NULL on failure
456bca67d68STomas Winkler  */
457bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
458bca67d68STomas Winkler 				  enum mei_cb_file_ops type, struct file *fp)
459bca67d68STomas Winkler {
460bca67d68STomas Winkler 	struct mei_cl_cb *cb;
461bca67d68STomas Winkler 
462bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, type, fp);
463bca67d68STomas Winkler 	if (!cb)
464bca67d68STomas Winkler 		return NULL;
465bca67d68STomas Winkler 
466bca67d68STomas Winkler 	if (mei_io_cb_alloc_buf(cb, length)) {
467bca67d68STomas Winkler 		mei_io_cb_free(cb);
468bca67d68STomas Winkler 		return NULL;
469bca67d68STomas Winkler 	}
470bca67d68STomas Winkler 
471bca67d68STomas Winkler 	return cb;
472bca67d68STomas Winkler }
473bca67d68STomas Winkler 
474bca67d68STomas Winkler /**
475a9bed610STomas Winkler  * mei_cl_read_cb - find this cl's callback in the read list
476a9bed610STomas Winkler  *     for a specific file
477a9bed610STomas Winkler  *
478a9bed610STomas Winkler  * @cl: host client
479a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
480a9bed610STomas Winkler  *
481a9bed610STomas Winkler  * Return: cb on success, NULL if cb is not found
482a9bed610STomas Winkler  */
483a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
484a9bed610STomas Winkler {
485a9bed610STomas Winkler 	struct mei_cl_cb *cb;
486a9bed610STomas Winkler 
487a9bed610STomas Winkler 	list_for_each_entry(cb, &cl->rd_completed, list)
488a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
489a9bed610STomas Winkler 			return cb;
490a9bed610STomas Winkler 
491a9bed610STomas Winkler 	return NULL;
492a9bed610STomas Winkler }
493a9bed610STomas Winkler 
494a9bed610STomas Winkler /**
495a9bed610STomas Winkler  * mei_cl_read_cb_flush - free client's read pending and completed cbs
496a9bed610STomas Winkler  *   for a specific file
497a9bed610STomas Winkler  *
498a9bed610STomas Winkler  * @cl: host client
499a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
500a9bed610STomas Winkler  */
501a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
502a9bed610STomas Winkler {
503a9bed610STomas Winkler 	struct mei_cl_cb *cb, *next;
504a9bed610STomas Winkler 
505a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
506a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
507a9bed610STomas Winkler 			mei_io_cb_free(cb);
508a9bed610STomas Winkler 
509a9bed610STomas Winkler 
510a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
511a9bed610STomas Winkler 		if (!fp || fp == cb->file_object)
512a9bed610STomas Winkler 			mei_io_cb_free(cb);
513a9bed610STomas Winkler }
514a9bed610STomas Winkler 
515a9bed610STomas Winkler /**
5169ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
5179ca9050bSTomas Winkler  *
5189ca9050bSTomas Winkler  * @cl: host client
519a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
520ce23139cSAlexander Usyskin  *
521ce23139cSAlexander Usyskin  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
5229ca9050bSTomas Winkler  */
523a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
5249ca9050bSTomas Winkler {
525c0abffbdSAlexander Usyskin 	struct mei_device *dev;
526c0abffbdSAlexander Usyskin 
52790e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5289ca9050bSTomas Winkler 		return -EINVAL;
5299ca9050bSTomas Winkler 
530c0abffbdSAlexander Usyskin 	dev = cl->dev;
531c0abffbdSAlexander Usyskin 
532c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
533cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_list, cl);
534cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_waiting_list, cl);
5359ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
5369ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
5379ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
5389ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
539a9bed610STomas Winkler 
540a9bed610STomas Winkler 	mei_cl_read_cb_flush(cl, fp);
541a9bed610STomas Winkler 
5429ca9050bSTomas Winkler 	return 0;
5439ca9050bSTomas Winkler }
5449ca9050bSTomas Winkler 
5459ca9050bSTomas Winkler 
5469ca9050bSTomas Winkler /**
54783ce0741SAlexander Usyskin  * mei_cl_init - initializes cl.
5489ca9050bSTomas Winkler  *
5499ca9050bSTomas Winkler  * @cl: host client to be initialized
5509ca9050bSTomas Winkler  * @dev: mei device
5519ca9050bSTomas Winkler  */
5529ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
5539ca9050bSTomas Winkler {
5549ca9050bSTomas Winkler 	memset(cl, 0, sizeof(struct mei_cl));
5559ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
5569ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
5579ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
558b38a362fSTomas Winkler 	init_waitqueue_head(&cl->ev_wait);
559a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_completed);
560a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_pending);
5619ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
5629ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
5633c666182STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
5649ca9050bSTomas Winkler 	cl->dev = dev;
5659ca9050bSTomas Winkler }
5669ca9050bSTomas Winkler 
5679ca9050bSTomas Winkler /**
5689ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
5699ca9050bSTomas Winkler  *
5709ca9050bSTomas Winkler  * @dev: mei device
571a8605ea2SAlexander Usyskin  * Return:  The allocated file or NULL on failure
5729ca9050bSTomas Winkler  */
5739ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
5749ca9050bSTomas Winkler {
5759ca9050bSTomas Winkler 	struct mei_cl *cl;
5769ca9050bSTomas Winkler 
5779ca9050bSTomas Winkler 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
5789ca9050bSTomas Winkler 	if (!cl)
5799ca9050bSTomas Winkler 		return NULL;
5809ca9050bSTomas Winkler 
5819ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
5829ca9050bSTomas Winkler 
5839ca9050bSTomas Winkler 	return cl;
5849ca9050bSTomas Winkler }
5859ca9050bSTomas Winkler 
58690e0b5f1STomas Winkler /**
5873908be6fSAlexander Usyskin  * mei_cl_link - allocate host id in the host map
5889ca9050bSTomas Winkler  *
5893908be6fSAlexander Usyskin  * @cl: host client
59003b8d341STomas Winkler  * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
591393b148fSMasanari Iida  *
592a8605ea2SAlexander Usyskin  * Return: 0 on success
5939ca9050bSTomas Winkler  *	-EINVAL on incorrect values
59403b8d341STomas Winkler  *	-EMFILE if open count exceeded.
5959ca9050bSTomas Winkler  */
596781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id)
5979ca9050bSTomas Winkler {
59890e0b5f1STomas Winkler 	struct mei_device *dev;
59922f96a0eSTomas Winkler 	long open_handle_count;
6009ca9050bSTomas Winkler 
601781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
6029ca9050bSTomas Winkler 		return -EINVAL;
6039ca9050bSTomas Winkler 
60490e0b5f1STomas Winkler 	dev = cl->dev;
60590e0b5f1STomas Winkler 
60683ce0741SAlexander Usyskin 	/* If Id is not assigned get one*/
607781d0d89STomas Winkler 	if (id == MEI_HOST_CLIENT_ID_ANY)
608781d0d89STomas Winkler 		id = find_first_zero_bit(dev->host_clients_map,
609781d0d89STomas Winkler 					MEI_CLIENTS_MAX);
6109ca9050bSTomas Winkler 
611781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
6122bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
613e036cc57STomas Winkler 		return -EMFILE;
614e036cc57STomas Winkler 	}
615e036cc57STomas Winkler 
61622f96a0eSTomas Winkler 	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
61722f96a0eSTomas Winkler 	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6182bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
619e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
620e036cc57STomas Winkler 		return -EMFILE;
6219ca9050bSTomas Winkler 	}
622781d0d89STomas Winkler 
623781d0d89STomas Winkler 	dev->open_handle_count++;
624781d0d89STomas Winkler 
625781d0d89STomas Winkler 	cl->host_client_id = id;
626781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
627781d0d89STomas Winkler 
628781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
629781d0d89STomas Winkler 
630781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
631781d0d89STomas Winkler 
632c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
633781d0d89STomas Winkler 	return 0;
634781d0d89STomas Winkler }
635781d0d89STomas Winkler 
6369ca9050bSTomas Winkler /**
637d49ed64aSAlexander Usyskin  * mei_cl_unlink - remove host client from the list
6389ca9050bSTomas Winkler  *
639393b148fSMasanari Iida  * @cl: host client
640ce23139cSAlexander Usyskin  *
641ce23139cSAlexander Usyskin  * Return: always 0
6429ca9050bSTomas Winkler  */
64390e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6449ca9050bSTomas Winkler {
64590e0b5f1STomas Winkler 	struct mei_device *dev;
64690e0b5f1STomas Winkler 
647781d0d89STomas Winkler 	/* don't shout on error exit path */
648781d0d89STomas Winkler 	if (!cl)
649781d0d89STomas Winkler 		return 0;
650781d0d89STomas Winkler 
6518e9a4a9aSTomas Winkler 	/* wd and amthif might not be initialized */
6528e9a4a9aSTomas Winkler 	if (!cl->dev)
6538e9a4a9aSTomas Winkler 		return 0;
65490e0b5f1STomas Winkler 
65590e0b5f1STomas Winkler 	dev = cl->dev;
65690e0b5f1STomas Winkler 
657a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
658a14c44d8STomas Winkler 
65922f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
66022f96a0eSTomas Winkler 		dev->open_handle_count--;
66122f96a0eSTomas Winkler 
66222f96a0eSTomas Winkler 	/* never clear the 0 bit */
66322f96a0eSTomas Winkler 	if (cl->host_client_id)
66422f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
66522f96a0eSTomas Winkler 
66622f96a0eSTomas Winkler 	list_del_init(&cl->link);
66722f96a0eSTomas Winkler 
66822f96a0eSTomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
66922f96a0eSTomas Winkler 
67090e0b5f1STomas Winkler 	return 0;
6719ca9050bSTomas Winkler }
6729ca9050bSTomas Winkler 
6739ca9050bSTomas Winkler 
6749ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work)
6759ca9050bSTomas Winkler {
676b7d88514STomas Winkler 	struct mei_device *dev =
677b7d88514STomas Winkler 		container_of(work, struct mei_device, init_work);
6785ca2d388STomas Winkler 	struct mei_me_client *me_cl;
6799ca9050bSTomas Winkler 
6809ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
6819ca9050bSTomas Winkler 
6829ca9050bSTomas Winkler 
683b7d88514STomas Winkler 	me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
684b7d88514STomas Winkler 	if (me_cl)
685d49ed64aSAlexander Usyskin 		mei_amthif_host_init(dev, me_cl);
686b43baf69STomas Winkler 	mei_me_cl_put(me_cl);
687b7d88514STomas Winkler 
688b7d88514STomas Winkler 	me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
689b7d88514STomas Winkler 	if (me_cl)
690d49ed64aSAlexander Usyskin 		mei_wd_host_init(dev, me_cl);
691b43baf69STomas Winkler 	mei_me_cl_put(me_cl);
692b7d88514STomas Winkler 
6939ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
6946adb8efbSTomas Winkler 	dev->reset_count = 0;
6959ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
69604bb139aSTomas Winkler 
6976009595aSTomas Winkler 	mei_cl_bus_rescan(dev);
6986009595aSTomas Winkler 
6992bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
7002bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
7012bf94cabSTomas Winkler 	pm_runtime_autosuspend(dev->dev);
7029ca9050bSTomas Winkler }
7039ca9050bSTomas Winkler 
7046aae48ffSTomas Winkler /**
705a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
7066aae48ffSTomas Winkler  *
7076aae48ffSTomas Winkler  * @dev: the device structure
708a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
7096aae48ffSTomas Winkler  */
7106aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
7116aae48ffSTomas Winkler {
71204bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
7133dc196eaSAlexander Usyskin 	    mei_pg_in_transition(dev)) {
7142bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
71504bb139aSTomas Winkler 		return false;
71604bb139aSTomas Winkler 	}
71704bb139aSTomas Winkler 
7186aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
7192bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
7206aae48ffSTomas Winkler 		return false;
7216aae48ffSTomas Winkler 	}
7226aae48ffSTomas Winkler 
7236aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7246aae48ffSTomas Winkler 
7256aae48ffSTomas Winkler 	return true;
7266aae48ffSTomas Winkler }
7279ca9050bSTomas Winkler 
7289ca9050bSTomas Winkler /**
7293c666182STomas Winkler  * mei_cl_set_disconnected - set disconnected state and clear
7303c666182STomas Winkler  *   associated states and resources
7313c666182STomas Winkler  *
7323c666182STomas Winkler  * @cl: host client
7333c666182STomas Winkler  */
7343c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl)
7353c666182STomas Winkler {
7363c666182STomas Winkler 	struct mei_device *dev = cl->dev;
7373c666182STomas Winkler 
7383c666182STomas Winkler 	if (cl->state == MEI_FILE_DISCONNECTED ||
7393c666182STomas Winkler 	    cl->state == MEI_FILE_INITIALIZING)
7403c666182STomas Winkler 		return;
7413c666182STomas Winkler 
7423c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTED;
7433c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7443c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
7453c666182STomas Winkler 	cl->mei_flow_ctrl_creds = 0;
7463c666182STomas Winkler 	cl->timer_count = 0;
747d49ed64aSAlexander Usyskin 
748a03d77f6SAlexander Usyskin 	if (!cl->me_cl)
749a03d77f6SAlexander Usyskin 		return;
750a03d77f6SAlexander Usyskin 
751a03d77f6SAlexander Usyskin 	if (!WARN_ON(cl->me_cl->connect_count == 0))
752a03d77f6SAlexander Usyskin 		cl->me_cl->connect_count--;
753a03d77f6SAlexander Usyskin 
754c241e9b1SAlexander Usyskin 	if (cl->me_cl->connect_count == 0)
755c241e9b1SAlexander Usyskin 		cl->me_cl->mei_flow_ctrl_creds = 0;
756c241e9b1SAlexander Usyskin 
757d49ed64aSAlexander Usyskin 	mei_me_cl_put(cl->me_cl);
758d49ed64aSAlexander Usyskin 	cl->me_cl = NULL;
7593c666182STomas Winkler }
7603c666182STomas Winkler 
761a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
762a03d77f6SAlexander Usyskin {
7631df629efSAlexander Usyskin 	if (!mei_me_cl_get(me_cl))
764a03d77f6SAlexander Usyskin 		return -ENOENT;
765a03d77f6SAlexander Usyskin 
7661df629efSAlexander Usyskin 	/* only one connection is allowed for fixed address clients */
7671df629efSAlexander Usyskin 	if (me_cl->props.fixed_address) {
7681df629efSAlexander Usyskin 		if (me_cl->connect_count) {
7691df629efSAlexander Usyskin 			mei_me_cl_put(me_cl);
7701df629efSAlexander Usyskin 			return -EBUSY;
7711df629efSAlexander Usyskin 		}
7721df629efSAlexander Usyskin 	}
7731df629efSAlexander Usyskin 
7741df629efSAlexander Usyskin 	cl->me_cl = me_cl;
775a03d77f6SAlexander Usyskin 	cl->state = MEI_FILE_CONNECTING;
776a03d77f6SAlexander Usyskin 	cl->me_cl->connect_count++;
777a03d77f6SAlexander Usyskin 
778a03d77f6SAlexander Usyskin 	return 0;
779a03d77f6SAlexander Usyskin }
780a03d77f6SAlexander Usyskin 
7813c666182STomas Winkler /*
7823c666182STomas Winkler  * mei_cl_send_disconnect - send disconnect request
7833c666182STomas Winkler  *
7843c666182STomas Winkler  * @cl: host client
7853c666182STomas Winkler  * @cb: callback block
7863c666182STomas Winkler  *
7873c666182STomas Winkler  * Return: 0, OK; otherwise, error.
7883c666182STomas Winkler  */
7893c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
7903c666182STomas Winkler {
7913c666182STomas Winkler 	struct mei_device *dev;
7923c666182STomas Winkler 	int ret;
7933c666182STomas Winkler 
7943c666182STomas Winkler 	dev = cl->dev;
7953c666182STomas Winkler 
7963c666182STomas Winkler 	ret = mei_hbm_cl_disconnect_req(dev, cl);
7973c666182STomas Winkler 	cl->status = ret;
7983c666182STomas Winkler 	if (ret) {
7993c666182STomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
8003c666182STomas Winkler 		return ret;
8013c666182STomas Winkler 	}
8023c666182STomas Winkler 
8033c666182STomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
8043c666182STomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
8053c666182STomas Winkler 
8063c666182STomas Winkler 	return 0;
8073c666182STomas Winkler }
8083c666182STomas Winkler 
8093c666182STomas Winkler /**
8103c666182STomas Winkler  * mei_cl_irq_disconnect - processes close related operation from
8113c666182STomas Winkler  *	interrupt thread context - send disconnect request
8123c666182STomas Winkler  *
8133c666182STomas Winkler  * @cl: client
8143c666182STomas Winkler  * @cb: callback block.
8153c666182STomas Winkler  * @cmpl_list: complete list.
8163c666182STomas Winkler  *
8173c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8183c666182STomas Winkler  */
8193c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
8203c666182STomas Winkler 			    struct mei_cl_cb *cmpl_list)
8213c666182STomas Winkler {
8223c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8233c666182STomas Winkler 	u32 msg_slots;
8243c666182STomas Winkler 	int slots;
8253c666182STomas Winkler 	int ret;
8263c666182STomas Winkler 
8273c666182STomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
8283c666182STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
8293c666182STomas Winkler 
8303c666182STomas Winkler 	if (slots < msg_slots)
8313c666182STomas Winkler 		return -EMSGSIZE;
8323c666182STomas Winkler 
8333c666182STomas Winkler 	ret = mei_cl_send_disconnect(cl, cb);
8343c666182STomas Winkler 	if (ret)
8353c666182STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
8363c666182STomas Winkler 
8373c666182STomas Winkler 	return ret;
8383c666182STomas Winkler }
8393c666182STomas Winkler 
8403c666182STomas Winkler /**
84118901357SAlexander Usyskin  * __mei_cl_disconnect - disconnect host client from the me one
84218901357SAlexander Usyskin  *     internal function runtime pm has to be already acquired
8439ca9050bSTomas Winkler  *
84490e0b5f1STomas Winkler  * @cl: host client
8459ca9050bSTomas Winkler  *
846a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
8479ca9050bSTomas Winkler  */
84818901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl)
8499ca9050bSTomas Winkler {
85090e0b5f1STomas Winkler 	struct mei_device *dev;
8519ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
852fe2f17ebSAlexander Usyskin 	int rets;
8539ca9050bSTomas Winkler 
85490e0b5f1STomas Winkler 	dev = cl->dev;
85590e0b5f1STomas Winkler 
8563c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTING;
8573c666182STomas Winkler 
858bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
859bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
860bca67d68STomas Winkler 	if (rets)
8613c666182STomas Winkler 		goto out;
8625a8373fbSTomas Winkler 
863c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "add disconnect cb to control write list\n");
8649ca9050bSTomas Winkler 	list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
8659ca9050bSTomas Winkler 
8663c666182STomas Winkler 	if (mei_hbuf_acquire(dev)) {
8673c666182STomas Winkler 		rets = mei_cl_send_disconnect(cl, cb);
8683c666182STomas Winkler 		if (rets) {
8693c666182STomas Winkler 			cl_err(dev, cl, "failed to disconnect.\n");
8703c666182STomas Winkler 			goto out;
8719ca9050bSTomas Winkler 		}
8723c666182STomas Winkler 	}
8733c666182STomas Winkler 
8749ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
8753c666182STomas Winkler 	wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY,
8769ca9050bSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
8779ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
878fe2f17ebSAlexander Usyskin 
8793c666182STomas Winkler 	rets = cl->status;
8803c666182STomas Winkler 	if (cl->state != MEI_FILE_DISCONNECT_REPLY) {
881fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
882fe2f17ebSAlexander Usyskin 		rets = -ETIME;
8839ca9050bSTomas Winkler 	}
8849ca9050bSTomas Winkler 
8853c666182STomas Winkler out:
8863c666182STomas Winkler 	/* we disconnect also on error */
8873c666182STomas Winkler 	mei_cl_set_disconnected(cl);
8883c666182STomas Winkler 	if (!rets)
8893c666182STomas Winkler 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
8903c666182STomas Winkler 
89118901357SAlexander Usyskin 	mei_io_cb_free(cb);
89218901357SAlexander Usyskin 	return rets;
89318901357SAlexander Usyskin }
89418901357SAlexander Usyskin 
89518901357SAlexander Usyskin /**
89618901357SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
89718901357SAlexander Usyskin  *
89818901357SAlexander Usyskin  * @cl: host client
89918901357SAlexander Usyskin  *
90018901357SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
90118901357SAlexander Usyskin  *
90218901357SAlexander Usyskin  * Return: 0 on success, <0 on failure.
90318901357SAlexander Usyskin  */
90418901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl)
90518901357SAlexander Usyskin {
90618901357SAlexander Usyskin 	struct mei_device *dev;
90718901357SAlexander Usyskin 	int rets;
90818901357SAlexander Usyskin 
90918901357SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
91018901357SAlexander Usyskin 		return -ENODEV;
91118901357SAlexander Usyskin 
91218901357SAlexander Usyskin 	dev = cl->dev;
91318901357SAlexander Usyskin 
91418901357SAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
91518901357SAlexander Usyskin 
91618901357SAlexander Usyskin 	if (!mei_cl_is_connected(cl))
91718901357SAlexander Usyskin 		return 0;
91818901357SAlexander Usyskin 
91918901357SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
92018901357SAlexander Usyskin 		mei_cl_set_disconnected(cl);
92118901357SAlexander Usyskin 		return 0;
92218901357SAlexander Usyskin 	}
92318901357SAlexander Usyskin 
92418901357SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
92518901357SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
92618901357SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
92718901357SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
92818901357SAlexander Usyskin 		return rets;
92918901357SAlexander Usyskin 	}
93018901357SAlexander Usyskin 
93118901357SAlexander Usyskin 	rets = __mei_cl_disconnect(cl);
93218901357SAlexander Usyskin 
93304bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
9342bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
9352bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
93604bb139aSTomas Winkler 
9379ca9050bSTomas Winkler 	return rets;
9389ca9050bSTomas Winkler }
9399ca9050bSTomas Winkler 
9409ca9050bSTomas Winkler 
9419ca9050bSTomas Winkler /**
94290e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
94390e0b5f1STomas Winkler  *    client with the same me client id is connecting
9449ca9050bSTomas Winkler  *
9459ca9050bSTomas Winkler  * @cl: private data of the file object
9469ca9050bSTomas Winkler  *
947a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
9489ca9050bSTomas Winkler  */
9490c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl)
9509ca9050bSTomas Winkler {
95190e0b5f1STomas Winkler 	struct mei_device *dev;
9520c53357cSTomas Winkler 	struct mei_cl_cb *cb;
95390e0b5f1STomas Winkler 
95490e0b5f1STomas Winkler 	dev = cl->dev;
95590e0b5f1STomas Winkler 
9560c53357cSTomas Winkler 	list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
9570c53357cSTomas Winkler 		if (cb->fop_type == MEI_FOP_CONNECT &&
958d49ed64aSAlexander Usyskin 		    mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
95990e0b5f1STomas Winkler 			return true;
9609ca9050bSTomas Winkler 	}
96190e0b5f1STomas Winkler 
96290e0b5f1STomas Winkler 	return false;
9639ca9050bSTomas Winkler }
9649ca9050bSTomas Winkler 
9659ca9050bSTomas Winkler /**
9660c53357cSTomas Winkler  * mei_cl_send_connect - send connect request
9670c53357cSTomas Winkler  *
9680c53357cSTomas Winkler  * @cl: host client
9690c53357cSTomas Winkler  * @cb: callback block
9700c53357cSTomas Winkler  *
9710c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
9720c53357cSTomas Winkler  */
9730c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
9740c53357cSTomas Winkler {
9750c53357cSTomas Winkler 	struct mei_device *dev;
9760c53357cSTomas Winkler 	int ret;
9770c53357cSTomas Winkler 
9780c53357cSTomas Winkler 	dev = cl->dev;
9790c53357cSTomas Winkler 
9800c53357cSTomas Winkler 	ret = mei_hbm_cl_connect_req(dev, cl);
9810c53357cSTomas Winkler 	cl->status = ret;
9820c53357cSTomas Winkler 	if (ret) {
9830c53357cSTomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
9840c53357cSTomas Winkler 		return ret;
9850c53357cSTomas Winkler 	}
9860c53357cSTomas Winkler 
9870c53357cSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
9880c53357cSTomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
9890c53357cSTomas Winkler 	return 0;
9900c53357cSTomas Winkler }
9910c53357cSTomas Winkler 
9920c53357cSTomas Winkler /**
9930c53357cSTomas Winkler  * mei_cl_irq_connect - send connect request in irq_thread context
9940c53357cSTomas Winkler  *
9950c53357cSTomas Winkler  * @cl: host client
9960c53357cSTomas Winkler  * @cb: callback block
9970c53357cSTomas Winkler  * @cmpl_list: complete list
9980c53357cSTomas Winkler  *
9990c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10000c53357cSTomas Winkler  */
10010c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
10020c53357cSTomas Winkler 			      struct mei_cl_cb *cmpl_list)
10030c53357cSTomas Winkler {
10040c53357cSTomas Winkler 	struct mei_device *dev = cl->dev;
10050c53357cSTomas Winkler 	u32 msg_slots;
10060c53357cSTomas Winkler 	int slots;
10070c53357cSTomas Winkler 	int rets;
10080c53357cSTomas Winkler 
10090c53357cSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
10100c53357cSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
10110c53357cSTomas Winkler 
10120c53357cSTomas Winkler 	if (mei_cl_is_other_connecting(cl))
10130c53357cSTomas Winkler 		return 0;
10140c53357cSTomas Winkler 
10150c53357cSTomas Winkler 	if (slots < msg_slots)
10160c53357cSTomas Winkler 		return -EMSGSIZE;
10170c53357cSTomas Winkler 
10180c53357cSTomas Winkler 	rets = mei_cl_send_connect(cl, cb);
10190c53357cSTomas Winkler 	if (rets)
10200c53357cSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
10210c53357cSTomas Winkler 
10220c53357cSTomas Winkler 	return rets;
10230c53357cSTomas Winkler }
10240c53357cSTomas Winkler 
10250c53357cSTomas Winkler /**
102683ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
10279f81abdaSTomas Winkler  *
10289f81abdaSTomas Winkler  * @cl: host client
1029d49ed64aSAlexander Usyskin  * @me_cl: me client
1030a8605ea2SAlexander Usyskin  * @file: pointer to file structure
10319f81abdaSTomas Winkler  *
10329f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
10339f81abdaSTomas Winkler  *
1034a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
10359f81abdaSTomas Winkler  */
1036d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
1037d49ed64aSAlexander Usyskin 		   struct file *file)
10389f81abdaSTomas Winkler {
10399f81abdaSTomas Winkler 	struct mei_device *dev;
10409f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
10419f81abdaSTomas Winkler 	int rets;
10429f81abdaSTomas Winkler 
10431df629efSAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev || !me_cl))
10449f81abdaSTomas Winkler 		return -ENODEV;
10459f81abdaSTomas Winkler 
10469f81abdaSTomas Winkler 	dev = cl->dev;
10479f81abdaSTomas Winkler 
10481df629efSAlexander Usyskin 	rets = mei_cl_set_connecting(cl, me_cl);
10491df629efSAlexander Usyskin 	if (rets)
10501df629efSAlexander Usyskin 		return rets;
10511df629efSAlexander Usyskin 
10521df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
10531df629efSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTED;
10541df629efSAlexander Usyskin 		return 0;
10551df629efSAlexander Usyskin 	}
10561df629efSAlexander Usyskin 
10572bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
105804bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
10592bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
106004bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
10611df629efSAlexander Usyskin 		goto nortpm;
106204bb139aSTomas Winkler 	}
106304bb139aSTomas Winkler 
1064bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
1065bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
1066bca67d68STomas Winkler 	if (rets)
10679f81abdaSTomas Winkler 		goto out;
10689f81abdaSTomas Winkler 
10690c53357cSTomas Winkler 	list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
10700c53357cSTomas Winkler 
10716aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
10726aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
10730c53357cSTomas Winkler 		rets = mei_cl_send_connect(cl, cb);
10740c53357cSTomas Winkler 		if (rets)
10759f81abdaSTomas Winkler 			goto out;
10769f81abdaSTomas Winkler 	}
10779f81abdaSTomas Winkler 
10789f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
107912f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
10809f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
108118901357SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
10823c666182STomas Winkler 			 cl->state == MEI_FILE_DISCONNECT_REPLY),
1083206ecfc2SFrode Isaksen 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
10849f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
10859f81abdaSTomas Winkler 
1086f3de9b63STomas Winkler 	if (!mei_cl_is_connected(cl)) {
108718901357SAlexander Usyskin 		if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
108818901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_rd_list, cl);
108918901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_wr_list, cl);
109018901357SAlexander Usyskin 			 /* ignore disconnect return valuue;
109118901357SAlexander Usyskin 			  * in case of failure reset will be invoked
109218901357SAlexander Usyskin 			  */
109318901357SAlexander Usyskin 			__mei_cl_disconnect(cl);
109418901357SAlexander Usyskin 			rets = -EFAULT;
109518901357SAlexander Usyskin 			goto out;
109618901357SAlexander Usyskin 		}
109718901357SAlexander Usyskin 
10980c53357cSTomas Winkler 		/* timeout or something went really wrong */
1099285e2996SAlexander Usyskin 		if (!cl->status)
1100285e2996SAlexander Usyskin 			cl->status = -EFAULT;
11019f81abdaSTomas Winkler 	}
11029f81abdaSTomas Winkler 
11039f81abdaSTomas Winkler 	rets = cl->status;
11049f81abdaSTomas Winkler out:
110504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
11062bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
11072bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
110804bb139aSTomas Winkler 
11099f81abdaSTomas Winkler 	mei_io_cb_free(cb);
11100c53357cSTomas Winkler 
11111df629efSAlexander Usyskin nortpm:
11120c53357cSTomas Winkler 	if (!mei_cl_is_connected(cl))
11130c53357cSTomas Winkler 		mei_cl_set_disconnected(cl);
11140c53357cSTomas Winkler 
11159f81abdaSTomas Winkler 	return rets;
11169f81abdaSTomas Winkler }
11179f81abdaSTomas Winkler 
11189f81abdaSTomas Winkler /**
111903b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
112003b8d341STomas Winkler  *
112103b8d341STomas Winkler  * @dev: the device structure
112203b8d341STomas Winkler  * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
112303b8d341STomas Winkler  *
112403b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
112503b8d341STomas Winkler  */
112603b8d341STomas Winkler struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id)
112703b8d341STomas Winkler {
112803b8d341STomas Winkler 	struct mei_cl *cl;
112903b8d341STomas Winkler 	int ret;
113003b8d341STomas Winkler 
113103b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
113203b8d341STomas Winkler 	if (!cl) {
113303b8d341STomas Winkler 		ret = -ENOMEM;
113403b8d341STomas Winkler 		goto err;
113503b8d341STomas Winkler 	}
113603b8d341STomas Winkler 
113703b8d341STomas Winkler 	ret = mei_cl_link(cl, id);
113803b8d341STomas Winkler 	if (ret)
113903b8d341STomas Winkler 		goto err;
114003b8d341STomas Winkler 
114103b8d341STomas Winkler 	return cl;
114203b8d341STomas Winkler err:
114303b8d341STomas Winkler 	kfree(cl);
114403b8d341STomas Winkler 	return ERR_PTR(ret);
114503b8d341STomas Winkler }
114603b8d341STomas Winkler 
114703b8d341STomas Winkler 
114803b8d341STomas Winkler 
114903b8d341STomas Winkler /**
115090e0b5f1STomas Winkler  * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
11519ca9050bSTomas Winkler  *
11529ca9050bSTomas Winkler  * @cl: private data of the file object
11539ca9050bSTomas Winkler  *
1154a8605ea2SAlexander Usyskin  * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
11559ca9050bSTomas Winkler  */
115690e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
11579ca9050bSTomas Winkler {
11581df629efSAlexander Usyskin 	int rets;
11591df629efSAlexander Usyskin 
1160d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
116190e0b5f1STomas Winkler 		return -EINVAL;
116290e0b5f1STomas Winkler 
11639ca9050bSTomas Winkler 	if (cl->mei_flow_ctrl_creds > 0)
11649ca9050bSTomas Winkler 		return 1;
11659ca9050bSTomas Winkler 
11661df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
11671df629efSAlexander Usyskin 		rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL);
11681df629efSAlexander Usyskin 		if (rets && rets != -EBUSY)
11691df629efSAlexander Usyskin 			return rets;
11701df629efSAlexander Usyskin 		return 1;
11711df629efSAlexander Usyskin 	}
11721df629efSAlexander Usyskin 
1173d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
1174d49ed64aSAlexander Usyskin 		if (cl->me_cl->mei_flow_ctrl_creds > 0)
1175d49ed64aSAlexander Usyskin 			return 1;
117612d00665SAlexander Usyskin 	}
1177d49ed64aSAlexander Usyskin 	return 0;
11789ca9050bSTomas Winkler }
11799ca9050bSTomas Winkler 
11809ca9050bSTomas Winkler /**
118190e0b5f1STomas Winkler  * mei_cl_flow_ctrl_reduce - reduces flow_control.
11829ca9050bSTomas Winkler  *
11839ca9050bSTomas Winkler  * @cl: private data of the file object
1184393b148fSMasanari Iida  *
1185a8605ea2SAlexander Usyskin  * Return:
11869ca9050bSTomas Winkler  *	0 on success
11879ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
11889ca9050bSTomas Winkler  */
118990e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
11909ca9050bSTomas Winkler {
1191d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
119290e0b5f1STomas Winkler 		return -EINVAL;
119390e0b5f1STomas Winkler 
11941df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
11951df629efSAlexander Usyskin 		return 0;
11961df629efSAlexander Usyskin 
1197d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
1198d49ed64aSAlexander Usyskin 		if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0))
1199d49ed64aSAlexander Usyskin 			return -EINVAL;
1200d49ed64aSAlexander Usyskin 		cl->me_cl->mei_flow_ctrl_creds--;
12019ca9050bSTomas Winkler 	} else {
1202d49ed64aSAlexander Usyskin 		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
1203d49ed64aSAlexander Usyskin 			return -EINVAL;
12049ca9050bSTomas Winkler 		cl->mei_flow_ctrl_creds--;
12059ca9050bSTomas Winkler 	}
1206d49ed64aSAlexander Usyskin 	return 0;
12079ca9050bSTomas Winkler }
12089ca9050bSTomas Winkler 
12099ca9050bSTomas Winkler /**
121051678ccbSTomas Winkler  *  mei_cl_notify_fop2req - convert fop to proper request
121151678ccbSTomas Winkler  *
121251678ccbSTomas Winkler  * @fop: client notification start response command
121351678ccbSTomas Winkler  *
121451678ccbSTomas Winkler  * Return:  MEI_HBM_NOTIFICATION_START/STOP
121551678ccbSTomas Winkler  */
121651678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
121751678ccbSTomas Winkler {
121851678ccbSTomas Winkler 	if (fop == MEI_FOP_NOTIFY_START)
121951678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_START;
122051678ccbSTomas Winkler 	else
122151678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_STOP;
122251678ccbSTomas Winkler }
122351678ccbSTomas Winkler 
122451678ccbSTomas Winkler /**
122551678ccbSTomas Winkler  *  mei_cl_notify_req2fop - convert notification request top file operation type
122651678ccbSTomas Winkler  *
122751678ccbSTomas Winkler  * @req: hbm notification request type
122851678ccbSTomas Winkler  *
122951678ccbSTomas Winkler  * Return:  MEI_FOP_NOTIFY_START/STOP
123051678ccbSTomas Winkler  */
123151678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
123251678ccbSTomas Winkler {
123351678ccbSTomas Winkler 	if (req == MEI_HBM_NOTIFICATION_START)
123451678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_START;
123551678ccbSTomas Winkler 	else
123651678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_STOP;
123751678ccbSTomas Winkler }
123851678ccbSTomas Winkler 
123951678ccbSTomas Winkler /**
124051678ccbSTomas Winkler  * mei_cl_irq_notify - send notification request in irq_thread context
124151678ccbSTomas Winkler  *
124251678ccbSTomas Winkler  * @cl: client
124351678ccbSTomas Winkler  * @cb: callback block.
124451678ccbSTomas Winkler  * @cmpl_list: complete list.
124551678ccbSTomas Winkler  *
124651678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
124751678ccbSTomas Winkler  */
124851678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
124951678ccbSTomas Winkler 		      struct mei_cl_cb *cmpl_list)
125051678ccbSTomas Winkler {
125151678ccbSTomas Winkler 	struct mei_device *dev = cl->dev;
125251678ccbSTomas Winkler 	u32 msg_slots;
125351678ccbSTomas Winkler 	int slots;
125451678ccbSTomas Winkler 	int ret;
125551678ccbSTomas Winkler 	bool request;
125651678ccbSTomas Winkler 
125751678ccbSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
125851678ccbSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
125951678ccbSTomas Winkler 
126051678ccbSTomas Winkler 	if (slots < msg_slots)
126151678ccbSTomas Winkler 		return -EMSGSIZE;
126251678ccbSTomas Winkler 
126351678ccbSTomas Winkler 	request = mei_cl_notify_fop2req(cb->fop_type);
126451678ccbSTomas Winkler 	ret = mei_hbm_cl_notify_req(dev, cl, request);
126551678ccbSTomas Winkler 	if (ret) {
126651678ccbSTomas Winkler 		cl->status = ret;
126751678ccbSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
126851678ccbSTomas Winkler 		return ret;
126951678ccbSTomas Winkler 	}
127051678ccbSTomas Winkler 
127151678ccbSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
127251678ccbSTomas Winkler 	return 0;
127351678ccbSTomas Winkler }
127451678ccbSTomas Winkler 
127551678ccbSTomas Winkler /**
127651678ccbSTomas Winkler  * mei_cl_notify_request - send notification stop/start request
127751678ccbSTomas Winkler  *
127851678ccbSTomas Winkler  * @cl: host client
127951678ccbSTomas Winkler  * @file: associate request with file
128051678ccbSTomas Winkler  * @request: 1 for start or 0 for stop
128151678ccbSTomas Winkler  *
128251678ccbSTomas Winkler  * Locking: called under "dev->device_lock" lock
128351678ccbSTomas Winkler  *
128451678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
128551678ccbSTomas Winkler  */
128651678ccbSTomas Winkler int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request)
128751678ccbSTomas Winkler {
128851678ccbSTomas Winkler 	struct mei_device *dev;
128951678ccbSTomas Winkler 	struct mei_cl_cb *cb;
129051678ccbSTomas Winkler 	enum mei_cb_file_ops fop_type;
129151678ccbSTomas Winkler 	int rets;
129251678ccbSTomas Winkler 
129351678ccbSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
129451678ccbSTomas Winkler 		return -ENODEV;
129551678ccbSTomas Winkler 
129651678ccbSTomas Winkler 	dev = cl->dev;
129751678ccbSTomas Winkler 
129851678ccbSTomas Winkler 	if (!dev->hbm_f_ev_supported) {
129951678ccbSTomas Winkler 		cl_dbg(dev, cl, "notifications not supported\n");
130051678ccbSTomas Winkler 		return -EOPNOTSUPP;
130151678ccbSTomas Winkler 	}
130251678ccbSTomas Winkler 
130351678ccbSTomas Winkler 	rets = pm_runtime_get(dev->dev);
130451678ccbSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
130551678ccbSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
130651678ccbSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
130751678ccbSTomas Winkler 		return rets;
130851678ccbSTomas Winkler 	}
130951678ccbSTomas Winkler 
131051678ccbSTomas Winkler 	fop_type = mei_cl_notify_req2fop(request);
131151678ccbSTomas Winkler 	cb = mei_io_cb_init(cl, fop_type, file);
131251678ccbSTomas Winkler 	if (!cb) {
131351678ccbSTomas Winkler 		rets = -ENOMEM;
131451678ccbSTomas Winkler 		goto out;
131551678ccbSTomas Winkler 	}
131651678ccbSTomas Winkler 
131751678ccbSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
131851678ccbSTomas Winkler 		if (mei_hbm_cl_notify_req(dev, cl, request)) {
131951678ccbSTomas Winkler 			rets = -ENODEV;
132051678ccbSTomas Winkler 			goto out;
132151678ccbSTomas Winkler 		}
132251678ccbSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
132351678ccbSTomas Winkler 	} else {
132451678ccbSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
132551678ccbSTomas Winkler 	}
132651678ccbSTomas Winkler 
132751678ccbSTomas Winkler 	mutex_unlock(&dev->device_lock);
132851678ccbSTomas Winkler 	wait_event_timeout(cl->wait, cl->notify_en == request,
132951678ccbSTomas Winkler 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
133051678ccbSTomas Winkler 	mutex_lock(&dev->device_lock);
133151678ccbSTomas Winkler 
133251678ccbSTomas Winkler 	if (cl->notify_en != request) {
133351678ccbSTomas Winkler 		mei_io_list_flush(&dev->ctrl_rd_list, cl);
133451678ccbSTomas Winkler 		mei_io_list_flush(&dev->ctrl_wr_list, cl);
133551678ccbSTomas Winkler 		if (!cl->status)
133651678ccbSTomas Winkler 			cl->status = -EFAULT;
133751678ccbSTomas Winkler 	}
133851678ccbSTomas Winkler 
133951678ccbSTomas Winkler 	rets = cl->status;
134051678ccbSTomas Winkler 
134151678ccbSTomas Winkler out:
134251678ccbSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
134351678ccbSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
134451678ccbSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
134551678ccbSTomas Winkler 
134651678ccbSTomas Winkler 	mei_io_cb_free(cb);
134751678ccbSTomas Winkler 	return rets;
134851678ccbSTomas Winkler }
134951678ccbSTomas Winkler 
135051678ccbSTomas Winkler /**
1351237092bfSTomas Winkler  * mei_cl_notify - raise notification
1352237092bfSTomas Winkler  *
1353237092bfSTomas Winkler  * @cl: host client
1354237092bfSTomas Winkler  *
1355237092bfSTomas Winkler  * Locking: called under "dev->device_lock" lock
1356237092bfSTomas Winkler  */
1357237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl)
1358237092bfSTomas Winkler {
1359237092bfSTomas Winkler 	struct mei_device *dev;
1360237092bfSTomas Winkler 
1361237092bfSTomas Winkler 	if (!cl || !cl->dev)
1362237092bfSTomas Winkler 		return;
1363237092bfSTomas Winkler 
1364237092bfSTomas Winkler 	dev = cl->dev;
1365237092bfSTomas Winkler 
1366237092bfSTomas Winkler 	if (!cl->notify_en)
1367237092bfSTomas Winkler 		return;
1368237092bfSTomas Winkler 
1369237092bfSTomas Winkler 	cl_dbg(dev, cl, "notify event");
1370237092bfSTomas Winkler 	cl->notify_ev = true;
1371237092bfSTomas Winkler 	wake_up_interruptible_all(&cl->ev_wait);
1372237092bfSTomas Winkler 
1373237092bfSTomas Winkler 	if (cl->ev_async)
1374237092bfSTomas Winkler 		kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
1375bb2ef9c3SAlexander Usyskin 
1376bb2ef9c3SAlexander Usyskin 	mei_cl_bus_notify_event(cl);
1377237092bfSTomas Winkler }
1378237092bfSTomas Winkler 
1379237092bfSTomas Winkler /**
1380b38a362fSTomas Winkler  * mei_cl_notify_get - get or wait for notification event
1381b38a362fSTomas Winkler  *
1382b38a362fSTomas Winkler  * @cl: host client
1383b38a362fSTomas Winkler  * @block: this request is blocking
1384b38a362fSTomas Winkler  * @notify_ev: true if notification event was received
1385b38a362fSTomas Winkler  *
1386b38a362fSTomas Winkler  * Locking: called under "dev->device_lock" lock
1387b38a362fSTomas Winkler  *
1388b38a362fSTomas Winkler  * Return: 0 on such and error otherwise.
1389b38a362fSTomas Winkler  */
1390b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
1391b38a362fSTomas Winkler {
1392b38a362fSTomas Winkler 	struct mei_device *dev;
1393b38a362fSTomas Winkler 	int rets;
1394b38a362fSTomas Winkler 
1395b38a362fSTomas Winkler 	*notify_ev = false;
1396b38a362fSTomas Winkler 
1397b38a362fSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1398b38a362fSTomas Winkler 		return -ENODEV;
1399b38a362fSTomas Winkler 
1400b38a362fSTomas Winkler 	dev = cl->dev;
1401b38a362fSTomas Winkler 
1402b38a362fSTomas Winkler 	if (!mei_cl_is_connected(cl))
1403b38a362fSTomas Winkler 		return -ENODEV;
1404b38a362fSTomas Winkler 
1405b38a362fSTomas Winkler 	if (cl->notify_ev)
1406b38a362fSTomas Winkler 		goto out;
1407b38a362fSTomas Winkler 
1408b38a362fSTomas Winkler 	if (!block)
1409b38a362fSTomas Winkler 		return -EAGAIN;
1410b38a362fSTomas Winkler 
1411b38a362fSTomas Winkler 	mutex_unlock(&dev->device_lock);
1412b38a362fSTomas Winkler 	rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev);
1413b38a362fSTomas Winkler 	mutex_lock(&dev->device_lock);
1414b38a362fSTomas Winkler 
1415b38a362fSTomas Winkler 	if (rets < 0)
1416b38a362fSTomas Winkler 		return rets;
1417b38a362fSTomas Winkler 
1418b38a362fSTomas Winkler out:
1419b38a362fSTomas Winkler 	*notify_ev = cl->notify_ev;
1420b38a362fSTomas Winkler 	cl->notify_ev = false;
1421b38a362fSTomas Winkler 	return 0;
1422b38a362fSTomas Winkler }
1423b38a362fSTomas Winkler 
1424b38a362fSTomas Winkler /**
142513cf9885SAlexander Usyskin  * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control
142613cf9885SAlexander Usyskin  *                        for given host client
142713cf9885SAlexander Usyskin  *
142813cf9885SAlexander Usyskin  * @cl: host client
142913cf9885SAlexander Usyskin  *
143013cf9885SAlexander Usyskin  * Return: true, if found at least one cb.
143113cf9885SAlexander Usyskin  */
143213cf9885SAlexander Usyskin static bool mei_cl_is_read_fc_cb(struct mei_cl *cl)
143313cf9885SAlexander Usyskin {
143413cf9885SAlexander Usyskin 	struct mei_device *dev = cl->dev;
143513cf9885SAlexander Usyskin 	struct mei_cl_cb *cb;
143613cf9885SAlexander Usyskin 
143713cf9885SAlexander Usyskin 	list_for_each_entry(cb, &dev->ctrl_wr_list.list, list)
143813cf9885SAlexander Usyskin 		if (cb->fop_type == MEI_FOP_READ && cb->cl == cl)
143913cf9885SAlexander Usyskin 			return true;
144013cf9885SAlexander Usyskin 	return false;
144113cf9885SAlexander Usyskin }
144213cf9885SAlexander Usyskin 
144313cf9885SAlexander Usyskin /**
1444393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
14459ca9050bSTomas Winkler  *
144690e0b5f1STomas Winkler  * @cl: host client
1447ce23139cSAlexander Usyskin  * @length: number of bytes to read
1448bca67d68STomas Winkler  * @fp: pointer to file structure
14499ca9050bSTomas Winkler  *
1450a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
14519ca9050bSTomas Winkler  */
1452bca67d68STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
14539ca9050bSTomas Winkler {
145490e0b5f1STomas Winkler 	struct mei_device *dev;
14559ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
14569ca9050bSTomas Winkler 	int rets;
14579ca9050bSTomas Winkler 
145890e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
145990e0b5f1STomas Winkler 		return -ENODEV;
146090e0b5f1STomas Winkler 
146190e0b5f1STomas Winkler 	dev = cl->dev;
146290e0b5f1STomas Winkler 
1463b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
14649ca9050bSTomas Winkler 		return -ENODEV;
14659ca9050bSTomas Winkler 
1466a9bed610STomas Winkler 	/* HW currently supports only one pending read */
146713cf9885SAlexander Usyskin 	if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl))
14689ca9050bSTomas Winkler 		return -EBUSY;
1469a9bed610STomas Winkler 
1470d49ed64aSAlexander Usyskin 	if (!mei_me_cl_is_active(cl->me_cl)) {
1471d49ed64aSAlexander Usyskin 		cl_err(dev, cl, "no such me client\n");
14727ca96aa2SAlexander Usyskin 		return  -ENOTTY;
14739ca9050bSTomas Winkler 	}
14741df629efSAlexander Usyskin 
147579563db9STomas Winkler 	/* always allocate at least client max message */
1476d49ed64aSAlexander Usyskin 	length = max_t(size_t, length, mei_cl_mtu(cl));
14771df629efSAlexander Usyskin 	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
14781df629efSAlexander Usyskin 	if (!cb)
14791df629efSAlexander Usyskin 		return -ENOMEM;
14801df629efSAlexander Usyskin 
14811df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
14821df629efSAlexander Usyskin 		list_add_tail(&cb->list, &cl->rd_pending);
14831df629efSAlexander Usyskin 		return 0;
14841df629efSAlexander Usyskin 	}
14859ca9050bSTomas Winkler 
14862bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
148704bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
14882bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
148904bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
14901df629efSAlexander Usyskin 		goto nortpm;
149104bb139aSTomas Winkler 	}
149204bb139aSTomas Winkler 
14936aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
149486113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
149586113500SAlexander Usyskin 		if (rets < 0)
149604bb139aSTomas Winkler 			goto out;
149704bb139aSTomas Winkler 
1498a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_pending);
14999ca9050bSTomas Winkler 	} else {
15001df629efSAlexander Usyskin 		rets = 0;
15019ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
15029ca9050bSTomas Winkler 	}
1503accb884bSChao Bi 
150404bb139aSTomas Winkler out:
150504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
15062bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
15072bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
15081df629efSAlexander Usyskin nortpm:
150904bb139aSTomas Winkler 	if (rets)
15109ca9050bSTomas Winkler 		mei_io_cb_free(cb);
151104bb139aSTomas Winkler 
15129ca9050bSTomas Winkler 	return rets;
15139ca9050bSTomas Winkler }
15149ca9050bSTomas Winkler 
1515074b4c01STomas Winkler /**
15169d098192STomas Winkler  * mei_cl_irq_write - write a message to device
151721767546STomas Winkler  *	from the interrupt thread context
151821767546STomas Winkler  *
151921767546STomas Winkler  * @cl: client
152021767546STomas Winkler  * @cb: callback block.
152121767546STomas Winkler  * @cmpl_list: complete list.
152221767546STomas Winkler  *
1523a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
152421767546STomas Winkler  */
15259d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
15269d098192STomas Winkler 		     struct mei_cl_cb *cmpl_list)
152721767546STomas Winkler {
1528136698e5STomas Winkler 	struct mei_device *dev;
1529136698e5STomas Winkler 	struct mei_msg_data *buf;
153021767546STomas Winkler 	struct mei_msg_hdr mei_hdr;
1531136698e5STomas Winkler 	size_t len;
1532136698e5STomas Winkler 	u32 msg_slots;
15339d098192STomas Winkler 	int slots;
15342ebf8c94STomas Winkler 	int rets;
1535b8b73035SAlexander Usyskin 	bool first_chunk;
153621767546STomas Winkler 
1537136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1538136698e5STomas Winkler 		return -ENODEV;
1539136698e5STomas Winkler 
1540136698e5STomas Winkler 	dev = cl->dev;
1541136698e5STomas Winkler 
15425db7514dSTomas Winkler 	buf = &cb->buf;
1543136698e5STomas Winkler 
1544b8b73035SAlexander Usyskin 	first_chunk = cb->buf_idx == 0;
1545b8b73035SAlexander Usyskin 
1546b8b73035SAlexander Usyskin 	rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1;
1547136698e5STomas Winkler 	if (rets < 0)
1548136698e5STomas Winkler 		return rets;
1549136698e5STomas Winkler 
1550136698e5STomas Winkler 	if (rets == 0) {
1551136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1552136698e5STomas Winkler 		return 0;
1553136698e5STomas Winkler 	}
1554136698e5STomas Winkler 
15559d098192STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1556136698e5STomas Winkler 	len = buf->size - cb->buf_idx;
1557136698e5STomas Winkler 	msg_slots = mei_data2slots(len);
1558136698e5STomas Winkler 
15591df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1560d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
156121767546STomas Winkler 	mei_hdr.reserved = 0;
1562479327fcSTomas Winkler 	mei_hdr.internal = cb->internal;
156321767546STomas Winkler 
15649d098192STomas Winkler 	if (slots >= msg_slots) {
156521767546STomas Winkler 		mei_hdr.length = len;
156621767546STomas Winkler 		mei_hdr.msg_complete = 1;
156721767546STomas Winkler 	/* Split the message only if we can write the whole host buffer */
15689d098192STomas Winkler 	} else if (slots == dev->hbuf_depth) {
15699d098192STomas Winkler 		msg_slots = slots;
15709d098192STomas Winkler 		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
157121767546STomas Winkler 		mei_hdr.length = len;
157221767546STomas Winkler 		mei_hdr.msg_complete = 0;
157321767546STomas Winkler 	} else {
157421767546STomas Winkler 		/* wait for next time the host buffer is empty */
157521767546STomas Winkler 		return 0;
157621767546STomas Winkler 	}
157721767546STomas Winkler 
1578c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
15795db7514dSTomas Winkler 			cb->buf.size, cb->buf_idx);
158021767546STomas Winkler 
1581136698e5STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
15822ebf8c94STomas Winkler 	if (rets) {
15832ebf8c94STomas Winkler 		cl->status = rets;
158421767546STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
15852ebf8c94STomas Winkler 		return rets;
158621767546STomas Winkler 	}
158721767546STomas Winkler 
158821767546STomas Winkler 	cl->status = 0;
15894dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
159021767546STomas Winkler 	cb->buf_idx += mei_hdr.length;
15918660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
15924dfaa9f7STomas Winkler 
1593b8b73035SAlexander Usyskin 	if (first_chunk) {
159421767546STomas Winkler 		if (mei_cl_flow_ctrl_reduce(cl))
15952ebf8c94STomas Winkler 			return -EIO;
159621767546STomas Winkler 	}
159721767546STomas Winkler 
1598b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
1599b8b73035SAlexander Usyskin 		list_move_tail(&cb->list, &dev->write_waiting_list.list);
1600b8b73035SAlexander Usyskin 
160121767546STomas Winkler 	return 0;
160221767546STomas Winkler }
160321767546STomas Winkler 
160421767546STomas Winkler /**
16054234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1606a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
16074234a6deSTomas Winkler  *
16084234a6deSTomas Winkler  * @cl: host client
1609a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
1610ce23139cSAlexander Usyskin  * @blocking: block until completed
16114234a6deSTomas Winkler  *
1612a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
16134234a6deSTomas Winkler  */
16144234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
16154234a6deSTomas Winkler {
16164234a6deSTomas Winkler 	struct mei_device *dev;
16174234a6deSTomas Winkler 	struct mei_msg_data *buf;
16184234a6deSTomas Winkler 	struct mei_msg_hdr mei_hdr;
161923253c31SAlexander Usyskin 	int size;
16204234a6deSTomas Winkler 	int rets;
16214234a6deSTomas Winkler 
16224234a6deSTomas Winkler 
16234234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
16244234a6deSTomas Winkler 		return -ENODEV;
16254234a6deSTomas Winkler 
16264234a6deSTomas Winkler 	if (WARN_ON(!cb))
16274234a6deSTomas Winkler 		return -EINVAL;
16284234a6deSTomas Winkler 
16294234a6deSTomas Winkler 	dev = cl->dev;
16304234a6deSTomas Winkler 
16315db7514dSTomas Winkler 	buf = &cb->buf;
163223253c31SAlexander Usyskin 	size = buf->size;
16334234a6deSTomas Winkler 
163423253c31SAlexander Usyskin 	cl_dbg(dev, cl, "size=%d\n", size);
16354234a6deSTomas Winkler 
16362bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
163704bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
16382bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
163904bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
164004bb139aSTomas Winkler 		return rets;
164104bb139aSTomas Winkler 	}
16424234a6deSTomas Winkler 
16436aae48ffSTomas Winkler 	cb->buf_idx = 0;
16446aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
16456aae48ffSTomas Winkler 
16461df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1647d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
16486aae48ffSTomas Winkler 	mei_hdr.reserved = 0;
16496aae48ffSTomas Winkler 	mei_hdr.msg_complete = 0;
16506aae48ffSTomas Winkler 	mei_hdr.internal = cb->internal;
16514234a6deSTomas Winkler 
16524234a6deSTomas Winkler 	rets = mei_cl_flow_ctrl_creds(cl);
16534234a6deSTomas Winkler 	if (rets < 0)
16544234a6deSTomas Winkler 		goto err;
16554234a6deSTomas Winkler 
16566aae48ffSTomas Winkler 	if (rets == 0) {
16576aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
165823253c31SAlexander Usyskin 		rets = size;
16594234a6deSTomas Winkler 		goto out;
16604234a6deSTomas Winkler 	}
16616aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
16626aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
166323253c31SAlexander Usyskin 		rets = size;
16646aae48ffSTomas Winkler 		goto out;
16656aae48ffSTomas Winkler 	}
16664234a6deSTomas Winkler 
16674234a6deSTomas Winkler 	/* Check for a maximum length */
166823253c31SAlexander Usyskin 	if (size > mei_hbuf_max_len(dev)) {
16694234a6deSTomas Winkler 		mei_hdr.length = mei_hbuf_max_len(dev);
16704234a6deSTomas Winkler 		mei_hdr.msg_complete = 0;
16714234a6deSTomas Winkler 	} else {
167223253c31SAlexander Usyskin 		mei_hdr.length = size;
16734234a6deSTomas Winkler 		mei_hdr.msg_complete = 1;
16744234a6deSTomas Winkler 	}
16754234a6deSTomas Winkler 
16762ebf8c94STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data);
16772ebf8c94STomas Winkler 	if (rets)
16784234a6deSTomas Winkler 		goto err;
16794234a6deSTomas Winkler 
1680b8b73035SAlexander Usyskin 	rets = mei_cl_flow_ctrl_reduce(cl);
1681b8b73035SAlexander Usyskin 	if (rets)
1682b8b73035SAlexander Usyskin 		goto err;
1683b8b73035SAlexander Usyskin 
16844234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
16854234a6deSTomas Winkler 	cb->buf_idx = mei_hdr.length;
16868660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
16874234a6deSTomas Winkler 
16884234a6deSTomas Winkler out:
1689b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
16904234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_waiting_list.list);
1691b8b73035SAlexander Usyskin 	else
16924234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_list.list);
16934234a6deSTomas Winkler 
169423253c31SAlexander Usyskin 	cb = NULL;
16954234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
16964234a6deSTomas Winkler 
16974234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
16987ca96aa2SAlexander Usyskin 		rets = wait_event_interruptible(cl->tx_wait,
16997ca96aa2SAlexander Usyskin 				cl->writing_state == MEI_WRITE_COMPLETE);
17007ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
17017ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
17027ca96aa2SAlexander Usyskin 		if (rets) {
17034234a6deSTomas Winkler 			if (signal_pending(current))
17044234a6deSTomas Winkler 				rets = -EINTR;
17057ca96aa2SAlexander Usyskin 			goto err;
17064234a6deSTomas Winkler 		}
17074234a6deSTomas Winkler 	}
17087ca96aa2SAlexander Usyskin 
170923253c31SAlexander Usyskin 	rets = size;
17104234a6deSTomas Winkler err:
171104bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
17122bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
17132bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
171404bb139aSTomas Winkler 
17154234a6deSTomas Winkler 	return rets;
17164234a6deSTomas Winkler }
17174234a6deSTomas Winkler 
17184234a6deSTomas Winkler 
1719db086fa9STomas Winkler /**
1720db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
1721db086fa9STomas Winkler  *
1722db086fa9STomas Winkler  * @cl: private data of the file object.
1723db086fa9STomas Winkler  * @cb: callback block.
1724db086fa9STomas Winkler  */
1725db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
1726db086fa9STomas Winkler {
1727a1809d38SAlexander Usyskin 	struct mei_device *dev = cl->dev;
1728a1809d38SAlexander Usyskin 
17293c666182STomas Winkler 	switch (cb->fop_type) {
17303c666182STomas Winkler 	case MEI_FOP_WRITE:
1731db086fa9STomas Winkler 		mei_io_cb_free(cb);
1732db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
1733a1809d38SAlexander Usyskin 		if (waitqueue_active(&cl->tx_wait)) {
1734db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
1735a1809d38SAlexander Usyskin 		} else {
1736a1809d38SAlexander Usyskin 			pm_runtime_mark_last_busy(dev->dev);
1737a1809d38SAlexander Usyskin 			pm_request_autosuspend(dev->dev);
1738a1809d38SAlexander Usyskin 		}
17393c666182STomas Winkler 		break;
1740db086fa9STomas Winkler 
17413c666182STomas Winkler 	case MEI_FOP_READ:
1742a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_completed);
1743db086fa9STomas Winkler 		if (waitqueue_active(&cl->rx_wait))
17441d9013f0STomas Winkler 			wake_up_interruptible_all(&cl->rx_wait);
1745db086fa9STomas Winkler 		else
1746db086fa9STomas Winkler 			mei_cl_bus_rx_event(cl);
17473c666182STomas Winkler 		break;
1748db086fa9STomas Winkler 
17493c666182STomas Winkler 	case MEI_FOP_CONNECT:
17503c666182STomas Winkler 	case MEI_FOP_DISCONNECT:
175151678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_STOP:
175251678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_START:
17533c666182STomas Winkler 		if (waitqueue_active(&cl->wait))
17543c666182STomas Winkler 			wake_up(&cl->wait);
17553c666182STomas Winkler 
17563c666182STomas Winkler 		break;
17573c666182STomas Winkler 	default:
17583c666182STomas Winkler 		BUG_ON(0);
1759db086fa9STomas Winkler 	}
1760db086fa9STomas Winkler }
1761db086fa9STomas Winkler 
17624234a6deSTomas Winkler 
17634234a6deSTomas Winkler /**
1764074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
1765074b4c01STomas Winkler  *
1766a8605ea2SAlexander Usyskin  * @dev: mei device
1767074b4c01STomas Winkler  */
1768074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
1769074b4c01STomas Winkler {
177031f88f57STomas Winkler 	struct mei_cl *cl;
1771074b4c01STomas Winkler 
17723c666182STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link)
17733c666182STomas Winkler 		mei_cl_set_disconnected(cl);
1774074b4c01STomas Winkler }
1775074b4c01STomas Winkler 
1776074b4c01STomas Winkler 
1777074b4c01STomas Winkler /**
17785290801cSTomas Winkler  * mei_cl_all_wakeup  - wake up all readers and writers they can be interrupted
1779074b4c01STomas Winkler  *
1780a8605ea2SAlexander Usyskin  * @dev: mei device
1781074b4c01STomas Winkler  */
17825290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev)
1783074b4c01STomas Winkler {
178431f88f57STomas Winkler 	struct mei_cl *cl;
178592db1555STomas Winkler 
178631f88f57STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link) {
1787074b4c01STomas Winkler 		if (waitqueue_active(&cl->rx_wait)) {
1788c0abffbdSAlexander Usyskin 			cl_dbg(dev, cl, "Waking up reading client!\n");
1789074b4c01STomas Winkler 			wake_up_interruptible(&cl->rx_wait);
1790074b4c01STomas Winkler 		}
17915290801cSTomas Winkler 		if (waitqueue_active(&cl->tx_wait)) {
1792c0abffbdSAlexander Usyskin 			cl_dbg(dev, cl, "Waking up writing client!\n");
17935290801cSTomas Winkler 			wake_up_interruptible(&cl->tx_wait);
17945290801cSTomas Winkler 		}
1795b38a362fSTomas Winkler 
1796b38a362fSTomas Winkler 		/* synchronized under device mutex */
1797b38a362fSTomas Winkler 		if (waitqueue_active(&cl->ev_wait)) {
1798b38a362fSTomas Winkler 			cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
1799b38a362fSTomas Winkler 			wake_up_interruptible(&cl->ev_wait);
1800b38a362fSTomas Winkler 		}
1801074b4c01STomas Winkler 	}
1802074b4c01STomas Winkler }
1803074b4c01STomas Winkler 
1804074b4c01STomas Winkler /**
1805074b4c01STomas Winkler  * mei_cl_all_write_clear - clear all pending writes
1806a8605ea2SAlexander Usyskin  *
1807a8605ea2SAlexander Usyskin  * @dev: mei device
1808074b4c01STomas Winkler  */
1809074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev)
1810074b4c01STomas Winkler {
1811cc99ecfdSTomas Winkler 	mei_io_list_free(&dev->write_list, NULL);
1812cc99ecfdSTomas Winkler 	mei_io_list_free(&dev->write_waiting_list, NULL);
1813074b4c01STomas Winkler }
1814074b4c01STomas Winkler 
1815074b4c01STomas Winkler 
1816