xref: /openbmc/linux/drivers/misc/mei/client.c (revision 9d27e73c)
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  */
3613030dc05STomas Winkler static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
3623030dc05STomas Winkler 					enum mei_cb_file_ops type,
363f23e2cc4STomas Winkler 					const struct file *fp)
3649ca9050bSTomas Winkler {
3659ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
3669ca9050bSTomas Winkler 
3679ca9050bSTomas Winkler 	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
3689ca9050bSTomas Winkler 	if (!cb)
3699ca9050bSTomas Winkler 		return NULL;
3709ca9050bSTomas Winkler 
371928fa666STomas Winkler 	INIT_LIST_HEAD(&cb->list);
37262e8e6adSTomas Winkler 	cb->fp = fp;
3739ca9050bSTomas Winkler 	cb->cl = cl;
3749ca9050bSTomas Winkler 	cb->buf_idx = 0;
375bca67d68STomas Winkler 	cb->fop_type = type;
3769ca9050bSTomas Winkler 	return cb;
3779ca9050bSTomas Winkler }
3789ca9050bSTomas Winkler 
3799ca9050bSTomas Winkler /**
380928fa666STomas Winkler  * __mei_io_list_flush - removes and frees cbs belonging to cl.
381928fa666STomas Winkler  *
382928fa666STomas Winkler  * @list:  an instance of our list structure
383928fa666STomas Winkler  * @cl:    host client, can be NULL for flushing the whole list
384928fa666STomas Winkler  * @free:  whether to free the cbs
385928fa666STomas Winkler  */
386928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list,
387928fa666STomas Winkler 				struct mei_cl *cl, bool free)
388928fa666STomas Winkler {
389928fa666STomas Winkler 	struct mei_cl_cb *cb, *next;
390928fa666STomas Winkler 
391928fa666STomas Winkler 	/* enable removing everything if no cl is specified */
392928fa666STomas Winkler 	list_for_each_entry_safe(cb, next, &list->list, list) {
393928fa666STomas Winkler 		if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
394928fa666STomas Winkler 			list_del_init(&cb->list);
395928fa666STomas Winkler 			if (free)
396928fa666STomas Winkler 				mei_io_cb_free(cb);
397928fa666STomas Winkler 		}
398928fa666STomas Winkler 	}
399928fa666STomas Winkler }
400928fa666STomas Winkler 
401928fa666STomas Winkler /**
402928fa666STomas Winkler  * mei_io_list_flush - removes list entry belonging to cl.
403928fa666STomas Winkler  *
404928fa666STomas Winkler  * @list:  An instance of our list structure
405928fa666STomas Winkler  * @cl: host client
406928fa666STomas Winkler  */
407928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
408928fa666STomas Winkler {
409928fa666STomas Winkler 	__mei_io_list_flush(list, cl, false);
410928fa666STomas Winkler }
411928fa666STomas Winkler 
412928fa666STomas Winkler /**
413928fa666STomas Winkler  * mei_io_list_free - removes cb belonging to cl and free them
414928fa666STomas Winkler  *
415928fa666STomas Winkler  * @list:  An instance of our list structure
416928fa666STomas Winkler  * @cl: host client
417928fa666STomas Winkler  */
418928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
419928fa666STomas Winkler {
420928fa666STomas Winkler 	__mei_io_list_flush(list, cl, true);
421928fa666STomas Winkler }
422928fa666STomas Winkler 
423928fa666STomas Winkler /**
424bca67d68STomas Winkler  * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
425bca67d68STomas Winkler  *
426bca67d68STomas Winkler  * @cl: host client
427bca67d68STomas Winkler  * @length: size of the buffer
428bca67d68STomas Winkler  * @type: operation type
429bca67d68STomas Winkler  * @fp: associated file pointer (might be NULL)
430bca67d68STomas Winkler  *
431bca67d68STomas Winkler  * Return: cb on success and NULL on failure
432bca67d68STomas Winkler  */
433bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
4343030dc05STomas Winkler 				  enum mei_cb_file_ops fop_type,
435f23e2cc4STomas Winkler 				  const struct file *fp)
436bca67d68STomas Winkler {
437bca67d68STomas Winkler 	struct mei_cl_cb *cb;
438bca67d68STomas Winkler 
4393030dc05STomas Winkler 	cb = mei_io_cb_init(cl, fop_type, fp);
440bca67d68STomas Winkler 	if (!cb)
441bca67d68STomas Winkler 		return NULL;
442bca67d68STomas Winkler 
443aab3b1a3SAlexander Usyskin 	if (length == 0)
444aab3b1a3SAlexander Usyskin 		return cb;
445aab3b1a3SAlexander Usyskin 
446aab3b1a3SAlexander Usyskin 	cb->buf.data = kmalloc(length, GFP_KERNEL);
447aab3b1a3SAlexander Usyskin 	if (!cb->buf.data) {
448bca67d68STomas Winkler 		mei_io_cb_free(cb);
449bca67d68STomas Winkler 		return NULL;
450bca67d68STomas Winkler 	}
451aab3b1a3SAlexander Usyskin 	cb->buf.size = length;
452bca67d68STomas Winkler 
453bca67d68STomas Winkler 	return cb;
454bca67d68STomas Winkler }
455bca67d68STomas Winkler 
456bca67d68STomas Winkler /**
4573030dc05STomas Winkler  * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating
4583030dc05STomas Winkler  *     and enqueuing of the control commands cb
4593030dc05STomas Winkler  *
4603030dc05STomas Winkler  * @cl: host client
4613030dc05STomas Winkler  * @length: size of the buffer
4623030dc05STomas Winkler  * @type: operation type
4633030dc05STomas Winkler  * @fp: associated file pointer (might be NULL)
4643030dc05STomas Winkler  *
4653030dc05STomas Winkler  * Return: cb on success and NULL on failure
4663030dc05STomas Winkler  * Locking: called under "dev->device_lock" lock
4673030dc05STomas Winkler  */
4683030dc05STomas Winkler struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
4693030dc05STomas Winkler 					    enum mei_cb_file_ops fop_type,
4703030dc05STomas Winkler 					    const struct file *fp)
4713030dc05STomas Winkler {
4723030dc05STomas Winkler 	struct mei_cl_cb *cb;
4733030dc05STomas Winkler 
4743030dc05STomas Winkler 	/* for RX always allocate at least client's mtu */
4753030dc05STomas Winkler 	if (length)
4763030dc05STomas Winkler 		length = max_t(size_t, length, mei_cl_mtu(cl));
4773030dc05STomas Winkler 
4783030dc05STomas Winkler 	cb = mei_cl_alloc_cb(cl, length, fop_type, fp);
4793030dc05STomas Winkler 	if (!cb)
4803030dc05STomas Winkler 		return NULL;
4813030dc05STomas Winkler 
4823030dc05STomas Winkler 	list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list);
4833030dc05STomas Winkler 	return cb;
4843030dc05STomas Winkler }
4853030dc05STomas Winkler 
4863030dc05STomas Winkler /**
487a9bed610STomas Winkler  * mei_cl_read_cb - find this cl's callback in the read list
488a9bed610STomas Winkler  *     for a specific file
489a9bed610STomas Winkler  *
490a9bed610STomas Winkler  * @cl: host client
491a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
492a9bed610STomas Winkler  *
493a9bed610STomas Winkler  * Return: cb on success, NULL if cb is not found
494a9bed610STomas Winkler  */
495a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
496a9bed610STomas Winkler {
497a9bed610STomas Winkler 	struct mei_cl_cb *cb;
498a9bed610STomas Winkler 
499a9bed610STomas Winkler 	list_for_each_entry(cb, &cl->rd_completed, list)
50062e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
501a9bed610STomas Winkler 			return cb;
502a9bed610STomas Winkler 
503a9bed610STomas Winkler 	return NULL;
504a9bed610STomas Winkler }
505a9bed610STomas Winkler 
506a9bed610STomas Winkler /**
507a9bed610STomas Winkler  * mei_cl_read_cb_flush - free client's read pending and completed cbs
508a9bed610STomas Winkler  *   for a specific file
509a9bed610STomas Winkler  *
510a9bed610STomas Winkler  * @cl: host client
511a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
512a9bed610STomas Winkler  */
513a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
514a9bed610STomas Winkler {
515a9bed610STomas Winkler 	struct mei_cl_cb *cb, *next;
516a9bed610STomas Winkler 
517a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
51862e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
519a9bed610STomas Winkler 			mei_io_cb_free(cb);
520a9bed610STomas Winkler 
521a9bed610STomas Winkler 
522a9bed610STomas Winkler 	list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
52362e8e6adSTomas Winkler 		if (!fp || fp == cb->fp)
524a9bed610STomas Winkler 			mei_io_cb_free(cb);
525a9bed610STomas Winkler }
526a9bed610STomas Winkler 
527a9bed610STomas Winkler /**
5289ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
5299ca9050bSTomas Winkler  *
5309ca9050bSTomas Winkler  * @cl: host client
531a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
532ce23139cSAlexander Usyskin  *
533ce23139cSAlexander Usyskin  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
5349ca9050bSTomas Winkler  */
535a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
5369ca9050bSTomas Winkler {
537c0abffbdSAlexander Usyskin 	struct mei_device *dev;
538c0abffbdSAlexander Usyskin 
53990e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5409ca9050bSTomas Winkler 		return -EINVAL;
5419ca9050bSTomas Winkler 
542c0abffbdSAlexander Usyskin 	dev = cl->dev;
543c0abffbdSAlexander Usyskin 
544c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
545cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_list, cl);
546cc99ecfdSTomas Winkler 	mei_io_list_free(&cl->dev->write_waiting_list, cl);
5479ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
5489ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
5499ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
550a9bed610STomas Winkler 
551a9bed610STomas Winkler 	mei_cl_read_cb_flush(cl, fp);
552a9bed610STomas Winkler 
5539ca9050bSTomas Winkler 	return 0;
5549ca9050bSTomas Winkler }
5559ca9050bSTomas Winkler 
5569ca9050bSTomas Winkler 
5579ca9050bSTomas Winkler /**
55883ce0741SAlexander Usyskin  * mei_cl_init - initializes cl.
5599ca9050bSTomas Winkler  *
5609ca9050bSTomas Winkler  * @cl: host client to be initialized
5619ca9050bSTomas Winkler  * @dev: mei device
5629ca9050bSTomas Winkler  */
5639ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
5649ca9050bSTomas Winkler {
5659ca9050bSTomas Winkler 	memset(cl, 0, sizeof(struct mei_cl));
5669ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
5679ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
5689ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
569b38a362fSTomas Winkler 	init_waitqueue_head(&cl->ev_wait);
570a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_completed);
571a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_pending);
5729ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
5739ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
5743c666182STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
5759ca9050bSTomas Winkler 	cl->dev = dev;
5769ca9050bSTomas Winkler }
5779ca9050bSTomas Winkler 
5789ca9050bSTomas Winkler /**
5799ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
5809ca9050bSTomas Winkler  *
5819ca9050bSTomas Winkler  * @dev: mei device
582a8605ea2SAlexander Usyskin  * Return:  The allocated file or NULL on failure
5839ca9050bSTomas Winkler  */
5849ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
5859ca9050bSTomas Winkler {
5869ca9050bSTomas Winkler 	struct mei_cl *cl;
5879ca9050bSTomas Winkler 
5889ca9050bSTomas Winkler 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
5899ca9050bSTomas Winkler 	if (!cl)
5909ca9050bSTomas Winkler 		return NULL;
5919ca9050bSTomas Winkler 
5929ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
5939ca9050bSTomas Winkler 
5949ca9050bSTomas Winkler 	return cl;
5959ca9050bSTomas Winkler }
5969ca9050bSTomas Winkler 
59790e0b5f1STomas Winkler /**
5983908be6fSAlexander Usyskin  * mei_cl_link - allocate host id in the host map
5999ca9050bSTomas Winkler  *
6003908be6fSAlexander Usyskin  * @cl: host client
601393b148fSMasanari Iida  *
602a8605ea2SAlexander Usyskin  * Return: 0 on success
6039ca9050bSTomas Winkler  *	-EINVAL on incorrect values
60403b8d341STomas Winkler  *	-EMFILE if open count exceeded.
6059ca9050bSTomas Winkler  */
6067851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl)
6079ca9050bSTomas Winkler {
60890e0b5f1STomas Winkler 	struct mei_device *dev;
60922f96a0eSTomas Winkler 	long open_handle_count;
6107851e008SAlexander Usyskin 	int id;
6119ca9050bSTomas Winkler 
612781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
6139ca9050bSTomas Winkler 		return -EINVAL;
6149ca9050bSTomas Winkler 
61590e0b5f1STomas Winkler 	dev = cl->dev;
61690e0b5f1STomas Winkler 
6177851e008SAlexander Usyskin 	id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
618781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
6192bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
620e036cc57STomas Winkler 		return -EMFILE;
621e036cc57STomas Winkler 	}
622e036cc57STomas Winkler 
62322f96a0eSTomas Winkler 	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
62422f96a0eSTomas Winkler 	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6252bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
626e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
627e036cc57STomas Winkler 		return -EMFILE;
6289ca9050bSTomas Winkler 	}
629781d0d89STomas Winkler 
630781d0d89STomas Winkler 	dev->open_handle_count++;
631781d0d89STomas Winkler 
632781d0d89STomas Winkler 	cl->host_client_id = id;
633781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
634781d0d89STomas Winkler 
635781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
636781d0d89STomas Winkler 
637781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
638781d0d89STomas Winkler 
639c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
640781d0d89STomas Winkler 	return 0;
641781d0d89STomas Winkler }
642781d0d89STomas Winkler 
6439ca9050bSTomas Winkler /**
644d49ed64aSAlexander Usyskin  * mei_cl_unlink - remove host client from the list
6459ca9050bSTomas Winkler  *
646393b148fSMasanari Iida  * @cl: host client
647ce23139cSAlexander Usyskin  *
648ce23139cSAlexander Usyskin  * Return: always 0
6499ca9050bSTomas Winkler  */
65090e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6519ca9050bSTomas Winkler {
65290e0b5f1STomas Winkler 	struct mei_device *dev;
65390e0b5f1STomas Winkler 
654781d0d89STomas Winkler 	/* don't shout on error exit path */
655781d0d89STomas Winkler 	if (!cl)
656781d0d89STomas Winkler 		return 0;
657781d0d89STomas Winkler 
658fdd9b865SAlexander Usyskin 	/* amthif might not be initialized */
6598e9a4a9aSTomas Winkler 	if (!cl->dev)
6608e9a4a9aSTomas Winkler 		return 0;
66190e0b5f1STomas Winkler 
66290e0b5f1STomas Winkler 	dev = cl->dev;
66390e0b5f1STomas Winkler 
664a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
665a14c44d8STomas Winkler 
66622f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
66722f96a0eSTomas Winkler 		dev->open_handle_count--;
66822f96a0eSTomas Winkler 
66922f96a0eSTomas Winkler 	/* never clear the 0 bit */
67022f96a0eSTomas Winkler 	if (cl->host_client_id)
67122f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
67222f96a0eSTomas Winkler 
67322f96a0eSTomas Winkler 	list_del_init(&cl->link);
67422f96a0eSTomas Winkler 
67522f96a0eSTomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
67622f96a0eSTomas Winkler 
67790e0b5f1STomas Winkler 	return 0;
6789ca9050bSTomas Winkler }
6799ca9050bSTomas Winkler 
680025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev)
6819ca9050bSTomas Winkler {
6829ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
6836adb8efbSTomas Winkler 	dev->reset_count = 0;
68404bb139aSTomas Winkler 
685025fb792SAlexander Usyskin 	schedule_work(&dev->bus_rescan_work);
6866009595aSTomas Winkler 
6872bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
6882bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
6892bf94cabSTomas Winkler 	pm_runtime_autosuspend(dev->dev);
6909ca9050bSTomas Winkler }
6919ca9050bSTomas Winkler 
6926aae48ffSTomas Winkler /**
693a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
6946aae48ffSTomas Winkler  *
6956aae48ffSTomas Winkler  * @dev: the device structure
696a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
6976aae48ffSTomas Winkler  */
6986aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
6996aae48ffSTomas Winkler {
70004bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
7013dc196eaSAlexander Usyskin 	    mei_pg_in_transition(dev)) {
7022bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
70304bb139aSTomas Winkler 		return false;
70404bb139aSTomas Winkler 	}
70504bb139aSTomas Winkler 
7066aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
7072bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
7086aae48ffSTomas Winkler 		return false;
7096aae48ffSTomas Winkler 	}
7106aae48ffSTomas Winkler 
7116aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7126aae48ffSTomas Winkler 
7136aae48ffSTomas Winkler 	return true;
7146aae48ffSTomas Winkler }
7159ca9050bSTomas Winkler 
7169ca9050bSTomas Winkler /**
717a4307fe4SAlexander Usyskin  * mei_cl_wake_all - wake up readers, writers and event waiters so
718a4307fe4SAlexander Usyskin  *                 they can be interrupted
719a4307fe4SAlexander Usyskin  *
720a4307fe4SAlexander Usyskin  * @cl: host client
721a4307fe4SAlexander Usyskin  */
722a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl)
723a4307fe4SAlexander Usyskin {
724a4307fe4SAlexander Usyskin 	struct mei_device *dev = cl->dev;
725a4307fe4SAlexander Usyskin 
726a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
727a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->rx_wait)) {
728a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up reading client!\n");
729a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->rx_wait);
730a4307fe4SAlexander Usyskin 	}
731a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
732a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->tx_wait)) {
733a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up writing client!\n");
734a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->tx_wait);
735a4307fe4SAlexander Usyskin 	}
736a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
737a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->ev_wait)) {
738a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
739a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->ev_wait);
740a4307fe4SAlexander Usyskin 	}
7417ff4bdd4SAlexander Usyskin 	/* synchronized under device mutex */
7427ff4bdd4SAlexander Usyskin 	if (waitqueue_active(&cl->wait)) {
7437ff4bdd4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up ctrl write clients!\n");
74469f1804aSAlexander Usyskin 		wake_up(&cl->wait);
7457ff4bdd4SAlexander Usyskin 	}
746a4307fe4SAlexander Usyskin }
747a4307fe4SAlexander Usyskin 
748a4307fe4SAlexander Usyskin /**
7493c666182STomas Winkler  * mei_cl_set_disconnected - set disconnected state and clear
7503c666182STomas Winkler  *   associated states and resources
7513c666182STomas Winkler  *
7523c666182STomas Winkler  * @cl: host client
7533c666182STomas Winkler  */
7543c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl)
7553c666182STomas Winkler {
7563c666182STomas Winkler 	struct mei_device *dev = cl->dev;
7573c666182STomas Winkler 
7583c666182STomas Winkler 	if (cl->state == MEI_FILE_DISCONNECTED ||
7593c666182STomas Winkler 	    cl->state == MEI_FILE_INITIALIZING)
7603c666182STomas Winkler 		return;
7613c666182STomas Winkler 
7623c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTED;
763a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_list, cl);
764a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_waiting_list, cl);
7653c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7663c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
767a4307fe4SAlexander Usyskin 	mei_cl_wake_all(cl);
76846978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds = 0;
7694034b81bSTomas Winkler 	cl->tx_flow_ctrl_creds = 0;
7703c666182STomas Winkler 	cl->timer_count = 0;
771d49ed64aSAlexander Usyskin 
772a03d77f6SAlexander Usyskin 	if (!cl->me_cl)
773a03d77f6SAlexander Usyskin 		return;
774a03d77f6SAlexander Usyskin 
775a03d77f6SAlexander Usyskin 	if (!WARN_ON(cl->me_cl->connect_count == 0))
776a03d77f6SAlexander Usyskin 		cl->me_cl->connect_count--;
777a03d77f6SAlexander Usyskin 
778c241e9b1SAlexander Usyskin 	if (cl->me_cl->connect_count == 0)
7794034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds = 0;
780c241e9b1SAlexander Usyskin 
781d49ed64aSAlexander Usyskin 	mei_me_cl_put(cl->me_cl);
782d49ed64aSAlexander Usyskin 	cl->me_cl = NULL;
7833c666182STomas Winkler }
7843c666182STomas Winkler 
785a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
786a03d77f6SAlexander Usyskin {
7871df629efSAlexander Usyskin 	if (!mei_me_cl_get(me_cl))
788a03d77f6SAlexander Usyskin 		return -ENOENT;
789a03d77f6SAlexander Usyskin 
7901df629efSAlexander Usyskin 	/* only one connection is allowed for fixed address clients */
7911df629efSAlexander Usyskin 	if (me_cl->props.fixed_address) {
7921df629efSAlexander Usyskin 		if (me_cl->connect_count) {
7931df629efSAlexander Usyskin 			mei_me_cl_put(me_cl);
7941df629efSAlexander Usyskin 			return -EBUSY;
7951df629efSAlexander Usyskin 		}
7961df629efSAlexander Usyskin 	}
7971df629efSAlexander Usyskin 
7981df629efSAlexander Usyskin 	cl->me_cl = me_cl;
799a03d77f6SAlexander Usyskin 	cl->state = MEI_FILE_CONNECTING;
800a03d77f6SAlexander Usyskin 	cl->me_cl->connect_count++;
801a03d77f6SAlexander Usyskin 
802a03d77f6SAlexander Usyskin 	return 0;
803a03d77f6SAlexander Usyskin }
804a03d77f6SAlexander Usyskin 
8053c666182STomas Winkler /*
8063c666182STomas Winkler  * mei_cl_send_disconnect - send disconnect request
8073c666182STomas Winkler  *
8083c666182STomas Winkler  * @cl: host client
8093c666182STomas Winkler  * @cb: callback block
8103c666182STomas Winkler  *
8113c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8123c666182STomas Winkler  */
8133c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
8143c666182STomas Winkler {
8153c666182STomas Winkler 	struct mei_device *dev;
8163c666182STomas Winkler 	int ret;
8173c666182STomas Winkler 
8183c666182STomas Winkler 	dev = cl->dev;
8193c666182STomas Winkler 
8203c666182STomas Winkler 	ret = mei_hbm_cl_disconnect_req(dev, cl);
8213c666182STomas Winkler 	cl->status = ret;
8223c666182STomas Winkler 	if (ret) {
8233c666182STomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
8243c666182STomas Winkler 		return ret;
8253c666182STomas Winkler 	}
8263c666182STomas Winkler 
8273c666182STomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
8283c666182STomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
8293c666182STomas Winkler 
8303c666182STomas Winkler 	return 0;
8313c666182STomas Winkler }
8323c666182STomas Winkler 
8333c666182STomas Winkler /**
8343c666182STomas Winkler  * mei_cl_irq_disconnect - processes close related operation from
8353c666182STomas Winkler  *	interrupt thread context - send disconnect request
8363c666182STomas Winkler  *
8373c666182STomas Winkler  * @cl: client
8383c666182STomas Winkler  * @cb: callback block.
8393c666182STomas Winkler  * @cmpl_list: complete list.
8403c666182STomas Winkler  *
8413c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8423c666182STomas Winkler  */
8433c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
8443c666182STomas Winkler 			    struct mei_cl_cb *cmpl_list)
8453c666182STomas Winkler {
8463c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8473c666182STomas Winkler 	u32 msg_slots;
8483c666182STomas Winkler 	int slots;
8493c666182STomas Winkler 	int ret;
8503c666182STomas Winkler 
8513c666182STomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
8523c666182STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
8533c666182STomas Winkler 
8543c666182STomas Winkler 	if (slots < msg_slots)
8553c666182STomas Winkler 		return -EMSGSIZE;
8563c666182STomas Winkler 
8573c666182STomas Winkler 	ret = mei_cl_send_disconnect(cl, cb);
8583c666182STomas Winkler 	if (ret)
8593c666182STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
8603c666182STomas Winkler 
8613c666182STomas Winkler 	return ret;
8623c666182STomas Winkler }
8633c666182STomas Winkler 
8643c666182STomas Winkler /**
86518901357SAlexander Usyskin  * __mei_cl_disconnect - disconnect host client from the me one
86618901357SAlexander Usyskin  *     internal function runtime pm has to be already acquired
8679ca9050bSTomas Winkler  *
86890e0b5f1STomas Winkler  * @cl: host client
8699ca9050bSTomas Winkler  *
870a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
8719ca9050bSTomas Winkler  */
87218901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl)
8739ca9050bSTomas Winkler {
87490e0b5f1STomas Winkler 	struct mei_device *dev;
8759ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
876fe2f17ebSAlexander Usyskin 	int rets;
8779ca9050bSTomas Winkler 
87890e0b5f1STomas Winkler 	dev = cl->dev;
87990e0b5f1STomas Winkler 
8803c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTING;
8813c666182STomas Winkler 
8823030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
8833030dc05STomas Winkler 	if (!cb) {
8843030dc05STomas Winkler 		rets = -ENOMEM;
8853c666182STomas Winkler 		goto out;
8863030dc05STomas Winkler 	}
8879ca9050bSTomas Winkler 
8883c666182STomas Winkler 	if (mei_hbuf_acquire(dev)) {
8893c666182STomas Winkler 		rets = mei_cl_send_disconnect(cl, cb);
8903c666182STomas Winkler 		if (rets) {
8913c666182STomas Winkler 			cl_err(dev, cl, "failed to disconnect.\n");
8923c666182STomas Winkler 			goto out;
8939ca9050bSTomas Winkler 		}
8943c666182STomas Winkler 	}
8953c666182STomas Winkler 
8969ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
8977ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
8987ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECT_REPLY ||
8997ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECTED,
9009ca9050bSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
9019ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
902fe2f17ebSAlexander Usyskin 
9033c666182STomas Winkler 	rets = cl->status;
9047ff4bdd4SAlexander Usyskin 	if (cl->state != MEI_FILE_DISCONNECT_REPLY &&
9057ff4bdd4SAlexander Usyskin 	    cl->state != MEI_FILE_DISCONNECTED) {
906fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
907fe2f17ebSAlexander Usyskin 		rets = -ETIME;
9089ca9050bSTomas Winkler 	}
9099ca9050bSTomas Winkler 
9103c666182STomas Winkler out:
9113c666182STomas Winkler 	/* we disconnect also on error */
9123c666182STomas Winkler 	mei_cl_set_disconnected(cl);
9133c666182STomas Winkler 	if (!rets)
9143c666182STomas Winkler 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
9153c666182STomas Winkler 
91618901357SAlexander Usyskin 	mei_io_cb_free(cb);
91718901357SAlexander Usyskin 	return rets;
91818901357SAlexander Usyskin }
91918901357SAlexander Usyskin 
92018901357SAlexander Usyskin /**
92118901357SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
92218901357SAlexander Usyskin  *
92318901357SAlexander Usyskin  * @cl: host client
92418901357SAlexander Usyskin  *
92518901357SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
92618901357SAlexander Usyskin  *
92718901357SAlexander Usyskin  * Return: 0 on success, <0 on failure.
92818901357SAlexander Usyskin  */
92918901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl)
93018901357SAlexander Usyskin {
93118901357SAlexander Usyskin 	struct mei_device *dev;
93218901357SAlexander Usyskin 	int rets;
93318901357SAlexander Usyskin 
93418901357SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
93518901357SAlexander Usyskin 		return -ENODEV;
93618901357SAlexander Usyskin 
93718901357SAlexander Usyskin 	dev = cl->dev;
93818901357SAlexander Usyskin 
93918901357SAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
94018901357SAlexander Usyskin 
94118901357SAlexander Usyskin 	if (!mei_cl_is_connected(cl))
94218901357SAlexander Usyskin 		return 0;
94318901357SAlexander Usyskin 
94418901357SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
94518901357SAlexander Usyskin 		mei_cl_set_disconnected(cl);
94618901357SAlexander Usyskin 		return 0;
94718901357SAlexander Usyskin 	}
94818901357SAlexander Usyskin 
94918901357SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
95018901357SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
95118901357SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
95218901357SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
95318901357SAlexander Usyskin 		return rets;
95418901357SAlexander Usyskin 	}
95518901357SAlexander Usyskin 
95618901357SAlexander Usyskin 	rets = __mei_cl_disconnect(cl);
95718901357SAlexander Usyskin 
95804bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
9592bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
9602bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
96104bb139aSTomas Winkler 
9629ca9050bSTomas Winkler 	return rets;
9639ca9050bSTomas Winkler }
9649ca9050bSTomas Winkler 
9659ca9050bSTomas Winkler 
9669ca9050bSTomas Winkler /**
96790e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
96890e0b5f1STomas Winkler  *    client with the same me client id is connecting
9699ca9050bSTomas Winkler  *
9709ca9050bSTomas Winkler  * @cl: private data of the file object
9719ca9050bSTomas Winkler  *
972a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
9739ca9050bSTomas Winkler  */
9740c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl)
9759ca9050bSTomas Winkler {
97690e0b5f1STomas Winkler 	struct mei_device *dev;
9770c53357cSTomas Winkler 	struct mei_cl_cb *cb;
97890e0b5f1STomas Winkler 
97990e0b5f1STomas Winkler 	dev = cl->dev;
98090e0b5f1STomas Winkler 
9810c53357cSTomas Winkler 	list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
9820c53357cSTomas Winkler 		if (cb->fop_type == MEI_FOP_CONNECT &&
983d49ed64aSAlexander Usyskin 		    mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
98490e0b5f1STomas Winkler 			return true;
9859ca9050bSTomas Winkler 	}
98690e0b5f1STomas Winkler 
98790e0b5f1STomas Winkler 	return false;
9889ca9050bSTomas Winkler }
9899ca9050bSTomas Winkler 
9909ca9050bSTomas Winkler /**
9910c53357cSTomas Winkler  * mei_cl_send_connect - send connect request
9920c53357cSTomas Winkler  *
9930c53357cSTomas Winkler  * @cl: host client
9940c53357cSTomas Winkler  * @cb: callback block
9950c53357cSTomas Winkler  *
9960c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
9970c53357cSTomas Winkler  */
9980c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
9990c53357cSTomas Winkler {
10000c53357cSTomas Winkler 	struct mei_device *dev;
10010c53357cSTomas Winkler 	int ret;
10020c53357cSTomas Winkler 
10030c53357cSTomas Winkler 	dev = cl->dev;
10040c53357cSTomas Winkler 
10050c53357cSTomas Winkler 	ret = mei_hbm_cl_connect_req(dev, cl);
10060c53357cSTomas Winkler 	cl->status = ret;
10070c53357cSTomas Winkler 	if (ret) {
10080c53357cSTomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
10090c53357cSTomas Winkler 		return ret;
10100c53357cSTomas Winkler 	}
10110c53357cSTomas Winkler 
10120c53357cSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
10130c53357cSTomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
10140c53357cSTomas Winkler 	return 0;
10150c53357cSTomas Winkler }
10160c53357cSTomas Winkler 
10170c53357cSTomas Winkler /**
10180c53357cSTomas Winkler  * mei_cl_irq_connect - send connect request in irq_thread context
10190c53357cSTomas Winkler  *
10200c53357cSTomas Winkler  * @cl: host client
10210c53357cSTomas Winkler  * @cb: callback block
10220c53357cSTomas Winkler  * @cmpl_list: complete list
10230c53357cSTomas Winkler  *
10240c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10250c53357cSTomas Winkler  */
10260c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
10270c53357cSTomas Winkler 			      struct mei_cl_cb *cmpl_list)
10280c53357cSTomas Winkler {
10290c53357cSTomas Winkler 	struct mei_device *dev = cl->dev;
10300c53357cSTomas Winkler 	u32 msg_slots;
10310c53357cSTomas Winkler 	int slots;
10320c53357cSTomas Winkler 	int rets;
10330c53357cSTomas Winkler 
10340c53357cSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
10350c53357cSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
10360c53357cSTomas Winkler 
10370c53357cSTomas Winkler 	if (mei_cl_is_other_connecting(cl))
10380c53357cSTomas Winkler 		return 0;
10390c53357cSTomas Winkler 
10400c53357cSTomas Winkler 	if (slots < msg_slots)
10410c53357cSTomas Winkler 		return -EMSGSIZE;
10420c53357cSTomas Winkler 
10430c53357cSTomas Winkler 	rets = mei_cl_send_connect(cl, cb);
10440c53357cSTomas Winkler 	if (rets)
10450c53357cSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
10460c53357cSTomas Winkler 
10470c53357cSTomas Winkler 	return rets;
10480c53357cSTomas Winkler }
10490c53357cSTomas Winkler 
10500c53357cSTomas Winkler /**
105183ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
10529f81abdaSTomas Winkler  *
10539f81abdaSTomas Winkler  * @cl: host client
1054d49ed64aSAlexander Usyskin  * @me_cl: me client
10553030dc05STomas Winkler  * @fp: pointer to file structure
10569f81abdaSTomas Winkler  *
10579f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
10589f81abdaSTomas Winkler  *
1059a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
10609f81abdaSTomas Winkler  */
1061d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
10623030dc05STomas Winkler 		   const struct file *fp)
10639f81abdaSTomas Winkler {
10649f81abdaSTomas Winkler 	struct mei_device *dev;
10659f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
10669f81abdaSTomas Winkler 	int rets;
10679f81abdaSTomas Winkler 
10681df629efSAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev || !me_cl))
10699f81abdaSTomas Winkler 		return -ENODEV;
10709f81abdaSTomas Winkler 
10719f81abdaSTomas Winkler 	dev = cl->dev;
10729f81abdaSTomas Winkler 
10731df629efSAlexander Usyskin 	rets = mei_cl_set_connecting(cl, me_cl);
10741df629efSAlexander Usyskin 	if (rets)
10751df629efSAlexander Usyskin 		return rets;
10761df629efSAlexander Usyskin 
10771df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
10781df629efSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTED;
10791df629efSAlexander Usyskin 		return 0;
10801df629efSAlexander Usyskin 	}
10811df629efSAlexander Usyskin 
10822bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
108304bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
10842bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
108504bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
10861df629efSAlexander Usyskin 		goto nortpm;
108704bb139aSTomas Winkler 	}
108804bb139aSTomas Winkler 
10893030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
10903030dc05STomas Winkler 	if (!cb) {
10913030dc05STomas Winkler 		rets = -ENOMEM;
10929f81abdaSTomas Winkler 		goto out;
10933030dc05STomas Winkler 	}
10940c53357cSTomas Winkler 
10956aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
10966aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
10970c53357cSTomas Winkler 		rets = mei_cl_send_connect(cl, cb);
10980c53357cSTomas Winkler 		if (rets)
10999f81abdaSTomas Winkler 			goto out;
11009f81abdaSTomas Winkler 	}
11019f81abdaSTomas Winkler 
11029f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
110312f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
11049f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
11057ff4bdd4SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECTED ||
110618901357SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
11073c666182STomas Winkler 			 cl->state == MEI_FILE_DISCONNECT_REPLY),
1108206ecfc2SFrode Isaksen 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
11099f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
11109f81abdaSTomas Winkler 
1111f3de9b63STomas Winkler 	if (!mei_cl_is_connected(cl)) {
111218901357SAlexander Usyskin 		if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
111318901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_rd_list, cl);
111418901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_wr_list, cl);
111518901357SAlexander Usyskin 			 /* ignore disconnect return valuue;
111618901357SAlexander Usyskin 			  * in case of failure reset will be invoked
111718901357SAlexander Usyskin 			  */
111818901357SAlexander Usyskin 			__mei_cl_disconnect(cl);
111918901357SAlexander Usyskin 			rets = -EFAULT;
112018901357SAlexander Usyskin 			goto out;
112118901357SAlexander Usyskin 		}
112218901357SAlexander Usyskin 
11230c53357cSTomas Winkler 		/* timeout or something went really wrong */
1124285e2996SAlexander Usyskin 		if (!cl->status)
1125285e2996SAlexander Usyskin 			cl->status = -EFAULT;
11269f81abdaSTomas Winkler 	}
11279f81abdaSTomas Winkler 
11289f81abdaSTomas Winkler 	rets = cl->status;
11299f81abdaSTomas Winkler out:
113004bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
11312bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
11322bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
113304bb139aSTomas Winkler 
11349f81abdaSTomas Winkler 	mei_io_cb_free(cb);
11350c53357cSTomas Winkler 
11361df629efSAlexander Usyskin nortpm:
11370c53357cSTomas Winkler 	if (!mei_cl_is_connected(cl))
11380c53357cSTomas Winkler 		mei_cl_set_disconnected(cl);
11390c53357cSTomas Winkler 
11409f81abdaSTomas Winkler 	return rets;
11419f81abdaSTomas Winkler }
11429f81abdaSTomas Winkler 
11439f81abdaSTomas Winkler /**
114403b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
114503b8d341STomas Winkler  *
114603b8d341STomas Winkler  * @dev: the device structure
114703b8d341STomas Winkler  *
114803b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
114903b8d341STomas Winkler  */
11507851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
115103b8d341STomas Winkler {
115203b8d341STomas Winkler 	struct mei_cl *cl;
115303b8d341STomas Winkler 	int ret;
115403b8d341STomas Winkler 
115503b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
115603b8d341STomas Winkler 	if (!cl) {
115703b8d341STomas Winkler 		ret = -ENOMEM;
115803b8d341STomas Winkler 		goto err;
115903b8d341STomas Winkler 	}
116003b8d341STomas Winkler 
11617851e008SAlexander Usyskin 	ret = mei_cl_link(cl);
116203b8d341STomas Winkler 	if (ret)
116303b8d341STomas Winkler 		goto err;
116403b8d341STomas Winkler 
116503b8d341STomas Winkler 	return cl;
116603b8d341STomas Winkler err:
116703b8d341STomas Winkler 	kfree(cl);
116803b8d341STomas Winkler 	return ERR_PTR(ret);
116903b8d341STomas Winkler }
117003b8d341STomas Winkler 
117103b8d341STomas Winkler /**
11724034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl.
11739ca9050bSTomas Winkler  *
117406ee536bSAlexander Usyskin  * @cl: host client
11759ca9050bSTomas Winkler  *
11764034b81bSTomas Winkler  * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise.
11779ca9050bSTomas Winkler  */
11784034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl)
11799ca9050bSTomas Winkler {
1180d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
118190e0b5f1STomas Winkler 		return -EINVAL;
118290e0b5f1STomas Winkler 
11834034b81bSTomas Winkler 	if (cl->tx_flow_ctrl_creds > 0)
11849ca9050bSTomas Winkler 		return 1;
11859ca9050bSTomas Winkler 
1186a808c80cSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
11871df629efSAlexander Usyskin 		return 1;
11881df629efSAlexander Usyskin 
1189d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
11904034b81bSTomas Winkler 		if (cl->me_cl->tx_flow_ctrl_creds > 0)
1191d49ed64aSAlexander Usyskin 			return 1;
119212d00665SAlexander Usyskin 	}
1193d49ed64aSAlexander Usyskin 	return 0;
11949ca9050bSTomas Winkler }
11959ca9050bSTomas Winkler 
11969ca9050bSTomas Winkler /**
11974034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits
11984034b81bSTomas Winkler  *   for a client
11999ca9050bSTomas Winkler  *
12004034b81bSTomas Winkler  * @cl: host client
1201393b148fSMasanari Iida  *
1202a8605ea2SAlexander Usyskin  * Return:
12039ca9050bSTomas Winkler  *	0 on success
12049ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
12059ca9050bSTomas Winkler  */
12064034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
12079ca9050bSTomas Winkler {
1208d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
120990e0b5f1STomas Winkler 		return -EINVAL;
121090e0b5f1STomas Winkler 
12111df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12121df629efSAlexander Usyskin 		return 0;
12131df629efSAlexander Usyskin 
1214d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
12154034b81bSTomas Winkler 		if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0))
1216d49ed64aSAlexander Usyskin 			return -EINVAL;
12174034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds--;
12189ca9050bSTomas Winkler 	} else {
12194034b81bSTomas Winkler 		if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
1220d49ed64aSAlexander Usyskin 			return -EINVAL;
12214034b81bSTomas Winkler 		cl->tx_flow_ctrl_creds--;
12229ca9050bSTomas Winkler 	}
1223d49ed64aSAlexander Usyskin 	return 0;
12249ca9050bSTomas Winkler }
12259ca9050bSTomas Winkler 
12269ca9050bSTomas Winkler /**
122751678ccbSTomas Winkler  *  mei_cl_notify_fop2req - convert fop to proper request
122851678ccbSTomas Winkler  *
122951678ccbSTomas Winkler  * @fop: client notification start response command
123051678ccbSTomas Winkler  *
123151678ccbSTomas Winkler  * Return:  MEI_HBM_NOTIFICATION_START/STOP
123251678ccbSTomas Winkler  */
123351678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
123451678ccbSTomas Winkler {
123551678ccbSTomas Winkler 	if (fop == MEI_FOP_NOTIFY_START)
123651678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_START;
123751678ccbSTomas Winkler 	else
123851678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_STOP;
123951678ccbSTomas Winkler }
124051678ccbSTomas Winkler 
124151678ccbSTomas Winkler /**
124251678ccbSTomas Winkler  *  mei_cl_notify_req2fop - convert notification request top file operation type
124351678ccbSTomas Winkler  *
124451678ccbSTomas Winkler  * @req: hbm notification request type
124551678ccbSTomas Winkler  *
124651678ccbSTomas Winkler  * Return:  MEI_FOP_NOTIFY_START/STOP
124751678ccbSTomas Winkler  */
124851678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
124951678ccbSTomas Winkler {
125051678ccbSTomas Winkler 	if (req == MEI_HBM_NOTIFICATION_START)
125151678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_START;
125251678ccbSTomas Winkler 	else
125351678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_STOP;
125451678ccbSTomas Winkler }
125551678ccbSTomas Winkler 
125651678ccbSTomas Winkler /**
125751678ccbSTomas Winkler  * mei_cl_irq_notify - send notification request in irq_thread context
125851678ccbSTomas Winkler  *
125951678ccbSTomas Winkler  * @cl: client
126051678ccbSTomas Winkler  * @cb: callback block.
126151678ccbSTomas Winkler  * @cmpl_list: complete list.
126251678ccbSTomas Winkler  *
126351678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
126451678ccbSTomas Winkler  */
126551678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
126651678ccbSTomas Winkler 		      struct mei_cl_cb *cmpl_list)
126751678ccbSTomas Winkler {
126851678ccbSTomas Winkler 	struct mei_device *dev = cl->dev;
126951678ccbSTomas Winkler 	u32 msg_slots;
127051678ccbSTomas Winkler 	int slots;
127151678ccbSTomas Winkler 	int ret;
127251678ccbSTomas Winkler 	bool request;
127351678ccbSTomas Winkler 
127451678ccbSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
127551678ccbSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
127651678ccbSTomas Winkler 
127751678ccbSTomas Winkler 	if (slots < msg_slots)
127851678ccbSTomas Winkler 		return -EMSGSIZE;
127951678ccbSTomas Winkler 
128051678ccbSTomas Winkler 	request = mei_cl_notify_fop2req(cb->fop_type);
128151678ccbSTomas Winkler 	ret = mei_hbm_cl_notify_req(dev, cl, request);
128251678ccbSTomas Winkler 	if (ret) {
128351678ccbSTomas Winkler 		cl->status = ret;
128451678ccbSTomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
128551678ccbSTomas Winkler 		return ret;
128651678ccbSTomas Winkler 	}
128751678ccbSTomas Winkler 
128851678ccbSTomas Winkler 	list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
128951678ccbSTomas Winkler 	return 0;
129051678ccbSTomas Winkler }
129151678ccbSTomas Winkler 
129251678ccbSTomas Winkler /**
129351678ccbSTomas Winkler  * mei_cl_notify_request - send notification stop/start request
129451678ccbSTomas Winkler  *
129551678ccbSTomas Winkler  * @cl: host client
12963030dc05STomas Winkler  * @fp: associate request with file
129751678ccbSTomas Winkler  * @request: 1 for start or 0 for stop
129851678ccbSTomas Winkler  *
129951678ccbSTomas Winkler  * Locking: called under "dev->device_lock" lock
130051678ccbSTomas Winkler  *
130151678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
130251678ccbSTomas Winkler  */
1303f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl,
13043030dc05STomas Winkler 			  const struct file *fp, u8 request)
130551678ccbSTomas Winkler {
130651678ccbSTomas Winkler 	struct mei_device *dev;
130751678ccbSTomas Winkler 	struct mei_cl_cb *cb;
130851678ccbSTomas Winkler 	enum mei_cb_file_ops fop_type;
130951678ccbSTomas Winkler 	int rets;
131051678ccbSTomas Winkler 
131151678ccbSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
131251678ccbSTomas Winkler 		return -ENODEV;
131351678ccbSTomas Winkler 
131451678ccbSTomas Winkler 	dev = cl->dev;
131551678ccbSTomas Winkler 
131651678ccbSTomas Winkler 	if (!dev->hbm_f_ev_supported) {
131751678ccbSTomas Winkler 		cl_dbg(dev, cl, "notifications not supported\n");
131851678ccbSTomas Winkler 		return -EOPNOTSUPP;
131951678ccbSTomas Winkler 	}
132051678ccbSTomas Winkler 
132151678ccbSTomas Winkler 	rets = pm_runtime_get(dev->dev);
132251678ccbSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
132351678ccbSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
132451678ccbSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
132551678ccbSTomas Winkler 		return rets;
132651678ccbSTomas Winkler 	}
132751678ccbSTomas Winkler 
132851678ccbSTomas Winkler 	fop_type = mei_cl_notify_req2fop(request);
13293030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp);
133051678ccbSTomas Winkler 	if (!cb) {
133151678ccbSTomas Winkler 		rets = -ENOMEM;
133251678ccbSTomas Winkler 		goto out;
133351678ccbSTomas Winkler 	}
133451678ccbSTomas Winkler 
133551678ccbSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
133651678ccbSTomas Winkler 		if (mei_hbm_cl_notify_req(dev, cl, request)) {
133751678ccbSTomas Winkler 			rets = -ENODEV;
133851678ccbSTomas Winkler 			goto out;
133951678ccbSTomas Winkler 		}
13403030dc05STomas Winkler 		list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
134151678ccbSTomas Winkler 	}
134251678ccbSTomas Winkler 
134351678ccbSTomas Winkler 	mutex_unlock(&dev->device_lock);
13447ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
13457ff4bdd4SAlexander Usyskin 			   cl->notify_en == request || !mei_cl_is_connected(cl),
134651678ccbSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
134751678ccbSTomas Winkler 	mutex_lock(&dev->device_lock);
134851678ccbSTomas Winkler 
13494a8eaa96SAlexander Usyskin 	if (cl->notify_en != request && !cl->status)
135051678ccbSTomas Winkler 		cl->status = -EFAULT;
135151678ccbSTomas Winkler 
135251678ccbSTomas Winkler 	rets = cl->status;
135351678ccbSTomas Winkler 
135451678ccbSTomas Winkler out:
135551678ccbSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
135651678ccbSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
135751678ccbSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
135851678ccbSTomas Winkler 
135951678ccbSTomas Winkler 	mei_io_cb_free(cb);
136051678ccbSTomas Winkler 	return rets;
136151678ccbSTomas Winkler }
136251678ccbSTomas Winkler 
136351678ccbSTomas Winkler /**
1364237092bfSTomas Winkler  * mei_cl_notify - raise notification
1365237092bfSTomas Winkler  *
1366237092bfSTomas Winkler  * @cl: host client
1367237092bfSTomas Winkler  *
1368237092bfSTomas Winkler  * Locking: called under "dev->device_lock" lock
1369237092bfSTomas Winkler  */
1370237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl)
1371237092bfSTomas Winkler {
1372237092bfSTomas Winkler 	struct mei_device *dev;
1373237092bfSTomas Winkler 
1374237092bfSTomas Winkler 	if (!cl || !cl->dev)
1375237092bfSTomas Winkler 		return;
1376237092bfSTomas Winkler 
1377237092bfSTomas Winkler 	dev = cl->dev;
1378237092bfSTomas Winkler 
1379237092bfSTomas Winkler 	if (!cl->notify_en)
1380237092bfSTomas Winkler 		return;
1381237092bfSTomas Winkler 
1382237092bfSTomas Winkler 	cl_dbg(dev, cl, "notify event");
1383237092bfSTomas Winkler 	cl->notify_ev = true;
1384850f8940STomas Winkler 	if (!mei_cl_bus_notify_event(cl))
1385850f8940STomas Winkler 		wake_up_interruptible(&cl->ev_wait);
1386237092bfSTomas Winkler 
1387237092bfSTomas Winkler 	if (cl->ev_async)
1388237092bfSTomas Winkler 		kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
1389bb2ef9c3SAlexander Usyskin 
1390237092bfSTomas Winkler }
1391237092bfSTomas Winkler 
1392237092bfSTomas Winkler /**
1393b38a362fSTomas Winkler  * mei_cl_notify_get - get or wait for notification event
1394b38a362fSTomas Winkler  *
1395b38a362fSTomas Winkler  * @cl: host client
1396b38a362fSTomas Winkler  * @block: this request is blocking
1397b38a362fSTomas Winkler  * @notify_ev: true if notification event was received
1398b38a362fSTomas Winkler  *
1399b38a362fSTomas Winkler  * Locking: called under "dev->device_lock" lock
1400b38a362fSTomas Winkler  *
1401b38a362fSTomas Winkler  * Return: 0 on such and error otherwise.
1402b38a362fSTomas Winkler  */
1403b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
1404b38a362fSTomas Winkler {
1405b38a362fSTomas Winkler 	struct mei_device *dev;
1406b38a362fSTomas Winkler 	int rets;
1407b38a362fSTomas Winkler 
1408b38a362fSTomas Winkler 	*notify_ev = false;
1409b38a362fSTomas Winkler 
1410b38a362fSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1411b38a362fSTomas Winkler 		return -ENODEV;
1412b38a362fSTomas Winkler 
1413b38a362fSTomas Winkler 	dev = cl->dev;
1414b38a362fSTomas Winkler 
1415b38a362fSTomas Winkler 	if (!mei_cl_is_connected(cl))
1416b38a362fSTomas Winkler 		return -ENODEV;
1417b38a362fSTomas Winkler 
1418b38a362fSTomas Winkler 	if (cl->notify_ev)
1419b38a362fSTomas Winkler 		goto out;
1420b38a362fSTomas Winkler 
1421b38a362fSTomas Winkler 	if (!block)
1422b38a362fSTomas Winkler 		return -EAGAIN;
1423b38a362fSTomas Winkler 
1424b38a362fSTomas Winkler 	mutex_unlock(&dev->device_lock);
1425b38a362fSTomas Winkler 	rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev);
1426b38a362fSTomas Winkler 	mutex_lock(&dev->device_lock);
1427b38a362fSTomas Winkler 
1428b38a362fSTomas Winkler 	if (rets < 0)
1429b38a362fSTomas Winkler 		return rets;
1430b38a362fSTomas Winkler 
1431b38a362fSTomas Winkler out:
1432b38a362fSTomas Winkler 	*notify_ev = cl->notify_ev;
1433b38a362fSTomas Winkler 	cl->notify_ev = false;
1434b38a362fSTomas Winkler 	return 0;
1435b38a362fSTomas Winkler }
1436b38a362fSTomas Winkler 
1437b38a362fSTomas Winkler /**
1438393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
14399ca9050bSTomas Winkler  *
144090e0b5f1STomas Winkler  * @cl: host client
1441ce23139cSAlexander Usyskin  * @length: number of bytes to read
1442bca67d68STomas Winkler  * @fp: pointer to file structure
14439ca9050bSTomas Winkler  *
1444a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
14459ca9050bSTomas Winkler  */
1446f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
14479ca9050bSTomas Winkler {
144890e0b5f1STomas Winkler 	struct mei_device *dev;
14499ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
14509ca9050bSTomas Winkler 	int rets;
14519ca9050bSTomas Winkler 
145290e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
145390e0b5f1STomas Winkler 		return -ENODEV;
145490e0b5f1STomas Winkler 
145590e0b5f1STomas Winkler 	dev = cl->dev;
145690e0b5f1STomas Winkler 
1457b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
14589ca9050bSTomas Winkler 		return -ENODEV;
14599ca9050bSTomas Winkler 
1460d49ed64aSAlexander Usyskin 	if (!mei_me_cl_is_active(cl->me_cl)) {
1461d49ed64aSAlexander Usyskin 		cl_err(dev, cl, "no such me client\n");
14627ca96aa2SAlexander Usyskin 		return  -ENOTTY;
14639ca9050bSTomas Winkler 	}
14641df629efSAlexander Usyskin 
14659d27e73cSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl)
1466e51dfa5aSAlexander Usyskin 		return 0;
1467e51dfa5aSAlexander Usyskin 
146846978adaSAlexander Usyskin 	/* HW currently supports only one pending read */
146946978adaSAlexander Usyskin 	if (cl->rx_flow_ctrl_creds)
147046978adaSAlexander Usyskin 		return -EBUSY;
147146978adaSAlexander Usyskin 
14723030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
14731df629efSAlexander Usyskin 	if (!cb)
14741df629efSAlexander Usyskin 		return -ENOMEM;
14751df629efSAlexander Usyskin 
14762bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
147704bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
14782bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
147904bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
14801df629efSAlexander Usyskin 		goto nortpm;
148104bb139aSTomas Winkler 	}
148204bb139aSTomas Winkler 
148346978adaSAlexander Usyskin 	rets = 0;
14846aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
148586113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
148686113500SAlexander Usyskin 		if (rets < 0)
148704bb139aSTomas Winkler 			goto out;
148804bb139aSTomas Winkler 
148946978adaSAlexander Usyskin 		list_move_tail(&cb->list, &cl->rd_pending);
14909ca9050bSTomas Winkler 	}
149146978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds++;
1492accb884bSChao Bi 
149304bb139aSTomas Winkler out:
149404bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
14952bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
14962bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
14971df629efSAlexander Usyskin nortpm:
149804bb139aSTomas Winkler 	if (rets)
14999ca9050bSTomas Winkler 		mei_io_cb_free(cb);
150004bb139aSTomas Winkler 
15019ca9050bSTomas Winkler 	return rets;
15029ca9050bSTomas Winkler }
15039ca9050bSTomas Winkler 
1504074b4c01STomas Winkler /**
15059d098192STomas Winkler  * mei_cl_irq_write - write a message to device
150621767546STomas Winkler  *	from the interrupt thread context
150721767546STomas Winkler  *
150821767546STomas Winkler  * @cl: client
150921767546STomas Winkler  * @cb: callback block.
151021767546STomas Winkler  * @cmpl_list: complete list.
151121767546STomas Winkler  *
1512a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
151321767546STomas Winkler  */
15149d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
15159d098192STomas Winkler 		     struct mei_cl_cb *cmpl_list)
151621767546STomas Winkler {
1517136698e5STomas Winkler 	struct mei_device *dev;
1518136698e5STomas Winkler 	struct mei_msg_data *buf;
151921767546STomas Winkler 	struct mei_msg_hdr mei_hdr;
1520136698e5STomas Winkler 	size_t len;
1521136698e5STomas Winkler 	u32 msg_slots;
15229d098192STomas Winkler 	int slots;
15232ebf8c94STomas Winkler 	int rets;
1524b8b73035SAlexander Usyskin 	bool first_chunk;
152521767546STomas Winkler 
1526136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1527136698e5STomas Winkler 		return -ENODEV;
1528136698e5STomas Winkler 
1529136698e5STomas Winkler 	dev = cl->dev;
1530136698e5STomas Winkler 
15315db7514dSTomas Winkler 	buf = &cb->buf;
1532136698e5STomas Winkler 
1533b8b73035SAlexander Usyskin 	first_chunk = cb->buf_idx == 0;
1534b8b73035SAlexander Usyskin 
15354034b81bSTomas Winkler 	rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
1536136698e5STomas Winkler 	if (rets < 0)
1537136698e5STomas Winkler 		return rets;
1538136698e5STomas Winkler 
1539136698e5STomas Winkler 	if (rets == 0) {
1540136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1541136698e5STomas Winkler 		return 0;
1542136698e5STomas Winkler 	}
1543136698e5STomas Winkler 
15449d098192STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1545136698e5STomas Winkler 	len = buf->size - cb->buf_idx;
1546136698e5STomas Winkler 	msg_slots = mei_data2slots(len);
1547136698e5STomas Winkler 
15481df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1549d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
155021767546STomas Winkler 	mei_hdr.reserved = 0;
1551479327fcSTomas Winkler 	mei_hdr.internal = cb->internal;
155221767546STomas Winkler 
15539d098192STomas Winkler 	if (slots >= msg_slots) {
155421767546STomas Winkler 		mei_hdr.length = len;
155521767546STomas Winkler 		mei_hdr.msg_complete = 1;
155621767546STomas Winkler 	/* Split the message only if we can write the whole host buffer */
15579d098192STomas Winkler 	} else if (slots == dev->hbuf_depth) {
15589d098192STomas Winkler 		msg_slots = slots;
15599d098192STomas Winkler 		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
156021767546STomas Winkler 		mei_hdr.length = len;
156121767546STomas Winkler 		mei_hdr.msg_complete = 0;
156221767546STomas Winkler 	} else {
156321767546STomas Winkler 		/* wait for next time the host buffer is empty */
156421767546STomas Winkler 		return 0;
156521767546STomas Winkler 	}
156621767546STomas Winkler 
156735bf7692SAlexander Usyskin 	cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
15685db7514dSTomas Winkler 			cb->buf.size, cb->buf_idx);
156921767546STomas Winkler 
1570136698e5STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
15712ebf8c94STomas Winkler 	if (rets) {
15722ebf8c94STomas Winkler 		cl->status = rets;
157321767546STomas Winkler 		list_move_tail(&cb->list, &cmpl_list->list);
15742ebf8c94STomas Winkler 		return rets;
157521767546STomas Winkler 	}
157621767546STomas Winkler 
157721767546STomas Winkler 	cl->status = 0;
15784dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
157921767546STomas Winkler 	cb->buf_idx += mei_hdr.length;
15808660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
15814dfaa9f7STomas Winkler 
1582b8b73035SAlexander Usyskin 	if (first_chunk) {
15834034b81bSTomas Winkler 		if (mei_cl_tx_flow_ctrl_creds_reduce(cl))
15842ebf8c94STomas Winkler 			return -EIO;
158521767546STomas Winkler 	}
158621767546STomas Winkler 
1587b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
1588b8b73035SAlexander Usyskin 		list_move_tail(&cb->list, &dev->write_waiting_list.list);
1589b8b73035SAlexander Usyskin 
159021767546STomas Winkler 	return 0;
159121767546STomas Winkler }
159221767546STomas Winkler 
159321767546STomas Winkler /**
15944234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1595a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
15964234a6deSTomas Winkler  *
15974234a6deSTomas Winkler  * @cl: host client
1598a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
1599ce23139cSAlexander Usyskin  * @blocking: block until completed
16004234a6deSTomas Winkler  *
1601a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
16024234a6deSTomas Winkler  */
16034234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
16044234a6deSTomas Winkler {
16054234a6deSTomas Winkler 	struct mei_device *dev;
16064234a6deSTomas Winkler 	struct mei_msg_data *buf;
16074234a6deSTomas Winkler 	struct mei_msg_hdr mei_hdr;
160823253c31SAlexander Usyskin 	int size;
16094234a6deSTomas Winkler 	int rets;
16104234a6deSTomas Winkler 
16114234a6deSTomas Winkler 
16124234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
16134234a6deSTomas Winkler 		return -ENODEV;
16144234a6deSTomas Winkler 
16154234a6deSTomas Winkler 	if (WARN_ON(!cb))
16164234a6deSTomas Winkler 		return -EINVAL;
16174234a6deSTomas Winkler 
16184234a6deSTomas Winkler 	dev = cl->dev;
16194234a6deSTomas Winkler 
16205db7514dSTomas Winkler 	buf = &cb->buf;
162123253c31SAlexander Usyskin 	size = buf->size;
16224234a6deSTomas Winkler 
162323253c31SAlexander Usyskin 	cl_dbg(dev, cl, "size=%d\n", size);
16244234a6deSTomas Winkler 
16252bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
162604bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
16272bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
162804bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
16296cbb097fSAlexander Usyskin 		goto free;
163004bb139aSTomas Winkler 	}
16314234a6deSTomas Winkler 
16326aae48ffSTomas Winkler 	cb->buf_idx = 0;
16336aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
16346aae48ffSTomas Winkler 
16351df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1636d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
16376aae48ffSTomas Winkler 	mei_hdr.reserved = 0;
16386aae48ffSTomas Winkler 	mei_hdr.msg_complete = 0;
16396aae48ffSTomas Winkler 	mei_hdr.internal = cb->internal;
16404234a6deSTomas Winkler 
16414034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds(cl);
16424234a6deSTomas Winkler 	if (rets < 0)
16434234a6deSTomas Winkler 		goto err;
16444234a6deSTomas Winkler 
16456aae48ffSTomas Winkler 	if (rets == 0) {
16466aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
164723253c31SAlexander Usyskin 		rets = size;
16484234a6deSTomas Winkler 		goto out;
16494234a6deSTomas Winkler 	}
16506aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
16516aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
165223253c31SAlexander Usyskin 		rets = size;
16536aae48ffSTomas Winkler 		goto out;
16546aae48ffSTomas Winkler 	}
16554234a6deSTomas Winkler 
16564234a6deSTomas Winkler 	/* Check for a maximum length */
165723253c31SAlexander Usyskin 	if (size > mei_hbuf_max_len(dev)) {
16584234a6deSTomas Winkler 		mei_hdr.length = mei_hbuf_max_len(dev);
16594234a6deSTomas Winkler 		mei_hdr.msg_complete = 0;
16604234a6deSTomas Winkler 	} else {
166123253c31SAlexander Usyskin 		mei_hdr.length = size;
16624234a6deSTomas Winkler 		mei_hdr.msg_complete = 1;
16634234a6deSTomas Winkler 	}
16644234a6deSTomas Winkler 
16652ebf8c94STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data);
16662ebf8c94STomas Winkler 	if (rets)
16674234a6deSTomas Winkler 		goto err;
16684234a6deSTomas Winkler 
16694034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
1670b8b73035SAlexander Usyskin 	if (rets)
1671b8b73035SAlexander Usyskin 		goto err;
1672b8b73035SAlexander Usyskin 
16734234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
16744234a6deSTomas Winkler 	cb->buf_idx = mei_hdr.length;
16758660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
16764234a6deSTomas Winkler 
16774234a6deSTomas Winkler out:
1678b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
16794234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_waiting_list.list);
1680b8b73035SAlexander Usyskin 	else
16814234a6deSTomas Winkler 		list_add_tail(&cb->list, &dev->write_list.list);
16824234a6deSTomas Winkler 
168323253c31SAlexander Usyskin 	cb = NULL;
16844234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
16854234a6deSTomas Winkler 
16864234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
16877ca96aa2SAlexander Usyskin 		rets = wait_event_interruptible(cl->tx_wait,
16880faf6a3bSAlexander Usyskin 				cl->writing_state == MEI_WRITE_COMPLETE ||
16890faf6a3bSAlexander Usyskin 				(!mei_cl_is_connected(cl)));
16907ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
16917ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
16927ca96aa2SAlexander Usyskin 		if (rets) {
16934234a6deSTomas Winkler 			if (signal_pending(current))
16944234a6deSTomas Winkler 				rets = -EINTR;
16957ca96aa2SAlexander Usyskin 			goto err;
16964234a6deSTomas Winkler 		}
16970faf6a3bSAlexander Usyskin 		if (cl->writing_state != MEI_WRITE_COMPLETE) {
16980faf6a3bSAlexander Usyskin 			rets = -EFAULT;
16990faf6a3bSAlexander Usyskin 			goto err;
17000faf6a3bSAlexander Usyskin 		}
17014234a6deSTomas Winkler 	}
17027ca96aa2SAlexander Usyskin 
170323253c31SAlexander Usyskin 	rets = size;
17044234a6deSTomas Winkler err:
170504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
17062bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
17072bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
17086cbb097fSAlexander Usyskin free:
17096cbb097fSAlexander Usyskin 	mei_io_cb_free(cb);
171004bb139aSTomas Winkler 
17114234a6deSTomas Winkler 	return rets;
17124234a6deSTomas Winkler }
17134234a6deSTomas Winkler 
17144234a6deSTomas Winkler 
1715db086fa9STomas Winkler /**
1716db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
1717db086fa9STomas Winkler  *
1718db086fa9STomas Winkler  * @cl: private data of the file object.
1719db086fa9STomas Winkler  * @cb: callback block.
1720db086fa9STomas Winkler  */
1721db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
1722db086fa9STomas Winkler {
1723a1809d38SAlexander Usyskin 	struct mei_device *dev = cl->dev;
1724a1809d38SAlexander Usyskin 
17253c666182STomas Winkler 	switch (cb->fop_type) {
17263c666182STomas Winkler 	case MEI_FOP_WRITE:
1727db086fa9STomas Winkler 		mei_io_cb_free(cb);
1728db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
1729a1809d38SAlexander Usyskin 		if (waitqueue_active(&cl->tx_wait)) {
1730db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
1731a1809d38SAlexander Usyskin 		} else {
1732a1809d38SAlexander Usyskin 			pm_runtime_mark_last_busy(dev->dev);
1733a1809d38SAlexander Usyskin 			pm_request_autosuspend(dev->dev);
1734a1809d38SAlexander Usyskin 		}
17353c666182STomas Winkler 		break;
1736db086fa9STomas Winkler 
17373c666182STomas Winkler 	case MEI_FOP_READ:
1738a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_completed);
173946978adaSAlexander Usyskin 		if (!mei_cl_is_fixed_address(cl) &&
174046978adaSAlexander Usyskin 		    !WARN_ON(!cl->rx_flow_ctrl_creds))
174146978adaSAlexander Usyskin 			cl->rx_flow_ctrl_creds--;
1742a1f9ae2bSTomas Winkler 		if (!mei_cl_bus_rx_event(cl))
1743a1f9ae2bSTomas Winkler 			wake_up_interruptible(&cl->rx_wait);
17443c666182STomas Winkler 		break;
1745db086fa9STomas Winkler 
17463c666182STomas Winkler 	case MEI_FOP_CONNECT:
17473c666182STomas Winkler 	case MEI_FOP_DISCONNECT:
174851678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_STOP:
174951678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_START:
17503c666182STomas Winkler 		if (waitqueue_active(&cl->wait))
17513c666182STomas Winkler 			wake_up(&cl->wait);
17523c666182STomas Winkler 
17533c666182STomas Winkler 		break;
17546a8d648cSAlexander Usyskin 	case MEI_FOP_DISCONNECT_RSP:
17556a8d648cSAlexander Usyskin 		mei_io_cb_free(cb);
17566a8d648cSAlexander Usyskin 		mei_cl_set_disconnected(cl);
17576a8d648cSAlexander Usyskin 		break;
17583c666182STomas Winkler 	default:
17593c666182STomas Winkler 		BUG_ON(0);
1760db086fa9STomas Winkler 	}
1761db086fa9STomas Winkler }
1762db086fa9STomas Winkler 
17634234a6deSTomas Winkler 
17644234a6deSTomas Winkler /**
1765074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
1766074b4c01STomas Winkler  *
1767a8605ea2SAlexander Usyskin  * @dev: mei device
1768074b4c01STomas Winkler  */
1769074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
1770074b4c01STomas Winkler {
177131f88f57STomas Winkler 	struct mei_cl *cl;
1772074b4c01STomas Winkler 
17733c666182STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link)
17743c666182STomas Winkler 		mei_cl_set_disconnected(cl);
1775074b4c01STomas Winkler }
1776