xref: /openbmc/linux/drivers/misc/mei/client.c (revision 35bf7692)
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,
362f23e2cc4STomas Winkler 				 const 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);
37162e8e6adSTomas Winkler 	cb->fp = 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,
458f23e2cc4STomas Winkler 				  enum mei_cb_file_ops type,
459f23e2cc4STomas Winkler 				  const struct file *fp)
460bca67d68STomas Winkler {
461bca67d68STomas Winkler 	struct mei_cl_cb *cb;
462bca67d68STomas Winkler 
463bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, type, fp);
464bca67d68STomas Winkler 	if (!cb)
465bca67d68STomas Winkler 		return NULL;
466bca67d68STomas Winkler 
467bca67d68STomas Winkler 	if (mei_io_cb_alloc_buf(cb, length)) {
468bca67d68STomas Winkler 		mei_io_cb_free(cb);
469bca67d68STomas Winkler 		return NULL;
470bca67d68STomas Winkler 	}
471bca67d68STomas Winkler 
472bca67d68STomas Winkler 	return cb;
473bca67d68STomas Winkler }
474bca67d68STomas Winkler 
475bca67d68STomas Winkler /**
476a9bed610STomas Winkler  * mei_cl_read_cb - find this cl's callback in the read list
477a9bed610STomas Winkler  *     for a specific file
478a9bed610STomas Winkler  *
479a9bed610STomas Winkler  * @cl: host client
480a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
481a9bed610STomas Winkler  *
482a9bed610STomas Winkler  * Return: cb on success, NULL if cb is not found
483a9bed610STomas Winkler  */
484a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
485a9bed610STomas Winkler {
486a9bed610STomas Winkler 	struct mei_cl_cb *cb;
487a9bed610STomas Winkler 
488a9bed610STomas Winkler 	list_for_each_entry(cb, &cl->rd_completed, list)
48962e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
490a9bed610STomas Winkler 			return cb;
491a9bed610STomas Winkler 
492a9bed610STomas Winkler 	return NULL;
493a9bed610STomas Winkler }
494a9bed610STomas Winkler 
495a9bed610STomas Winkler /**
496a9bed610STomas Winkler  * mei_cl_read_cb_flush - free client's read pending and completed cbs
497a9bed610STomas Winkler  *   for a specific file
498a9bed610STomas Winkler  *
499a9bed610STomas Winkler  * @cl: host client
500a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
501a9bed610STomas Winkler  */
502a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
503a9bed610STomas Winkler {
504a9bed610STomas Winkler 	struct mei_cl_cb *cb, *next;
505a9bed610STomas Winkler 
506a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
50762e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
508a9bed610STomas Winkler 			mei_io_cb_free(cb);
509a9bed610STomas Winkler 
510a9bed610STomas Winkler 
511a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
51262e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
513a9bed610STomas Winkler 			mei_io_cb_free(cb);
514a9bed610STomas Winkler }
515a9bed610STomas Winkler 
516a9bed610STomas Winkler /**
5179ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
5189ca9050bSTomas Winkler  *
5199ca9050bSTomas Winkler  * @cl: host client
520a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
521ce23139cSAlexander Usyskin  *
522ce23139cSAlexander Usyskin  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
5239ca9050bSTomas Winkler  */
524a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
5259ca9050bSTomas Winkler {
526c0abffbdSAlexander Usyskin 	struct mei_device *dev;
527c0abffbdSAlexander Usyskin 
52890e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5299ca9050bSTomas Winkler 		return -EINVAL;
5309ca9050bSTomas Winkler 
531c0abffbdSAlexander Usyskin 	dev = cl->dev;
532c0abffbdSAlexander Usyskin 
533c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
534cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_list, cl);
535cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_waiting_list, cl);
5369ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
5379ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
5389ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_cmd_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
590393b148fSMasanari Iida  *
591a8605ea2SAlexander Usyskin  * Return: 0 on success
5929ca9050bSTomas Winkler  *	-EINVAL on incorrect values
59303b8d341STomas Winkler  *	-EMFILE if open count exceeded.
5949ca9050bSTomas Winkler  */
5957851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl)
5969ca9050bSTomas Winkler {
59790e0b5f1STomas Winkler 	struct mei_device *dev;
59822f96a0eSTomas Winkler 	long open_handle_count;
5997851e008SAlexander Usyskin 	int id;
6009ca9050bSTomas Winkler 
601781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
6029ca9050bSTomas Winkler 		return -EINVAL;
6039ca9050bSTomas Winkler 
60490e0b5f1STomas Winkler 	dev = cl->dev;
60590e0b5f1STomas Winkler 
6067851e008SAlexander Usyskin 	id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
607781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
6082bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
609e036cc57STomas Winkler 		return -EMFILE;
610e036cc57STomas Winkler 	}
611e036cc57STomas Winkler 
61222f96a0eSTomas Winkler 	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
61322f96a0eSTomas Winkler 	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6142bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
615e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
616e036cc57STomas Winkler 		return -EMFILE;
6179ca9050bSTomas Winkler 	}
618781d0d89STomas Winkler 
619781d0d89STomas Winkler 	dev->open_handle_count++;
620781d0d89STomas Winkler 
621781d0d89STomas Winkler 	cl->host_client_id = id;
622781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
623781d0d89STomas Winkler 
624781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
625781d0d89STomas Winkler 
626781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
627781d0d89STomas Winkler 
628c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
629781d0d89STomas Winkler 	return 0;
630781d0d89STomas Winkler }
631781d0d89STomas Winkler 
6329ca9050bSTomas Winkler /**
633d49ed64aSAlexander Usyskin  * mei_cl_unlink - remove host client from the list
6349ca9050bSTomas Winkler  *
635393b148fSMasanari Iida  * @cl: host client
636ce23139cSAlexander Usyskin  *
637ce23139cSAlexander Usyskin  * Return: always 0
6389ca9050bSTomas Winkler  */
63990e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6409ca9050bSTomas Winkler {
64190e0b5f1STomas Winkler 	struct mei_device *dev;
64290e0b5f1STomas Winkler 
643781d0d89STomas Winkler 	/* don't shout on error exit path */
644781d0d89STomas Winkler 	if (!cl)
645781d0d89STomas Winkler 		return 0;
646781d0d89STomas Winkler 
647fdd9b865SAlexander Usyskin 	/* amthif might not be initialized */
6488e9a4a9aSTomas Winkler 	if (!cl->dev)
6498e9a4a9aSTomas Winkler 		return 0;
65090e0b5f1STomas Winkler 
65190e0b5f1STomas Winkler 	dev = cl->dev;
65290e0b5f1STomas Winkler 
653a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
654a14c44d8STomas Winkler 
65522f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
65622f96a0eSTomas Winkler 		dev->open_handle_count--;
65722f96a0eSTomas Winkler 
65822f96a0eSTomas Winkler 	/* never clear the 0 bit */
65922f96a0eSTomas Winkler 	if (cl->host_client_id)
66022f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
66122f96a0eSTomas Winkler 
66222f96a0eSTomas Winkler 	list_del_init(&cl->link);
66322f96a0eSTomas Winkler 
66422f96a0eSTomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
66522f96a0eSTomas Winkler 
66690e0b5f1STomas Winkler 	return 0;
6679ca9050bSTomas Winkler }
6689ca9050bSTomas Winkler 
669025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev)
6709ca9050bSTomas Winkler {
6719ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
6726adb8efbSTomas Winkler 	dev->reset_count = 0;
67304bb139aSTomas Winkler 
674025fb792SAlexander Usyskin 	schedule_work(&dev->bus_rescan_work);
6756009595aSTomas Winkler 
6762bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
6772bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
6782bf94cabSTomas Winkler 	pm_runtime_autosuspend(dev->dev);
6799ca9050bSTomas Winkler }
6809ca9050bSTomas Winkler 
6816aae48ffSTomas Winkler /**
682a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
6836aae48ffSTomas Winkler  *
6846aae48ffSTomas Winkler  * @dev: the device structure
685a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
6866aae48ffSTomas Winkler  */
6876aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
6886aae48ffSTomas Winkler {
68904bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
6903dc196eaSAlexander Usyskin 	    mei_pg_in_transition(dev)) {
6912bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
69204bb139aSTomas Winkler 		return false;
69304bb139aSTomas Winkler 	}
69404bb139aSTomas Winkler 
6956aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
6962bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
6976aae48ffSTomas Winkler 		return false;
6986aae48ffSTomas Winkler 	}
6996aae48ffSTomas Winkler 
7006aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7016aae48ffSTomas Winkler 
7026aae48ffSTomas Winkler 	return true;
7036aae48ffSTomas Winkler }
7049ca9050bSTomas Winkler 
7059ca9050bSTomas Winkler /**
706a4307fe4SAlexander Usyskin  * mei_cl_wake_all - wake up readers, writers and event waiters so
707a4307fe4SAlexander Usyskin  *                 they can be interrupted
708a4307fe4SAlexander Usyskin  *
709a4307fe4SAlexander Usyskin  * @cl: host client
710a4307fe4SAlexander Usyskin  */
711a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl)
712a4307fe4SAlexander Usyskin {
713a4307fe4SAlexander Usyskin 	struct mei_device *dev = cl->dev;
714a4307fe4SAlexander Usyskin 
715a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
716a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->rx_wait)) {
717a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up reading client!\n");
718a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->rx_wait);
719a4307fe4SAlexander Usyskin 	}
720a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
721a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->tx_wait)) {
722a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up writing client!\n");
723a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->tx_wait);
724a4307fe4SAlexander Usyskin 	}
725a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
726a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->ev_wait)) {
727a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
728a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->ev_wait);
729a4307fe4SAlexander Usyskin 	}
730a4307fe4SAlexander Usyskin }
731a4307fe4SAlexander Usyskin 
732a4307fe4SAlexander Usyskin /**
7333c666182STomas Winkler  * mei_cl_set_disconnected - set disconnected state and clear
7343c666182STomas Winkler  *   associated states and resources
7353c666182STomas Winkler  *
7363c666182STomas Winkler  * @cl: host client
7373c666182STomas Winkler  */
7383c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl)
7393c666182STomas Winkler {
7403c666182STomas Winkler 	struct mei_device *dev = cl->dev;
7413c666182STomas Winkler 
7423c666182STomas Winkler 	if (cl->state == MEI_FILE_DISCONNECTED ||
7433c666182STomas Winkler 	    cl->state == MEI_FILE_INITIALIZING)
7443c666182STomas Winkler 		return;
7453c666182STomas Winkler 
7463c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTED;
747a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_list, cl);
748a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_waiting_list, cl);
7493c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7503c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
751a4307fe4SAlexander Usyskin 	mei_cl_wake_all(cl);
7523c666182STomas Winkler 	cl->mei_flow_ctrl_creds = 0;
7533c666182STomas Winkler 	cl->timer_count = 0;
754d49ed64aSAlexander Usyskin 
755a03d77f6SAlexander Usyskin 	if (!cl->me_cl)
756a03d77f6SAlexander Usyskin 		return;
757a03d77f6SAlexander Usyskin 
758a03d77f6SAlexander Usyskin 	if (!WARN_ON(cl->me_cl->connect_count == 0))
759a03d77f6SAlexander Usyskin 		cl->me_cl->connect_count--;
760a03d77f6SAlexander Usyskin 
761c241e9b1SAlexander Usyskin 	if (cl->me_cl->connect_count == 0)
762c241e9b1SAlexander Usyskin 		cl->me_cl->mei_flow_ctrl_creds = 0;
763c241e9b1SAlexander Usyskin 
764d49ed64aSAlexander Usyskin 	mei_me_cl_put(cl->me_cl);
765d49ed64aSAlexander Usyskin 	cl->me_cl = NULL;
7663c666182STomas Winkler }
7673c666182STomas Winkler 
768a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
769a03d77f6SAlexander Usyskin {
7701df629efSAlexander Usyskin 	if (!mei_me_cl_get(me_cl))
771a03d77f6SAlexander Usyskin 		return -ENOENT;
772a03d77f6SAlexander Usyskin 
7731df629efSAlexander Usyskin 	/* only one connection is allowed for fixed address clients */
7741df629efSAlexander Usyskin 	if (me_cl->props.fixed_address) {
7751df629efSAlexander Usyskin 		if (me_cl->connect_count) {
7761df629efSAlexander Usyskin 			mei_me_cl_put(me_cl);
7771df629efSAlexander Usyskin 			return -EBUSY;
7781df629efSAlexander Usyskin 		}
7791df629efSAlexander Usyskin 	}
7801df629efSAlexander Usyskin 
7811df629efSAlexander Usyskin 	cl->me_cl = me_cl;
782a03d77f6SAlexander Usyskin 	cl->state = MEI_FILE_CONNECTING;
783a03d77f6SAlexander Usyskin 	cl->me_cl->connect_count++;
784a03d77f6SAlexander Usyskin 
785a03d77f6SAlexander Usyskin 	return 0;
786a03d77f6SAlexander Usyskin }
787a03d77f6SAlexander Usyskin 
7883c666182STomas Winkler /*
7893c666182STomas Winkler  * mei_cl_send_disconnect - send disconnect request
7903c666182STomas Winkler  *
7913c666182STomas Winkler  * @cl: host client
7923c666182STomas Winkler  * @cb: callback block
7933c666182STomas Winkler  *
7943c666182STomas Winkler  * Return: 0, OK; otherwise, error.
7953c666182STomas Winkler  */
7963c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
7973c666182STomas Winkler {
7983c666182STomas Winkler 	struct mei_device *dev;
7993c666182STomas Winkler 	int ret;
8003c666182STomas Winkler 
8013c666182STomas Winkler 	dev = cl->dev;
8023c666182STomas Winkler 
8033c666182STomas Winkler 	ret = mei_hbm_cl_disconnect_req(dev, cl);
8043c666182STomas Winkler 	cl->status = ret;
8053c666182STomas Winkler 	if (ret) {
8063c666182STomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
8073c666182STomas Winkler 		return ret;
8083c666182STomas Winkler 	}
8093c666182STomas Winkler 
8103c666182STomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
8113c666182STomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
8123c666182STomas Winkler 
8133c666182STomas Winkler 	return 0;
8143c666182STomas Winkler }
8153c666182STomas Winkler 
8163c666182STomas Winkler /**
8173c666182STomas Winkler  * mei_cl_irq_disconnect - processes close related operation from
8183c666182STomas Winkler  *	interrupt thread context - send disconnect request
8193c666182STomas Winkler  *
8203c666182STomas Winkler  * @cl: client
8213c666182STomas Winkler  * @cb: callback block.
8223c666182STomas Winkler  * @cmpl_list: complete list.
8233c666182STomas Winkler  *
8243c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8253c666182STomas Winkler  */
8263c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
8273c666182STomas Winkler 			    struct mei_cl_cb *cmpl_list)
8283c666182STomas Winkler {
8293c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8303c666182STomas Winkler 	u32 msg_slots;
8313c666182STomas Winkler 	int slots;
8323c666182STomas Winkler 	int ret;
8333c666182STomas Winkler 
8343c666182STomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
8353c666182STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
8363c666182STomas Winkler 
8373c666182STomas Winkler 	if (slots < msg_slots)
8383c666182STomas Winkler 		return -EMSGSIZE;
8393c666182STomas Winkler 
8403c666182STomas Winkler 	ret = mei_cl_send_disconnect(cl, cb);
8413c666182STomas Winkler 	if (ret)
8423c666182STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
8433c666182STomas Winkler 
8443c666182STomas Winkler 	return ret;
8453c666182STomas Winkler }
8463c666182STomas Winkler 
8473c666182STomas Winkler /**
84818901357SAlexander Usyskin  * __mei_cl_disconnect - disconnect host client from the me one
84918901357SAlexander Usyskin  *     internal function runtime pm has to be already acquired
8509ca9050bSTomas Winkler  *
85190e0b5f1STomas Winkler  * @cl: host client
8529ca9050bSTomas Winkler  *
853a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
8549ca9050bSTomas Winkler  */
85518901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl)
8569ca9050bSTomas Winkler {
85790e0b5f1STomas Winkler 	struct mei_device *dev;
8589ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
859fe2f17ebSAlexander Usyskin 	int rets;
8609ca9050bSTomas Winkler 
86190e0b5f1STomas Winkler 	dev = cl->dev;
86290e0b5f1STomas Winkler 
8633c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTING;
8643c666182STomas Winkler 
865bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
866bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
867bca67d68STomas Winkler 	if (rets)
8683c666182STomas Winkler 		goto out;
8695a8373fbSTomas Winkler 
870c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "add disconnect cb to control write list\n");
8719ca9050bSTomas Winkler 	list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
8729ca9050bSTomas Winkler 
8733c666182STomas Winkler 	if (mei_hbuf_acquire(dev)) {
8743c666182STomas Winkler 		rets = mei_cl_send_disconnect(cl, cb);
8753c666182STomas Winkler 		if (rets) {
8763c666182STomas Winkler 			cl_err(dev, cl, "failed to disconnect.\n");
8773c666182STomas Winkler 			goto out;
8789ca9050bSTomas Winkler 		}
8793c666182STomas Winkler 	}
8803c666182STomas Winkler 
8819ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
8823c666182STomas Winkler 	wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY,
8839ca9050bSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
8849ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
885fe2f17ebSAlexander Usyskin 
8863c666182STomas Winkler 	rets = cl->status;
8873c666182STomas Winkler 	if (cl->state != MEI_FILE_DISCONNECT_REPLY) {
888fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
889fe2f17ebSAlexander Usyskin 		rets = -ETIME;
8909ca9050bSTomas Winkler 	}
8919ca9050bSTomas Winkler 
8923c666182STomas Winkler out:
8933c666182STomas Winkler 	/* we disconnect also on error */
8943c666182STomas Winkler 	mei_cl_set_disconnected(cl);
8953c666182STomas Winkler 	if (!rets)
8963c666182STomas Winkler 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
8973c666182STomas Winkler 
89818901357SAlexander Usyskin 	mei_io_cb_free(cb);
89918901357SAlexander Usyskin 	return rets;
90018901357SAlexander Usyskin }
90118901357SAlexander Usyskin 
90218901357SAlexander Usyskin /**
90318901357SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
90418901357SAlexander Usyskin  *
90518901357SAlexander Usyskin  * @cl: host client
90618901357SAlexander Usyskin  *
90718901357SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
90818901357SAlexander Usyskin  *
90918901357SAlexander Usyskin  * Return: 0 on success, <0 on failure.
91018901357SAlexander Usyskin  */
91118901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl)
91218901357SAlexander Usyskin {
91318901357SAlexander Usyskin 	struct mei_device *dev;
91418901357SAlexander Usyskin 	int rets;
91518901357SAlexander Usyskin 
91618901357SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
91718901357SAlexander Usyskin 		return -ENODEV;
91818901357SAlexander Usyskin 
91918901357SAlexander Usyskin 	dev = cl->dev;
92018901357SAlexander Usyskin 
92118901357SAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
92218901357SAlexander Usyskin 
92318901357SAlexander Usyskin 	if (!mei_cl_is_connected(cl))
92418901357SAlexander Usyskin 		return 0;
92518901357SAlexander Usyskin 
92618901357SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
92718901357SAlexander Usyskin 		mei_cl_set_disconnected(cl);
92818901357SAlexander Usyskin 		return 0;
92918901357SAlexander Usyskin 	}
93018901357SAlexander Usyskin 
93118901357SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
93218901357SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
93318901357SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
93418901357SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
93518901357SAlexander Usyskin 		return rets;
93618901357SAlexander Usyskin 	}
93718901357SAlexander Usyskin 
93818901357SAlexander Usyskin 	rets = __mei_cl_disconnect(cl);
93918901357SAlexander Usyskin 
94004bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
9412bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
9422bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
94304bb139aSTomas Winkler 
9449ca9050bSTomas Winkler 	return rets;
9459ca9050bSTomas Winkler }
9469ca9050bSTomas Winkler 
9479ca9050bSTomas Winkler 
9489ca9050bSTomas Winkler /**
94990e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
95090e0b5f1STomas Winkler  *    client with the same me client id is connecting
9519ca9050bSTomas Winkler  *
9529ca9050bSTomas Winkler  * @cl: private data of the file object
9539ca9050bSTomas Winkler  *
954a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
9559ca9050bSTomas Winkler  */
9560c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl)
9579ca9050bSTomas Winkler {
95890e0b5f1STomas Winkler 	struct mei_device *dev;
9590c53357cSTomas Winkler 	struct mei_cl_cb *cb;
96090e0b5f1STomas Winkler 
96190e0b5f1STomas Winkler 	dev = cl->dev;
96290e0b5f1STomas Winkler 
9630c53357cSTomas Winkler 	list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
9640c53357cSTomas Winkler 		if (cb->fop_type == MEI_FOP_CONNECT &&
965d49ed64aSAlexander Usyskin 		    mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
96690e0b5f1STomas Winkler 			return true;
9679ca9050bSTomas Winkler 	}
96890e0b5f1STomas Winkler 
96990e0b5f1STomas Winkler 	return false;
9709ca9050bSTomas Winkler }
9719ca9050bSTomas Winkler 
9729ca9050bSTomas Winkler /**
9730c53357cSTomas Winkler  * mei_cl_send_connect - send connect request
9740c53357cSTomas Winkler  *
9750c53357cSTomas Winkler  * @cl: host client
9760c53357cSTomas Winkler  * @cb: callback block
9770c53357cSTomas Winkler  *
9780c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
9790c53357cSTomas Winkler  */
9800c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
9810c53357cSTomas Winkler {
9820c53357cSTomas Winkler 	struct mei_device *dev;
9830c53357cSTomas Winkler 	int ret;
9840c53357cSTomas Winkler 
9850c53357cSTomas Winkler 	dev = cl->dev;
9860c53357cSTomas Winkler 
9870c53357cSTomas Winkler 	ret = mei_hbm_cl_connect_req(dev, cl);
9880c53357cSTomas Winkler 	cl->status = ret;
9890c53357cSTomas Winkler 	if (ret) {
9900c53357cSTomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
9910c53357cSTomas Winkler 		return ret;
9920c53357cSTomas Winkler 	}
9930c53357cSTomas Winkler 
9940c53357cSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
9950c53357cSTomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
9960c53357cSTomas Winkler 	return 0;
9970c53357cSTomas Winkler }
9980c53357cSTomas Winkler 
9990c53357cSTomas Winkler /**
10000c53357cSTomas Winkler  * mei_cl_irq_connect - send connect request in irq_thread context
10010c53357cSTomas Winkler  *
10020c53357cSTomas Winkler  * @cl: host client
10030c53357cSTomas Winkler  * @cb: callback block
10040c53357cSTomas Winkler  * @cmpl_list: complete list
10050c53357cSTomas Winkler  *
10060c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10070c53357cSTomas Winkler  */
10080c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
10090c53357cSTomas Winkler 			      struct mei_cl_cb *cmpl_list)
10100c53357cSTomas Winkler {
10110c53357cSTomas Winkler 	struct mei_device *dev = cl->dev;
10120c53357cSTomas Winkler 	u32 msg_slots;
10130c53357cSTomas Winkler 	int slots;
10140c53357cSTomas Winkler 	int rets;
10150c53357cSTomas Winkler 
10160c53357cSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
10170c53357cSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
10180c53357cSTomas Winkler 
10190c53357cSTomas Winkler 	if (mei_cl_is_other_connecting(cl))
10200c53357cSTomas Winkler 		return 0;
10210c53357cSTomas Winkler 
10220c53357cSTomas Winkler 	if (slots < msg_slots)
10230c53357cSTomas Winkler 		return -EMSGSIZE;
10240c53357cSTomas Winkler 
10250c53357cSTomas Winkler 	rets = mei_cl_send_connect(cl, cb);
10260c53357cSTomas Winkler 	if (rets)
10270c53357cSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
10280c53357cSTomas Winkler 
10290c53357cSTomas Winkler 	return rets;
10300c53357cSTomas Winkler }
10310c53357cSTomas Winkler 
10320c53357cSTomas Winkler /**
103383ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
10349f81abdaSTomas Winkler  *
10359f81abdaSTomas Winkler  * @cl: host client
1036d49ed64aSAlexander Usyskin  * @me_cl: me client
1037a8605ea2SAlexander Usyskin  * @file: pointer to file structure
10389f81abdaSTomas Winkler  *
10399f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
10409f81abdaSTomas Winkler  *
1041a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
10429f81abdaSTomas Winkler  */
1043d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
1044f23e2cc4STomas Winkler 		  const struct file *file)
10459f81abdaSTomas Winkler {
10469f81abdaSTomas Winkler 	struct mei_device *dev;
10479f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
10489f81abdaSTomas Winkler 	int rets;
10499f81abdaSTomas Winkler 
10501df629efSAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev || !me_cl))
10519f81abdaSTomas Winkler 		return -ENODEV;
10529f81abdaSTomas Winkler 
10539f81abdaSTomas Winkler 	dev = cl->dev;
10549f81abdaSTomas Winkler 
10551df629efSAlexander Usyskin 	rets = mei_cl_set_connecting(cl, me_cl);
10561df629efSAlexander Usyskin 	if (rets)
10571df629efSAlexander Usyskin 		return rets;
10581df629efSAlexander Usyskin 
10591df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
10601df629efSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTED;
10611df629efSAlexander Usyskin 		return 0;
10621df629efSAlexander Usyskin 	}
10631df629efSAlexander Usyskin 
10642bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
106504bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
10662bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
106704bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
10681df629efSAlexander Usyskin 		goto nortpm;
106904bb139aSTomas Winkler 	}
107004bb139aSTomas Winkler 
1071bca67d68STomas Winkler 	cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
1072bca67d68STomas Winkler 	rets = cb ? 0 : -ENOMEM;
1073bca67d68STomas Winkler 	if (rets)
10749f81abdaSTomas Winkler 		goto out;
10759f81abdaSTomas Winkler 
10760c53357cSTomas Winkler 	list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
10770c53357cSTomas Winkler 
10786aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
10796aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
10800c53357cSTomas Winkler 		rets = mei_cl_send_connect(cl, cb);
10810c53357cSTomas Winkler 		if (rets)
10829f81abdaSTomas Winkler 			goto out;
10839f81abdaSTomas Winkler 	}
10849f81abdaSTomas Winkler 
10859f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
108612f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
10879f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
108818901357SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
10893c666182STomas Winkler 			 cl->state == MEI_FILE_DISCONNECT_REPLY),
1090206ecfc2SFrode Isaksen 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
10919f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
10929f81abdaSTomas Winkler 
1093f3de9b63STomas Winkler 	if (!mei_cl_is_connected(cl)) {
109418901357SAlexander Usyskin 		if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
109518901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_rd_list, cl);
109618901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_wr_list, cl);
109718901357SAlexander Usyskin 			 /* ignore disconnect return valuue;
109818901357SAlexander Usyskin 			  * in case of failure reset will be invoked
109918901357SAlexander Usyskin 			  */
110018901357SAlexander Usyskin 			__mei_cl_disconnect(cl);
110118901357SAlexander Usyskin 			rets = -EFAULT;
110218901357SAlexander Usyskin 			goto out;
110318901357SAlexander Usyskin 		}
110418901357SAlexander Usyskin 
11050c53357cSTomas Winkler 		/* timeout or something went really wrong */
1106285e2996SAlexander Usyskin 		if (!cl->status)
1107285e2996SAlexander Usyskin 			cl->status = -EFAULT;
11089f81abdaSTomas Winkler 	}
11099f81abdaSTomas Winkler 
11109f81abdaSTomas Winkler 	rets = cl->status;
11119f81abdaSTomas Winkler out:
111204bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
11132bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
11142bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
111504bb139aSTomas Winkler 
11169f81abdaSTomas Winkler 	mei_io_cb_free(cb);
11170c53357cSTomas Winkler 
11181df629efSAlexander Usyskin nortpm:
11190c53357cSTomas Winkler 	if (!mei_cl_is_connected(cl))
11200c53357cSTomas Winkler 		mei_cl_set_disconnected(cl);
11210c53357cSTomas Winkler 
11229f81abdaSTomas Winkler 	return rets;
11239f81abdaSTomas Winkler }
11249f81abdaSTomas Winkler 
11259f81abdaSTomas Winkler /**
112603b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
112703b8d341STomas Winkler  *
112803b8d341STomas Winkler  * @dev: the device structure
112903b8d341STomas Winkler  *
113003b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
113103b8d341STomas Winkler  */
11327851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
113303b8d341STomas Winkler {
113403b8d341STomas Winkler 	struct mei_cl *cl;
113503b8d341STomas Winkler 	int ret;
113603b8d341STomas Winkler 
113703b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
113803b8d341STomas Winkler 	if (!cl) {
113903b8d341STomas Winkler 		ret = -ENOMEM;
114003b8d341STomas Winkler 		goto err;
114103b8d341STomas Winkler 	}
114203b8d341STomas Winkler 
11437851e008SAlexander Usyskin 	ret = mei_cl_link(cl);
114403b8d341STomas Winkler 	if (ret)
114503b8d341STomas Winkler 		goto err;
114603b8d341STomas Winkler 
114703b8d341STomas Winkler 	return cl;
114803b8d341STomas Winkler err:
114903b8d341STomas Winkler 	kfree(cl);
115003b8d341STomas Winkler 	return ERR_PTR(ret);
115103b8d341STomas Winkler }
115203b8d341STomas Winkler 
115303b8d341STomas Winkler 
115403b8d341STomas Winkler 
115503b8d341STomas Winkler /**
115690e0b5f1STomas Winkler  * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
11579ca9050bSTomas Winkler  *
115806ee536bSAlexander Usyskin  * @cl: host client
115906ee536bSAlexander Usyskin  * @fp: the file pointer associated with the pointer
11609ca9050bSTomas Winkler  *
1161a8605ea2SAlexander Usyskin  * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
11629ca9050bSTomas Winkler  */
116306ee536bSAlexander Usyskin static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp)
11649ca9050bSTomas Winkler {
11651df629efSAlexander Usyskin 	int rets;
11661df629efSAlexander Usyskin 
1167d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
116890e0b5f1STomas Winkler 		return -EINVAL;
116990e0b5f1STomas Winkler 
11709ca9050bSTomas Winkler 	if (cl->mei_flow_ctrl_creds > 0)
11719ca9050bSTomas Winkler 		return 1;
11729ca9050bSTomas Winkler 
11731df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
117406ee536bSAlexander Usyskin 		rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp);
11751df629efSAlexander Usyskin 		if (rets && rets != -EBUSY)
11761df629efSAlexander Usyskin 			return rets;
11771df629efSAlexander Usyskin 		return 1;
11781df629efSAlexander Usyskin 	}
11791df629efSAlexander Usyskin 
1180d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
1181d49ed64aSAlexander Usyskin 		if (cl->me_cl->mei_flow_ctrl_creds > 0)
1182d49ed64aSAlexander Usyskin 			return 1;
118312d00665SAlexander Usyskin 	}
1184d49ed64aSAlexander Usyskin 	return 0;
11859ca9050bSTomas Winkler }
11869ca9050bSTomas Winkler 
11879ca9050bSTomas Winkler /**
118890e0b5f1STomas Winkler  * mei_cl_flow_ctrl_reduce - reduces flow_control.
11899ca9050bSTomas Winkler  *
11909ca9050bSTomas Winkler  * @cl: private data of the file object
1191393b148fSMasanari Iida  *
1192a8605ea2SAlexander Usyskin  * Return:
11939ca9050bSTomas Winkler  *	0 on success
11949ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
11959ca9050bSTomas Winkler  */
1196fdd9b865SAlexander Usyskin static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
11979ca9050bSTomas Winkler {
1198d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
119990e0b5f1STomas Winkler 		return -EINVAL;
120090e0b5f1STomas Winkler 
12011df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12021df629efSAlexander Usyskin 		return 0;
12031df629efSAlexander Usyskin 
1204d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
1205d49ed64aSAlexander Usyskin 		if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0))
1206d49ed64aSAlexander Usyskin 			return -EINVAL;
1207d49ed64aSAlexander Usyskin 		cl->me_cl->mei_flow_ctrl_creds--;
12089ca9050bSTomas Winkler 	} else {
1209d49ed64aSAlexander Usyskin 		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
1210d49ed64aSAlexander Usyskin 			return -EINVAL;
12119ca9050bSTomas Winkler 		cl->mei_flow_ctrl_creds--;
12129ca9050bSTomas Winkler 	}
1213d49ed64aSAlexander Usyskin 	return 0;
12149ca9050bSTomas Winkler }
12159ca9050bSTomas Winkler 
12169ca9050bSTomas Winkler /**
121751678ccbSTomas Winkler  *  mei_cl_notify_fop2req - convert fop to proper request
121851678ccbSTomas Winkler  *
121951678ccbSTomas Winkler  * @fop: client notification start response command
122051678ccbSTomas Winkler  *
122151678ccbSTomas Winkler  * Return:  MEI_HBM_NOTIFICATION_START/STOP
122251678ccbSTomas Winkler  */
122351678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
122451678ccbSTomas Winkler {
122551678ccbSTomas Winkler 	if (fop == MEI_FOP_NOTIFY_START)
122651678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_START;
122751678ccbSTomas Winkler 	else
122851678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_STOP;
122951678ccbSTomas Winkler }
123051678ccbSTomas Winkler 
123151678ccbSTomas Winkler /**
123251678ccbSTomas Winkler  *  mei_cl_notify_req2fop - convert notification request top file operation type
123351678ccbSTomas Winkler  *
123451678ccbSTomas Winkler  * @req: hbm notification request type
123551678ccbSTomas Winkler  *
123651678ccbSTomas Winkler  * Return:  MEI_FOP_NOTIFY_START/STOP
123751678ccbSTomas Winkler  */
123851678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
123951678ccbSTomas Winkler {
124051678ccbSTomas Winkler 	if (req == MEI_HBM_NOTIFICATION_START)
124151678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_START;
124251678ccbSTomas Winkler 	else
124351678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_STOP;
124451678ccbSTomas Winkler }
124551678ccbSTomas Winkler 
124651678ccbSTomas Winkler /**
124751678ccbSTomas Winkler  * mei_cl_irq_notify - send notification request in irq_thread context
124851678ccbSTomas Winkler  *
124951678ccbSTomas Winkler  * @cl: client
125051678ccbSTomas Winkler  * @cb: callback block.
125151678ccbSTomas Winkler  * @cmpl_list: complete list.
125251678ccbSTomas Winkler  *
125351678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
125451678ccbSTomas Winkler  */
125551678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
125651678ccbSTomas Winkler 		      struct mei_cl_cb *cmpl_list)
125751678ccbSTomas Winkler {
125851678ccbSTomas Winkler 	struct mei_device *dev = cl->dev;
125951678ccbSTomas Winkler 	u32 msg_slots;
126051678ccbSTomas Winkler 	int slots;
126151678ccbSTomas Winkler 	int ret;
126251678ccbSTomas Winkler 	bool request;
126351678ccbSTomas Winkler 
126451678ccbSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
126551678ccbSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
126651678ccbSTomas Winkler 
126751678ccbSTomas Winkler 	if (slots < msg_slots)
126851678ccbSTomas Winkler 		return -EMSGSIZE;
126951678ccbSTomas Winkler 
127051678ccbSTomas Winkler 	request = mei_cl_notify_fop2req(cb->fop_type);
127151678ccbSTomas Winkler 	ret = mei_hbm_cl_notify_req(dev, cl, request);
127251678ccbSTomas Winkler 	if (ret) {
127351678ccbSTomas Winkler 		cl->status = ret;
127451678ccbSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
127551678ccbSTomas Winkler 		return ret;
127651678ccbSTomas Winkler 	}
127751678ccbSTomas Winkler 
127851678ccbSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
127951678ccbSTomas Winkler 	return 0;
128051678ccbSTomas Winkler }
128151678ccbSTomas Winkler 
128251678ccbSTomas Winkler /**
128351678ccbSTomas Winkler  * mei_cl_notify_request - send notification stop/start request
128451678ccbSTomas Winkler  *
128551678ccbSTomas Winkler  * @cl: host client
128651678ccbSTomas Winkler  * @file: associate request with file
128751678ccbSTomas Winkler  * @request: 1 for start or 0 for stop
128851678ccbSTomas Winkler  *
128951678ccbSTomas Winkler  * Locking: called under "dev->device_lock" lock
129051678ccbSTomas Winkler  *
129151678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
129251678ccbSTomas Winkler  */
1293f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl,
1294f23e2cc4STomas Winkler 			  const struct file *file, u8 request)
129551678ccbSTomas Winkler {
129651678ccbSTomas Winkler 	struct mei_device *dev;
129751678ccbSTomas Winkler 	struct mei_cl_cb *cb;
129851678ccbSTomas Winkler 	enum mei_cb_file_ops fop_type;
129951678ccbSTomas Winkler 	int rets;
130051678ccbSTomas Winkler 
130151678ccbSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
130251678ccbSTomas Winkler 		return -ENODEV;
130351678ccbSTomas Winkler 
130451678ccbSTomas Winkler 	dev = cl->dev;
130551678ccbSTomas Winkler 
130651678ccbSTomas Winkler 	if (!dev->hbm_f_ev_supported) {
130751678ccbSTomas Winkler 		cl_dbg(dev, cl, "notifications not supported\n");
130851678ccbSTomas Winkler 		return -EOPNOTSUPP;
130951678ccbSTomas Winkler 	}
131051678ccbSTomas Winkler 
131151678ccbSTomas Winkler 	rets = pm_runtime_get(dev->dev);
131251678ccbSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
131351678ccbSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
131451678ccbSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
131551678ccbSTomas Winkler 		return rets;
131651678ccbSTomas Winkler 	}
131751678ccbSTomas Winkler 
131851678ccbSTomas Winkler 	fop_type = mei_cl_notify_req2fop(request);
131951678ccbSTomas Winkler 	cb = mei_io_cb_init(cl, fop_type, file);
132051678ccbSTomas Winkler 	if (!cb) {
132151678ccbSTomas Winkler 		rets = -ENOMEM;
132251678ccbSTomas Winkler 		goto out;
132351678ccbSTomas Winkler 	}
132451678ccbSTomas Winkler 
132551678ccbSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
132651678ccbSTomas Winkler 		if (mei_hbm_cl_notify_req(dev, cl, request)) {
132751678ccbSTomas Winkler 			rets = -ENODEV;
132851678ccbSTomas Winkler 			goto out;
132951678ccbSTomas Winkler 		}
133051678ccbSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
133151678ccbSTomas Winkler 	} else {
133251678ccbSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
133351678ccbSTomas Winkler 	}
133451678ccbSTomas Winkler 
133551678ccbSTomas Winkler 	mutex_unlock(&dev->device_lock);
133651678ccbSTomas Winkler 	wait_event_timeout(cl->wait, cl->notify_en == request,
133751678ccbSTomas Winkler 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
133851678ccbSTomas Winkler 	mutex_lock(&dev->device_lock);
133951678ccbSTomas Winkler 
134051678ccbSTomas Winkler 	if (cl->notify_en != request) {
134151678ccbSTomas Winkler 		mei_io_list_flush(&dev->ctrl_rd_list, cl);
134251678ccbSTomas Winkler 		mei_io_list_flush(&dev->ctrl_wr_list, cl);
134351678ccbSTomas Winkler 		if (!cl->status)
134451678ccbSTomas Winkler 			cl->status = -EFAULT;
134551678ccbSTomas Winkler 	}
134651678ccbSTomas Winkler 
134751678ccbSTomas Winkler 	rets = cl->status;
134851678ccbSTomas Winkler 
134951678ccbSTomas Winkler out:
135051678ccbSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
135151678ccbSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
135251678ccbSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
135351678ccbSTomas Winkler 
135451678ccbSTomas Winkler 	mei_io_cb_free(cb);
135551678ccbSTomas Winkler 	return rets;
135651678ccbSTomas Winkler }
135751678ccbSTomas Winkler 
135851678ccbSTomas Winkler /**
1359237092bfSTomas Winkler  * mei_cl_notify - raise notification
1360237092bfSTomas Winkler  *
1361237092bfSTomas Winkler  * @cl: host client
1362237092bfSTomas Winkler  *
1363237092bfSTomas Winkler  * Locking: called under "dev->device_lock" lock
1364237092bfSTomas Winkler  */
1365237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl)
1366237092bfSTomas Winkler {
1367237092bfSTomas Winkler 	struct mei_device *dev;
1368237092bfSTomas Winkler 
1369237092bfSTomas Winkler 	if (!cl || !cl->dev)
1370237092bfSTomas Winkler 		return;
1371237092bfSTomas Winkler 
1372237092bfSTomas Winkler 	dev = cl->dev;
1373237092bfSTomas Winkler 
1374237092bfSTomas Winkler 	if (!cl->notify_en)
1375237092bfSTomas Winkler 		return;
1376237092bfSTomas Winkler 
1377237092bfSTomas Winkler 	cl_dbg(dev, cl, "notify event");
1378237092bfSTomas Winkler 	cl->notify_ev = true;
1379850f8940STomas Winkler 	if (!mei_cl_bus_notify_event(cl))
1380850f8940STomas Winkler 		wake_up_interruptible(&cl->ev_wait);
1381237092bfSTomas Winkler 
1382237092bfSTomas Winkler 	if (cl->ev_async)
1383237092bfSTomas Winkler 		kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
1384bb2ef9c3SAlexander Usyskin 
1385237092bfSTomas Winkler }
1386237092bfSTomas Winkler 
1387237092bfSTomas Winkler /**
1388b38a362fSTomas Winkler  * mei_cl_notify_get - get or wait for notification event
1389b38a362fSTomas Winkler  *
1390b38a362fSTomas Winkler  * @cl: host client
1391b38a362fSTomas Winkler  * @block: this request is blocking
1392b38a362fSTomas Winkler  * @notify_ev: true if notification event was received
1393b38a362fSTomas Winkler  *
1394b38a362fSTomas Winkler  * Locking: called under "dev->device_lock" lock
1395b38a362fSTomas Winkler  *
1396b38a362fSTomas Winkler  * Return: 0 on such and error otherwise.
1397b38a362fSTomas Winkler  */
1398b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
1399b38a362fSTomas Winkler {
1400b38a362fSTomas Winkler 	struct mei_device *dev;
1401b38a362fSTomas Winkler 	int rets;
1402b38a362fSTomas Winkler 
1403b38a362fSTomas Winkler 	*notify_ev = false;
1404b38a362fSTomas Winkler 
1405b38a362fSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1406b38a362fSTomas Winkler 		return -ENODEV;
1407b38a362fSTomas Winkler 
1408b38a362fSTomas Winkler 	dev = cl->dev;
1409b38a362fSTomas Winkler 
1410b38a362fSTomas Winkler 	if (!mei_cl_is_connected(cl))
1411b38a362fSTomas Winkler 		return -ENODEV;
1412b38a362fSTomas Winkler 
1413b38a362fSTomas Winkler 	if (cl->notify_ev)
1414b38a362fSTomas Winkler 		goto out;
1415b38a362fSTomas Winkler 
1416b38a362fSTomas Winkler 	if (!block)
1417b38a362fSTomas Winkler 		return -EAGAIN;
1418b38a362fSTomas Winkler 
1419b38a362fSTomas Winkler 	mutex_unlock(&dev->device_lock);
1420b38a362fSTomas Winkler 	rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev);
1421b38a362fSTomas Winkler 	mutex_lock(&dev->device_lock);
1422b38a362fSTomas Winkler 
1423b38a362fSTomas Winkler 	if (rets < 0)
1424b38a362fSTomas Winkler 		return rets;
1425b38a362fSTomas Winkler 
1426b38a362fSTomas Winkler out:
1427b38a362fSTomas Winkler 	*notify_ev = cl->notify_ev;
1428b38a362fSTomas Winkler 	cl->notify_ev = false;
1429b38a362fSTomas Winkler 	return 0;
1430b38a362fSTomas Winkler }
1431b38a362fSTomas Winkler 
1432b38a362fSTomas Winkler /**
143313cf9885SAlexander Usyskin  * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control
143413cf9885SAlexander Usyskin  *                        for given host client
143513cf9885SAlexander Usyskin  *
143613cf9885SAlexander Usyskin  * @cl: host client
143713cf9885SAlexander Usyskin  *
143813cf9885SAlexander Usyskin  * Return: true, if found at least one cb.
143913cf9885SAlexander Usyskin  */
144013cf9885SAlexander Usyskin static bool mei_cl_is_read_fc_cb(struct mei_cl *cl)
144113cf9885SAlexander Usyskin {
144213cf9885SAlexander Usyskin 	struct mei_device *dev = cl->dev;
144313cf9885SAlexander Usyskin 	struct mei_cl_cb *cb;
144413cf9885SAlexander Usyskin 
144513cf9885SAlexander Usyskin 	list_for_each_entry(cb, &dev->ctrl_wr_list.list, list)
144613cf9885SAlexander Usyskin 		if (cb->fop_type == MEI_FOP_READ && cb->cl == cl)
144713cf9885SAlexander Usyskin 			return true;
144813cf9885SAlexander Usyskin 	return false;
144913cf9885SAlexander Usyskin }
145013cf9885SAlexander Usyskin 
145113cf9885SAlexander Usyskin /**
1452393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
14539ca9050bSTomas Winkler  *
145490e0b5f1STomas Winkler  * @cl: host client
1455ce23139cSAlexander Usyskin  * @length: number of bytes to read
1456bca67d68STomas Winkler  * @fp: pointer to file structure
14579ca9050bSTomas Winkler  *
1458a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
14599ca9050bSTomas Winkler  */
1460f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
14619ca9050bSTomas Winkler {
146290e0b5f1STomas Winkler 	struct mei_device *dev;
14639ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
14649ca9050bSTomas Winkler 	int rets;
14659ca9050bSTomas Winkler 
146690e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
146790e0b5f1STomas Winkler 		return -ENODEV;
146890e0b5f1STomas Winkler 
146990e0b5f1STomas Winkler 	dev = cl->dev;
147090e0b5f1STomas Winkler 
1471b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
14729ca9050bSTomas Winkler 		return -ENODEV;
14739ca9050bSTomas Winkler 
1474a9bed610STomas Winkler 	/* HW currently supports only one pending read */
147513cf9885SAlexander Usyskin 	if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl))
14769ca9050bSTomas Winkler 		return -EBUSY;
1477a9bed610STomas Winkler 
1478d49ed64aSAlexander Usyskin 	if (!mei_me_cl_is_active(cl->me_cl)) {
1479d49ed64aSAlexander Usyskin 		cl_err(dev, cl, "no such me client\n");
14807ca96aa2SAlexander Usyskin 		return  -ENOTTY;
14819ca9050bSTomas Winkler 	}
14821df629efSAlexander Usyskin 
148379563db9STomas Winkler 	/* always allocate at least client max message */
1484d49ed64aSAlexander Usyskin 	length = max_t(size_t, length, mei_cl_mtu(cl));
14851df629efSAlexander Usyskin 	cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
14861df629efSAlexander Usyskin 	if (!cb)
14871df629efSAlexander Usyskin 		return -ENOMEM;
14881df629efSAlexander Usyskin 
14891df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
14901df629efSAlexander Usyskin 		list_add_tail(&cb->list, &cl->rd_pending);
14911df629efSAlexander Usyskin 		return 0;
14921df629efSAlexander Usyskin 	}
14939ca9050bSTomas Winkler 
14942bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
149504bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
14962bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
149704bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
14981df629efSAlexander Usyskin 		goto nortpm;
149904bb139aSTomas Winkler 	}
150004bb139aSTomas Winkler 
15016aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
150286113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
150386113500SAlexander Usyskin 		if (rets < 0)
150404bb139aSTomas Winkler 			goto out;
150504bb139aSTomas Winkler 
1506a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_pending);
15079ca9050bSTomas Winkler 	} else {
15081df629efSAlexander Usyskin 		rets = 0;
15099ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
15109ca9050bSTomas Winkler 	}
1511accb884bSChao Bi 
151204bb139aSTomas Winkler out:
151304bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
15142bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
15152bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
15161df629efSAlexander Usyskin nortpm:
151704bb139aSTomas Winkler 	if (rets)
15189ca9050bSTomas Winkler 		mei_io_cb_free(cb);
151904bb139aSTomas Winkler 
15209ca9050bSTomas Winkler 	return rets;
15219ca9050bSTomas Winkler }
15229ca9050bSTomas Winkler 
1523074b4c01STomas Winkler /**
15249d098192STomas Winkler  * mei_cl_irq_write - write a message to device
152521767546STomas Winkler  *	from the interrupt thread context
152621767546STomas Winkler  *
152721767546STomas Winkler  * @cl: client
152821767546STomas Winkler  * @cb: callback block.
152921767546STomas Winkler  * @cmpl_list: complete list.
153021767546STomas Winkler  *
1531a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
153221767546STomas Winkler  */
15339d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
15349d098192STomas Winkler 		     struct mei_cl_cb *cmpl_list)
153521767546STomas Winkler {
1536136698e5STomas Winkler 	struct mei_device *dev;
1537136698e5STomas Winkler 	struct mei_msg_data *buf;
153821767546STomas Winkler 	struct mei_msg_hdr mei_hdr;
1539136698e5STomas Winkler 	size_t len;
1540136698e5STomas Winkler 	u32 msg_slots;
15419d098192STomas Winkler 	int slots;
15422ebf8c94STomas Winkler 	int rets;
1543b8b73035SAlexander Usyskin 	bool first_chunk;
154421767546STomas Winkler 
1545136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1546136698e5STomas Winkler 		return -ENODEV;
1547136698e5STomas Winkler 
1548136698e5STomas Winkler 	dev = cl->dev;
1549136698e5STomas Winkler 
15505db7514dSTomas Winkler 	buf = &cb->buf;
1551136698e5STomas Winkler 
1552b8b73035SAlexander Usyskin 	first_chunk = cb->buf_idx == 0;
1553b8b73035SAlexander Usyskin 
155406ee536bSAlexander Usyskin 	rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1;
1555136698e5STomas Winkler 	if (rets < 0)
1556136698e5STomas Winkler 		return rets;
1557136698e5STomas Winkler 
1558136698e5STomas Winkler 	if (rets == 0) {
1559136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1560136698e5STomas Winkler 		return 0;
1561136698e5STomas Winkler 	}
1562136698e5STomas Winkler 
15639d098192STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1564136698e5STomas Winkler 	len = buf->size - cb->buf_idx;
1565136698e5STomas Winkler 	msg_slots = mei_data2slots(len);
1566136698e5STomas Winkler 
15671df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1568d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
156921767546STomas Winkler 	mei_hdr.reserved = 0;
1570479327fcSTomas Winkler 	mei_hdr.internal = cb->internal;
157121767546STomas Winkler 
15729d098192STomas Winkler 	if (slots >= msg_slots) {
157321767546STomas Winkler 		mei_hdr.length = len;
157421767546STomas Winkler 		mei_hdr.msg_complete = 1;
157521767546STomas Winkler 	/* Split the message only if we can write the whole host buffer */
15769d098192STomas Winkler 	} else if (slots == dev->hbuf_depth) {
15779d098192STomas Winkler 		msg_slots = slots;
15789d098192STomas Winkler 		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
157921767546STomas Winkler 		mei_hdr.length = len;
158021767546STomas Winkler 		mei_hdr.msg_complete = 0;
158121767546STomas Winkler 	} else {
158221767546STomas Winkler 		/* wait for next time the host buffer is empty */
158321767546STomas Winkler 		return 0;
158421767546STomas Winkler 	}
158521767546STomas Winkler 
158635bf7692SAlexander Usyskin 	cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
15875db7514dSTomas Winkler 			cb->buf.size, cb->buf_idx);
158821767546STomas Winkler 
1589136698e5STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
15902ebf8c94STomas Winkler 	if (rets) {
15912ebf8c94STomas Winkler 		cl->status = rets;
159221767546STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
15932ebf8c94STomas Winkler 		return rets;
159421767546STomas Winkler 	}
159521767546STomas Winkler 
159621767546STomas Winkler 	cl->status = 0;
15974dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
159821767546STomas Winkler 	cb->buf_idx += mei_hdr.length;
15998660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
16004dfaa9f7STomas Winkler 
1601b8b73035SAlexander Usyskin 	if (first_chunk) {
160221767546STomas Winkler 		if (mei_cl_flow_ctrl_reduce(cl))
16032ebf8c94STomas Winkler 			return -EIO;
160421767546STomas Winkler 	}
160521767546STomas Winkler 
1606b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
1607b8b73035SAlexander Usyskin 		list_move_tail(&cb->list, &dev->write_waiting_list.list);
1608b8b73035SAlexander Usyskin 
160921767546STomas Winkler 	return 0;
161021767546STomas Winkler }
161121767546STomas Winkler 
161221767546STomas Winkler /**
16134234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1614a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
16154234a6deSTomas Winkler  *
16164234a6deSTomas Winkler  * @cl: host client
1617a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
1618ce23139cSAlexander Usyskin  * @blocking: block until completed
16194234a6deSTomas Winkler  *
1620a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
16214234a6deSTomas Winkler  */
16224234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
16234234a6deSTomas Winkler {
16244234a6deSTomas Winkler 	struct mei_device *dev;
16254234a6deSTomas Winkler 	struct mei_msg_data *buf;
16264234a6deSTomas Winkler 	struct mei_msg_hdr mei_hdr;
162723253c31SAlexander Usyskin 	int size;
16284234a6deSTomas Winkler 	int rets;
16294234a6deSTomas Winkler 
16304234a6deSTomas Winkler 
16314234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
16324234a6deSTomas Winkler 		return -ENODEV;
16334234a6deSTomas Winkler 
16344234a6deSTomas Winkler 	if (WARN_ON(!cb))
16354234a6deSTomas Winkler 		return -EINVAL;
16364234a6deSTomas Winkler 
16374234a6deSTomas Winkler 	dev = cl->dev;
16384234a6deSTomas Winkler 
16395db7514dSTomas Winkler 	buf = &cb->buf;
164023253c31SAlexander Usyskin 	size = buf->size;
16414234a6deSTomas Winkler 
164223253c31SAlexander Usyskin 	cl_dbg(dev, cl, "size=%d\n", size);
16434234a6deSTomas Winkler 
16442bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
164504bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
16462bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
164704bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
16486cbb097fSAlexander Usyskin 		goto free;
164904bb139aSTomas Winkler 	}
16504234a6deSTomas Winkler 
16516aae48ffSTomas Winkler 	cb->buf_idx = 0;
16526aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
16536aae48ffSTomas Winkler 
16541df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1655d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
16566aae48ffSTomas Winkler 	mei_hdr.reserved = 0;
16576aae48ffSTomas Winkler 	mei_hdr.msg_complete = 0;
16586aae48ffSTomas Winkler 	mei_hdr.internal = cb->internal;
16594234a6deSTomas Winkler 
166006ee536bSAlexander Usyskin 	rets = mei_cl_flow_ctrl_creds(cl, cb->fp);
16614234a6deSTomas Winkler 	if (rets < 0)
16624234a6deSTomas Winkler 		goto err;
16634234a6deSTomas Winkler 
16646aae48ffSTomas Winkler 	if (rets == 0) {
16656aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
166623253c31SAlexander Usyskin 		rets = size;
16674234a6deSTomas Winkler 		goto out;
16684234a6deSTomas Winkler 	}
16696aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
16706aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
167123253c31SAlexander Usyskin 		rets = size;
16726aae48ffSTomas Winkler 		goto out;
16736aae48ffSTomas Winkler 	}
16744234a6deSTomas Winkler 
16754234a6deSTomas Winkler 	/* Check for a maximum length */
167623253c31SAlexander Usyskin 	if (size > mei_hbuf_max_len(dev)) {
16774234a6deSTomas Winkler 		mei_hdr.length = mei_hbuf_max_len(dev);
16784234a6deSTomas Winkler 		mei_hdr.msg_complete = 0;
16794234a6deSTomas Winkler 	} else {
168023253c31SAlexander Usyskin 		mei_hdr.length = size;
16814234a6deSTomas Winkler 		mei_hdr.msg_complete = 1;
16824234a6deSTomas Winkler 	}
16834234a6deSTomas Winkler 
16842ebf8c94STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data);
16852ebf8c94STomas Winkler 	if (rets)
16864234a6deSTomas Winkler 		goto err;
16874234a6deSTomas Winkler 
1688b8b73035SAlexander Usyskin 	rets = mei_cl_flow_ctrl_reduce(cl);
1689b8b73035SAlexander Usyskin 	if (rets)
1690b8b73035SAlexander Usyskin 		goto err;
1691b8b73035SAlexander Usyskin 
16924234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
16934234a6deSTomas Winkler 	cb->buf_idx = mei_hdr.length;
16948660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
16954234a6deSTomas Winkler 
16964234a6deSTomas Winkler out:
1697b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
16984234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_waiting_list.list);
1699b8b73035SAlexander Usyskin 	else
17004234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_list.list);
17014234a6deSTomas Winkler 
170223253c31SAlexander Usyskin 	cb = NULL;
17034234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
17044234a6deSTomas Winkler 
17054234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
17067ca96aa2SAlexander Usyskin 		rets = wait_event_interruptible(cl->tx_wait,
17070faf6a3bSAlexander Usyskin 				cl->writing_state == MEI_WRITE_COMPLETE ||
17080faf6a3bSAlexander Usyskin 				(!mei_cl_is_connected(cl)));
17097ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
17107ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
17117ca96aa2SAlexander Usyskin 		if (rets) {
17124234a6deSTomas Winkler 			if (signal_pending(current))
17134234a6deSTomas Winkler 				rets = -EINTR;
17147ca96aa2SAlexander Usyskin 			goto err;
17154234a6deSTomas Winkler 		}
17160faf6a3bSAlexander Usyskin 		if (cl->writing_state != MEI_WRITE_COMPLETE) {
17170faf6a3bSAlexander Usyskin 			rets = -EFAULT;
17180faf6a3bSAlexander Usyskin 			goto err;
17190faf6a3bSAlexander Usyskin 		}
17204234a6deSTomas Winkler 	}
17217ca96aa2SAlexander Usyskin 
172223253c31SAlexander Usyskin 	rets = size;
17234234a6deSTomas Winkler err:
172404bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
17252bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
17262bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
17276cbb097fSAlexander Usyskin free:
17286cbb097fSAlexander Usyskin 	mei_io_cb_free(cb);
172904bb139aSTomas Winkler 
17304234a6deSTomas Winkler 	return rets;
17314234a6deSTomas Winkler }
17324234a6deSTomas Winkler 
17334234a6deSTomas Winkler 
1734db086fa9STomas Winkler /**
1735db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
1736db086fa9STomas Winkler  *
1737db086fa9STomas Winkler  * @cl: private data of the file object.
1738db086fa9STomas Winkler  * @cb: callback block.
1739db086fa9STomas Winkler  */
1740db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
1741db086fa9STomas Winkler {
1742a1809d38SAlexander Usyskin 	struct mei_device *dev = cl->dev;
1743a1809d38SAlexander Usyskin 
17443c666182STomas Winkler 	switch (cb->fop_type) {
17453c666182STomas Winkler 	case MEI_FOP_WRITE:
1746db086fa9STomas Winkler 		mei_io_cb_free(cb);
1747db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
1748a1809d38SAlexander Usyskin 		if (waitqueue_active(&cl->tx_wait)) {
1749db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
1750a1809d38SAlexander Usyskin 		} else {
1751a1809d38SAlexander Usyskin 			pm_runtime_mark_last_busy(dev->dev);
1752a1809d38SAlexander Usyskin 			pm_request_autosuspend(dev->dev);
1753a1809d38SAlexander Usyskin 		}
17543c666182STomas Winkler 		break;
1755db086fa9STomas Winkler 
17563c666182STomas Winkler 	case MEI_FOP_READ:
1757a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_completed);
1758a1f9ae2bSTomas Winkler 		if (!mei_cl_bus_rx_event(cl))
1759a1f9ae2bSTomas Winkler 			wake_up_interruptible(&cl->rx_wait);
17603c666182STomas Winkler 		break;
1761db086fa9STomas Winkler 
17623c666182STomas Winkler 	case MEI_FOP_CONNECT:
17633c666182STomas Winkler 	case MEI_FOP_DISCONNECT:
176451678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_STOP:
176551678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_START:
17663c666182STomas Winkler 		if (waitqueue_active(&cl->wait))
17673c666182STomas Winkler 			wake_up(&cl->wait);
17683c666182STomas Winkler 
17693c666182STomas Winkler 		break;
17703c666182STomas Winkler 	default:
17713c666182STomas Winkler 		BUG_ON(0);
1772db086fa9STomas Winkler 	}
1773db086fa9STomas Winkler }
1774db086fa9STomas Winkler 
17754234a6deSTomas Winkler 
17764234a6deSTomas Winkler /**
1777074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
1778074b4c01STomas Winkler  *
1779a8605ea2SAlexander Usyskin  * @dev: mei device
1780074b4c01STomas Winkler  */
1781074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
1782074b4c01STomas Winkler {
178331f88f57STomas Winkler 	struct mei_cl *cl;
1784074b4c01STomas Winkler 
17853c666182STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link)
17863c666182STomas Winkler 		mei_cl_set_disconnected(cl);
1787074b4c01STomas Winkler }
1788