xref: /openbmc/linux/drivers/misc/mei/client.c (revision 962ff7bc)
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  *
382962ff7bcSAlexander Usyskin  * @head:  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  */
386962ff7bcSAlexander Usyskin static void __mei_io_list_flush(struct list_head *head,
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 */
392962ff7bcSAlexander Usyskin 	list_for_each_entry_safe(cb, next, head, 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  *
404962ff7bcSAlexander Usyskin  * @head: An instance of our list structure
405928fa666STomas Winkler  * @cl: host client
406928fa666STomas Winkler  */
407962ff7bcSAlexander Usyskin static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl)
408928fa666STomas Winkler {
409962ff7bcSAlexander Usyskin 	__mei_io_list_flush(head, cl, false);
410928fa666STomas Winkler }
411928fa666STomas Winkler 
412928fa666STomas Winkler /**
413928fa666STomas Winkler  * mei_io_list_free - removes cb belonging to cl and free them
414928fa666STomas Winkler  *
415962ff7bcSAlexander Usyskin  * @head: An instance of our list structure
416928fa666STomas Winkler  * @cl: host client
417928fa666STomas Winkler  */
418962ff7bcSAlexander Usyskin static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl)
419928fa666STomas Winkler {
420962ff7bcSAlexander Usyskin 	__mei_io_list_flush(head, 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
428967b274eSAlexander Usyskin  * @fop_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
462967b274eSAlexander Usyskin  * @fop_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 
482962ff7bcSAlexander Usyskin 	list_add_tail(&cb->list, &cl->dev->ctrl_wr_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);
549a9bed610STomas Winkler 
550a9bed610STomas Winkler 	mei_cl_read_cb_flush(cl, fp);
551a9bed610STomas Winkler 
5529ca9050bSTomas Winkler 	return 0;
5539ca9050bSTomas Winkler }
5549ca9050bSTomas Winkler 
5559ca9050bSTomas Winkler 
5569ca9050bSTomas Winkler /**
55783ce0741SAlexander Usyskin  * mei_cl_init - initializes cl.
5589ca9050bSTomas Winkler  *
5599ca9050bSTomas Winkler  * @cl: host client to be initialized
5609ca9050bSTomas Winkler  * @dev: mei device
5619ca9050bSTomas Winkler  */
5629ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
5639ca9050bSTomas Winkler {
5649ca9050bSTomas Winkler 	memset(cl, 0, sizeof(struct mei_cl));
5659ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
5669ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
5679ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
568b38a362fSTomas Winkler 	init_waitqueue_head(&cl->ev_wait);
569a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_completed);
570a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_pending);
5719ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
5729ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
573bd47b526SAlexander Usyskin 	cl->state = MEI_FILE_UNINITIALIZED;
5749ca9050bSTomas Winkler 	cl->dev = dev;
5759ca9050bSTomas Winkler }
5769ca9050bSTomas Winkler 
5779ca9050bSTomas Winkler /**
5789ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
5799ca9050bSTomas Winkler  *
5809ca9050bSTomas Winkler  * @dev: mei device
581a8605ea2SAlexander Usyskin  * Return:  The allocated file or NULL on failure
5829ca9050bSTomas Winkler  */
5839ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
5849ca9050bSTomas Winkler {
5859ca9050bSTomas Winkler 	struct mei_cl *cl;
5869ca9050bSTomas Winkler 
5879ca9050bSTomas Winkler 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
5889ca9050bSTomas Winkler 	if (!cl)
5899ca9050bSTomas Winkler 		return NULL;
5909ca9050bSTomas Winkler 
5919ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
5929ca9050bSTomas Winkler 
5939ca9050bSTomas Winkler 	return cl;
5949ca9050bSTomas Winkler }
5959ca9050bSTomas Winkler 
59690e0b5f1STomas Winkler /**
5973908be6fSAlexander Usyskin  * mei_cl_link - allocate host id in the host map
5989ca9050bSTomas Winkler  *
5993908be6fSAlexander Usyskin  * @cl: host client
600393b148fSMasanari Iida  *
601a8605ea2SAlexander Usyskin  * Return: 0 on success
6029ca9050bSTomas Winkler  *	-EINVAL on incorrect values
60303b8d341STomas Winkler  *	-EMFILE if open count exceeded.
6049ca9050bSTomas Winkler  */
6057851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl)
6069ca9050bSTomas Winkler {
60790e0b5f1STomas Winkler 	struct mei_device *dev;
60822f96a0eSTomas Winkler 	long open_handle_count;
6097851e008SAlexander Usyskin 	int id;
6109ca9050bSTomas Winkler 
611781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
6129ca9050bSTomas Winkler 		return -EINVAL;
6139ca9050bSTomas Winkler 
61490e0b5f1STomas Winkler 	dev = cl->dev;
61590e0b5f1STomas Winkler 
6167851e008SAlexander Usyskin 	id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
617781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
6182bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
619e036cc57STomas Winkler 		return -EMFILE;
620e036cc57STomas Winkler 	}
621e036cc57STomas Winkler 
62222f96a0eSTomas Winkler 	open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
62322f96a0eSTomas Winkler 	if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6242bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
625e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
626e036cc57STomas Winkler 		return -EMFILE;
6279ca9050bSTomas Winkler 	}
628781d0d89STomas Winkler 
629781d0d89STomas Winkler 	dev->open_handle_count++;
630781d0d89STomas Winkler 
631781d0d89STomas Winkler 	cl->host_client_id = id;
632781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
633781d0d89STomas Winkler 
634781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
635781d0d89STomas Winkler 
636781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
637781d0d89STomas Winkler 
638c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
639781d0d89STomas Winkler 	return 0;
640781d0d89STomas Winkler }
641781d0d89STomas Winkler 
6429ca9050bSTomas Winkler /**
643d49ed64aSAlexander Usyskin  * mei_cl_unlink - remove host client from the list
6449ca9050bSTomas Winkler  *
645393b148fSMasanari Iida  * @cl: host client
646ce23139cSAlexander Usyskin  *
647ce23139cSAlexander Usyskin  * Return: always 0
6489ca9050bSTomas Winkler  */
64990e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6509ca9050bSTomas Winkler {
65190e0b5f1STomas Winkler 	struct mei_device *dev;
65290e0b5f1STomas Winkler 
653781d0d89STomas Winkler 	/* don't shout on error exit path */
654781d0d89STomas Winkler 	if (!cl)
655781d0d89STomas Winkler 		return 0;
656781d0d89STomas Winkler 
657fdd9b865SAlexander Usyskin 	/* amthif might not be initialized */
6588e9a4a9aSTomas Winkler 	if (!cl->dev)
6598e9a4a9aSTomas Winkler 		return 0;
66090e0b5f1STomas Winkler 
66190e0b5f1STomas Winkler 	dev = cl->dev;
66290e0b5f1STomas Winkler 
663a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
664a14c44d8STomas Winkler 
66522f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
66622f96a0eSTomas Winkler 		dev->open_handle_count--;
66722f96a0eSTomas Winkler 
66822f96a0eSTomas Winkler 	/* never clear the 0 bit */
66922f96a0eSTomas Winkler 	if (cl->host_client_id)
67022f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
67122f96a0eSTomas Winkler 
67222f96a0eSTomas Winkler 	list_del_init(&cl->link);
67322f96a0eSTomas Winkler 
674bd47b526SAlexander Usyskin 	cl->state = MEI_FILE_UNINITIALIZED;
6757c7a6077SAlexander Usyskin 	cl->writing_state = MEI_IDLE;
6767c7a6077SAlexander Usyskin 
6777c7a6077SAlexander Usyskin 	WARN_ON(!list_empty(&cl->rd_completed) ||
6787c7a6077SAlexander Usyskin 		!list_empty(&cl->rd_pending) ||
6797c7a6077SAlexander Usyskin 		!list_empty(&cl->link));
68022f96a0eSTomas Winkler 
68190e0b5f1STomas Winkler 	return 0;
6829ca9050bSTomas Winkler }
6839ca9050bSTomas Winkler 
684025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev)
6859ca9050bSTomas Winkler {
6869ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
6876adb8efbSTomas Winkler 	dev->reset_count = 0;
68804bb139aSTomas Winkler 
689025fb792SAlexander Usyskin 	schedule_work(&dev->bus_rescan_work);
6906009595aSTomas Winkler 
6912bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
6922bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
693d5f8e166SAlexander Usyskin 	pm_request_autosuspend(dev->dev);
6949ca9050bSTomas Winkler }
6959ca9050bSTomas Winkler 
6966aae48ffSTomas Winkler /**
697a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
6986aae48ffSTomas Winkler  *
6996aae48ffSTomas Winkler  * @dev: the device structure
700a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
7016aae48ffSTomas Winkler  */
7026aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
7036aae48ffSTomas Winkler {
70404bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
7053dc196eaSAlexander Usyskin 	    mei_pg_in_transition(dev)) {
7062bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
70704bb139aSTomas Winkler 		return false;
70804bb139aSTomas Winkler 	}
70904bb139aSTomas Winkler 
7106aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
7112bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
7126aae48ffSTomas Winkler 		return false;
7136aae48ffSTomas Winkler 	}
7146aae48ffSTomas Winkler 
7156aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7166aae48ffSTomas Winkler 
7176aae48ffSTomas Winkler 	return true;
7186aae48ffSTomas Winkler }
7199ca9050bSTomas Winkler 
7209ca9050bSTomas Winkler /**
721a4307fe4SAlexander Usyskin  * mei_cl_wake_all - wake up readers, writers and event waiters so
722a4307fe4SAlexander Usyskin  *                 they can be interrupted
723a4307fe4SAlexander Usyskin  *
724a4307fe4SAlexander Usyskin  * @cl: host client
725a4307fe4SAlexander Usyskin  */
726a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl)
727a4307fe4SAlexander Usyskin {
728a4307fe4SAlexander Usyskin 	struct mei_device *dev = cl->dev;
729a4307fe4SAlexander Usyskin 
730a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
731a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->rx_wait)) {
732a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up reading client!\n");
733a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->rx_wait);
734a4307fe4SAlexander Usyskin 	}
735a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
736a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->tx_wait)) {
737a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up writing client!\n");
738a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->tx_wait);
739a4307fe4SAlexander Usyskin 	}
740a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
741a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->ev_wait)) {
742a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
743a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->ev_wait);
744a4307fe4SAlexander Usyskin 	}
7457ff4bdd4SAlexander Usyskin 	/* synchronized under device mutex */
7467ff4bdd4SAlexander Usyskin 	if (waitqueue_active(&cl->wait)) {
7477ff4bdd4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up ctrl write clients!\n");
74869f1804aSAlexander Usyskin 		wake_up(&cl->wait);
7497ff4bdd4SAlexander Usyskin 	}
750a4307fe4SAlexander Usyskin }
751a4307fe4SAlexander Usyskin 
752a4307fe4SAlexander Usyskin /**
7533c666182STomas Winkler  * mei_cl_set_disconnected - set disconnected state and clear
7543c666182STomas Winkler  *   associated states and resources
7553c666182STomas Winkler  *
7563c666182STomas Winkler  * @cl: host client
7573c666182STomas Winkler  */
758669c256cSAlexander Usyskin static void mei_cl_set_disconnected(struct mei_cl *cl)
7593c666182STomas Winkler {
7603c666182STomas Winkler 	struct mei_device *dev = cl->dev;
7613c666182STomas Winkler 
7623c666182STomas Winkler 	if (cl->state == MEI_FILE_DISCONNECTED ||
763bd47b526SAlexander Usyskin 	    cl->state <= MEI_FILE_INITIALIZING)
7643c666182STomas Winkler 		return;
7653c666182STomas Winkler 
7663c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTED;
767a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_list, cl);
768a4307fe4SAlexander Usyskin 	mei_io_list_free(&dev->write_waiting_list, cl);
7693c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
7703c666182STomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
7716537ae2fSTomas Winkler 	mei_io_list_free(&dev->amthif_cmd_list, cl);
772a4307fe4SAlexander Usyskin 	mei_cl_wake_all(cl);
77346978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds = 0;
7744034b81bSTomas Winkler 	cl->tx_flow_ctrl_creds = 0;
7753c666182STomas Winkler 	cl->timer_count = 0;
776d49ed64aSAlexander Usyskin 
7775d882460SAlexander Usyskin 	mei_cl_bus_module_put(cl);
7785d882460SAlexander Usyskin 
779a03d77f6SAlexander Usyskin 	if (!cl->me_cl)
780a03d77f6SAlexander Usyskin 		return;
781a03d77f6SAlexander Usyskin 
782a03d77f6SAlexander Usyskin 	if (!WARN_ON(cl->me_cl->connect_count == 0))
783a03d77f6SAlexander Usyskin 		cl->me_cl->connect_count--;
784a03d77f6SAlexander Usyskin 
785c241e9b1SAlexander Usyskin 	if (cl->me_cl->connect_count == 0)
7864034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds = 0;
787c241e9b1SAlexander Usyskin 
788d49ed64aSAlexander Usyskin 	mei_me_cl_put(cl->me_cl);
789d49ed64aSAlexander Usyskin 	cl->me_cl = NULL;
7903c666182STomas Winkler }
7913c666182STomas Winkler 
792a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
793a03d77f6SAlexander Usyskin {
7941df629efSAlexander Usyskin 	if (!mei_me_cl_get(me_cl))
795a03d77f6SAlexander Usyskin 		return -ENOENT;
796a03d77f6SAlexander Usyskin 
7971df629efSAlexander Usyskin 	/* only one connection is allowed for fixed address clients */
7981df629efSAlexander Usyskin 	if (me_cl->props.fixed_address) {
7991df629efSAlexander Usyskin 		if (me_cl->connect_count) {
8001df629efSAlexander Usyskin 			mei_me_cl_put(me_cl);
8011df629efSAlexander Usyskin 			return -EBUSY;
8021df629efSAlexander Usyskin 		}
8031df629efSAlexander Usyskin 	}
8041df629efSAlexander Usyskin 
8051df629efSAlexander Usyskin 	cl->me_cl = me_cl;
806a03d77f6SAlexander Usyskin 	cl->state = MEI_FILE_CONNECTING;
807a03d77f6SAlexander Usyskin 	cl->me_cl->connect_count++;
808a03d77f6SAlexander Usyskin 
809a03d77f6SAlexander Usyskin 	return 0;
810a03d77f6SAlexander Usyskin }
811a03d77f6SAlexander Usyskin 
8123c666182STomas Winkler /*
8133c666182STomas Winkler  * mei_cl_send_disconnect - send disconnect request
8143c666182STomas Winkler  *
8153c666182STomas Winkler  * @cl: host client
8163c666182STomas Winkler  * @cb: callback block
8173c666182STomas Winkler  *
8183c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8193c666182STomas Winkler  */
8203c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
8213c666182STomas Winkler {
8223c666182STomas Winkler 	struct mei_device *dev;
8233c666182STomas Winkler 	int ret;
8243c666182STomas Winkler 
8253c666182STomas Winkler 	dev = cl->dev;
8263c666182STomas Winkler 
8273c666182STomas Winkler 	ret = mei_hbm_cl_disconnect_req(dev, cl);
8283c666182STomas Winkler 	cl->status = ret;
8293c666182STomas Winkler 	if (ret) {
8303c666182STomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
8313c666182STomas Winkler 		return ret;
8323c666182STomas Winkler 	}
8333c666182STomas Winkler 
834962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
8353c666182STomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
8361892fc2eSAlexander Usyskin 	mei_schedule_stall_timer(dev);
8373c666182STomas Winkler 
8383c666182STomas Winkler 	return 0;
8393c666182STomas Winkler }
8403c666182STomas Winkler 
8413c666182STomas Winkler /**
8423c666182STomas Winkler  * mei_cl_irq_disconnect - processes close related operation from
8433c666182STomas Winkler  *	interrupt thread context - send disconnect request
8443c666182STomas Winkler  *
8453c666182STomas Winkler  * @cl: client
8463c666182STomas Winkler  * @cb: callback block.
8473c666182STomas Winkler  * @cmpl_list: complete list.
8483c666182STomas Winkler  *
8493c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8503c666182STomas Winkler  */
8513c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
852962ff7bcSAlexander Usyskin 			  struct list_head *cmpl_list)
8533c666182STomas Winkler {
8543c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8553c666182STomas Winkler 	u32 msg_slots;
8563c666182STomas Winkler 	int slots;
8573c666182STomas Winkler 	int ret;
8583c666182STomas Winkler 
8593c666182STomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
8603c666182STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
8613c666182STomas Winkler 
8623c666182STomas Winkler 	if (slots < msg_slots)
8633c666182STomas Winkler 		return -EMSGSIZE;
8643c666182STomas Winkler 
8653c666182STomas Winkler 	ret = mei_cl_send_disconnect(cl, cb);
8663c666182STomas Winkler 	if (ret)
867962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
8683c666182STomas Winkler 
8693c666182STomas Winkler 	return ret;
8703c666182STomas Winkler }
8713c666182STomas Winkler 
8723c666182STomas Winkler /**
87318901357SAlexander Usyskin  * __mei_cl_disconnect - disconnect host client from the me one
87418901357SAlexander Usyskin  *     internal function runtime pm has to be already acquired
8759ca9050bSTomas Winkler  *
87690e0b5f1STomas Winkler  * @cl: host client
8779ca9050bSTomas Winkler  *
878a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
8799ca9050bSTomas Winkler  */
88018901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl)
8819ca9050bSTomas Winkler {
88290e0b5f1STomas Winkler 	struct mei_device *dev;
8839ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
884fe2f17ebSAlexander Usyskin 	int rets;
8859ca9050bSTomas Winkler 
88690e0b5f1STomas Winkler 	dev = cl->dev;
88790e0b5f1STomas Winkler 
8883c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTING;
8893c666182STomas Winkler 
8903030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
8913030dc05STomas Winkler 	if (!cb) {
8923030dc05STomas Winkler 		rets = -ENOMEM;
8933c666182STomas Winkler 		goto out;
8943030dc05STomas Winkler 	}
8959ca9050bSTomas Winkler 
8963c666182STomas Winkler 	if (mei_hbuf_acquire(dev)) {
8973c666182STomas Winkler 		rets = mei_cl_send_disconnect(cl, cb);
8983c666182STomas Winkler 		if (rets) {
8993c666182STomas Winkler 			cl_err(dev, cl, "failed to disconnect.\n");
9003c666182STomas Winkler 			goto out;
9019ca9050bSTomas Winkler 		}
9023c666182STomas Winkler 	}
9033c666182STomas Winkler 
9049ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
9057ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
9067ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECT_REPLY ||
9077ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECTED,
9089ca9050bSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
9099ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
910fe2f17ebSAlexander Usyskin 
9113c666182STomas Winkler 	rets = cl->status;
9127ff4bdd4SAlexander Usyskin 	if (cl->state != MEI_FILE_DISCONNECT_REPLY &&
9137ff4bdd4SAlexander Usyskin 	    cl->state != MEI_FILE_DISCONNECTED) {
914fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
915fe2f17ebSAlexander Usyskin 		rets = -ETIME;
9169ca9050bSTomas Winkler 	}
9179ca9050bSTomas Winkler 
9183c666182STomas Winkler out:
9193c666182STomas Winkler 	/* we disconnect also on error */
9203c666182STomas Winkler 	mei_cl_set_disconnected(cl);
9213c666182STomas Winkler 	if (!rets)
9223c666182STomas Winkler 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
9233c666182STomas Winkler 
92418901357SAlexander Usyskin 	mei_io_cb_free(cb);
92518901357SAlexander Usyskin 	return rets;
92618901357SAlexander Usyskin }
92718901357SAlexander Usyskin 
92818901357SAlexander Usyskin /**
92918901357SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
93018901357SAlexander Usyskin  *
93118901357SAlexander Usyskin  * @cl: host client
93218901357SAlexander Usyskin  *
93318901357SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
93418901357SAlexander Usyskin  *
93518901357SAlexander Usyskin  * Return: 0 on success, <0 on failure.
93618901357SAlexander Usyskin  */
93718901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl)
93818901357SAlexander Usyskin {
93918901357SAlexander Usyskin 	struct mei_device *dev;
94018901357SAlexander Usyskin 	int rets;
94118901357SAlexander Usyskin 
94218901357SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
94318901357SAlexander Usyskin 		return -ENODEV;
94418901357SAlexander Usyskin 
94518901357SAlexander Usyskin 	dev = cl->dev;
94618901357SAlexander Usyskin 
94718901357SAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
94818901357SAlexander Usyskin 
94918901357SAlexander Usyskin 	if (!mei_cl_is_connected(cl))
95018901357SAlexander Usyskin 		return 0;
95118901357SAlexander Usyskin 
95218901357SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
95318901357SAlexander Usyskin 		mei_cl_set_disconnected(cl);
95418901357SAlexander Usyskin 		return 0;
95518901357SAlexander Usyskin 	}
95618901357SAlexander Usyskin 
95718901357SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
95818901357SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
95918901357SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
96018901357SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
96118901357SAlexander Usyskin 		return rets;
96218901357SAlexander Usyskin 	}
96318901357SAlexander Usyskin 
96418901357SAlexander Usyskin 	rets = __mei_cl_disconnect(cl);
96518901357SAlexander Usyskin 
96604bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
9672bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
9682bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
96904bb139aSTomas Winkler 
9709ca9050bSTomas Winkler 	return rets;
9719ca9050bSTomas Winkler }
9729ca9050bSTomas Winkler 
9739ca9050bSTomas Winkler 
9749ca9050bSTomas Winkler /**
97590e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
97690e0b5f1STomas Winkler  *    client with the same me client id is connecting
9779ca9050bSTomas Winkler  *
9789ca9050bSTomas Winkler  * @cl: private data of the file object
9799ca9050bSTomas Winkler  *
980a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
9819ca9050bSTomas Winkler  */
9820c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl)
9839ca9050bSTomas Winkler {
98490e0b5f1STomas Winkler 	struct mei_device *dev;
9850c53357cSTomas Winkler 	struct mei_cl_cb *cb;
98690e0b5f1STomas Winkler 
98790e0b5f1STomas Winkler 	dev = cl->dev;
98890e0b5f1STomas Winkler 
989962ff7bcSAlexander Usyskin 	list_for_each_entry(cb, &dev->ctrl_rd_list, list) {
9900c53357cSTomas Winkler 		if (cb->fop_type == MEI_FOP_CONNECT &&
991d49ed64aSAlexander Usyskin 		    mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
99290e0b5f1STomas Winkler 			return true;
9939ca9050bSTomas Winkler 	}
99490e0b5f1STomas Winkler 
99590e0b5f1STomas Winkler 	return false;
9969ca9050bSTomas Winkler }
9979ca9050bSTomas Winkler 
9989ca9050bSTomas Winkler /**
9990c53357cSTomas Winkler  * mei_cl_send_connect - send connect request
10000c53357cSTomas Winkler  *
10010c53357cSTomas Winkler  * @cl: host client
10020c53357cSTomas Winkler  * @cb: callback block
10030c53357cSTomas Winkler  *
10040c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10050c53357cSTomas Winkler  */
10060c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
10070c53357cSTomas Winkler {
10080c53357cSTomas Winkler 	struct mei_device *dev;
10090c53357cSTomas Winkler 	int ret;
10100c53357cSTomas Winkler 
10110c53357cSTomas Winkler 	dev = cl->dev;
10120c53357cSTomas Winkler 
10130c53357cSTomas Winkler 	ret = mei_hbm_cl_connect_req(dev, cl);
10140c53357cSTomas Winkler 	cl->status = ret;
10150c53357cSTomas Winkler 	if (ret) {
10160c53357cSTomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
10170c53357cSTomas Winkler 		return ret;
10180c53357cSTomas Winkler 	}
10190c53357cSTomas Winkler 
1020962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
10210c53357cSTomas Winkler 	cl->timer_count = MEI_CONNECT_TIMEOUT;
10221892fc2eSAlexander Usyskin 	mei_schedule_stall_timer(dev);
10230c53357cSTomas Winkler 	return 0;
10240c53357cSTomas Winkler }
10250c53357cSTomas Winkler 
10260c53357cSTomas Winkler /**
10270c53357cSTomas Winkler  * mei_cl_irq_connect - send connect request in irq_thread context
10280c53357cSTomas Winkler  *
10290c53357cSTomas Winkler  * @cl: host client
10300c53357cSTomas Winkler  * @cb: callback block
10310c53357cSTomas Winkler  * @cmpl_list: complete list
10320c53357cSTomas Winkler  *
10330c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10340c53357cSTomas Winkler  */
10350c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
1036962ff7bcSAlexander Usyskin 		       struct list_head *cmpl_list)
10370c53357cSTomas Winkler {
10380c53357cSTomas Winkler 	struct mei_device *dev = cl->dev;
10390c53357cSTomas Winkler 	u32 msg_slots;
10400c53357cSTomas Winkler 	int slots;
10410c53357cSTomas Winkler 	int rets;
10420c53357cSTomas Winkler 
10430c53357cSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
10440c53357cSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
10450c53357cSTomas Winkler 
10460c53357cSTomas Winkler 	if (mei_cl_is_other_connecting(cl))
10470c53357cSTomas Winkler 		return 0;
10480c53357cSTomas Winkler 
10490c53357cSTomas Winkler 	if (slots < msg_slots)
10500c53357cSTomas Winkler 		return -EMSGSIZE;
10510c53357cSTomas Winkler 
10520c53357cSTomas Winkler 	rets = mei_cl_send_connect(cl, cb);
10530c53357cSTomas Winkler 	if (rets)
1054962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
10550c53357cSTomas Winkler 
10560c53357cSTomas Winkler 	return rets;
10570c53357cSTomas Winkler }
10580c53357cSTomas Winkler 
10590c53357cSTomas Winkler /**
106083ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
10619f81abdaSTomas Winkler  *
10629f81abdaSTomas Winkler  * @cl: host client
1063d49ed64aSAlexander Usyskin  * @me_cl: me client
10643030dc05STomas Winkler  * @fp: pointer to file structure
10659f81abdaSTomas Winkler  *
10669f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
10679f81abdaSTomas Winkler  *
1068a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
10699f81abdaSTomas Winkler  */
1070d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
10713030dc05STomas Winkler 		   const struct file *fp)
10729f81abdaSTomas Winkler {
10739f81abdaSTomas Winkler 	struct mei_device *dev;
10749f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
10759f81abdaSTomas Winkler 	int rets;
10769f81abdaSTomas Winkler 
10771df629efSAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev || !me_cl))
10789f81abdaSTomas Winkler 		return -ENODEV;
10799f81abdaSTomas Winkler 
10809f81abdaSTomas Winkler 	dev = cl->dev;
10819f81abdaSTomas Winkler 
10825d882460SAlexander Usyskin 	if (!mei_cl_bus_module_get(cl))
10835d882460SAlexander Usyskin 		return -ENODEV;
10845d882460SAlexander Usyskin 
10851df629efSAlexander Usyskin 	rets = mei_cl_set_connecting(cl, me_cl);
10861df629efSAlexander Usyskin 	if (rets)
10875d882460SAlexander Usyskin 		goto nortpm;
10881df629efSAlexander Usyskin 
10891df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
10901df629efSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTED;
10915d882460SAlexander Usyskin 		rets = 0;
10925d882460SAlexander Usyskin 		goto nortpm;
10931df629efSAlexander Usyskin 	}
10941df629efSAlexander Usyskin 
10952bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
109604bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
10972bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
109804bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
10991df629efSAlexander Usyskin 		goto nortpm;
110004bb139aSTomas Winkler 	}
110104bb139aSTomas Winkler 
11023030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
11033030dc05STomas Winkler 	if (!cb) {
11043030dc05STomas Winkler 		rets = -ENOMEM;
11059f81abdaSTomas Winkler 		goto out;
11063030dc05STomas Winkler 	}
11070c53357cSTomas Winkler 
11086aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
11096aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
11100c53357cSTomas Winkler 		rets = mei_cl_send_connect(cl, cb);
11110c53357cSTomas Winkler 		if (rets)
11129f81abdaSTomas Winkler 			goto out;
11139f81abdaSTomas Winkler 	}
11149f81abdaSTomas Winkler 
11159f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
111612f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
11179f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
11187ff4bdd4SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECTED ||
111918901357SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
11203c666182STomas Winkler 			 cl->state == MEI_FILE_DISCONNECT_REPLY),
1121206ecfc2SFrode Isaksen 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
11229f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
11239f81abdaSTomas Winkler 
1124f3de9b63STomas Winkler 	if (!mei_cl_is_connected(cl)) {
112518901357SAlexander Usyskin 		if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
112618901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_rd_list, cl);
112718901357SAlexander Usyskin 			mei_io_list_flush(&dev->ctrl_wr_list, cl);
112818901357SAlexander Usyskin 			 /* ignore disconnect return valuue;
112918901357SAlexander Usyskin 			  * in case of failure reset will be invoked
113018901357SAlexander Usyskin 			  */
113118901357SAlexander Usyskin 			__mei_cl_disconnect(cl);
113218901357SAlexander Usyskin 			rets = -EFAULT;
113318901357SAlexander Usyskin 			goto out;
113418901357SAlexander Usyskin 		}
113518901357SAlexander Usyskin 
11360c53357cSTomas Winkler 		/* timeout or something went really wrong */
1137285e2996SAlexander Usyskin 		if (!cl->status)
1138285e2996SAlexander Usyskin 			cl->status = -EFAULT;
11399f81abdaSTomas Winkler 	}
11409f81abdaSTomas Winkler 
11419f81abdaSTomas Winkler 	rets = cl->status;
11429f81abdaSTomas Winkler out:
114304bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
11442bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
11452bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
114604bb139aSTomas Winkler 
11479f81abdaSTomas Winkler 	mei_io_cb_free(cb);
11480c53357cSTomas Winkler 
11491df629efSAlexander Usyskin nortpm:
11500c53357cSTomas Winkler 	if (!mei_cl_is_connected(cl))
11510c53357cSTomas Winkler 		mei_cl_set_disconnected(cl);
11520c53357cSTomas Winkler 
11539f81abdaSTomas Winkler 	return rets;
11549f81abdaSTomas Winkler }
11559f81abdaSTomas Winkler 
11569f81abdaSTomas Winkler /**
115703b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
115803b8d341STomas Winkler  *
115903b8d341STomas Winkler  * @dev: the device structure
116003b8d341STomas Winkler  *
116103b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
116203b8d341STomas Winkler  */
11637851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
116403b8d341STomas Winkler {
116503b8d341STomas Winkler 	struct mei_cl *cl;
116603b8d341STomas Winkler 	int ret;
116703b8d341STomas Winkler 
116803b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
116903b8d341STomas Winkler 	if (!cl) {
117003b8d341STomas Winkler 		ret = -ENOMEM;
117103b8d341STomas Winkler 		goto err;
117203b8d341STomas Winkler 	}
117303b8d341STomas Winkler 
11747851e008SAlexander Usyskin 	ret = mei_cl_link(cl);
117503b8d341STomas Winkler 	if (ret)
117603b8d341STomas Winkler 		goto err;
117703b8d341STomas Winkler 
117803b8d341STomas Winkler 	return cl;
117903b8d341STomas Winkler err:
118003b8d341STomas Winkler 	kfree(cl);
118103b8d341STomas Winkler 	return ERR_PTR(ret);
118203b8d341STomas Winkler }
118303b8d341STomas Winkler 
118403b8d341STomas Winkler /**
11854034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl.
11869ca9050bSTomas Winkler  *
118706ee536bSAlexander Usyskin  * @cl: host client
11889ca9050bSTomas Winkler  *
11894034b81bSTomas Winkler  * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise.
11909ca9050bSTomas Winkler  */
11914034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl)
11929ca9050bSTomas Winkler {
1193d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
119490e0b5f1STomas Winkler 		return -EINVAL;
119590e0b5f1STomas Winkler 
11964034b81bSTomas Winkler 	if (cl->tx_flow_ctrl_creds > 0)
11979ca9050bSTomas Winkler 		return 1;
11989ca9050bSTomas Winkler 
1199a808c80cSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12001df629efSAlexander Usyskin 		return 1;
12011df629efSAlexander Usyskin 
1202d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
12034034b81bSTomas Winkler 		if (cl->me_cl->tx_flow_ctrl_creds > 0)
1204d49ed64aSAlexander Usyskin 			return 1;
120512d00665SAlexander Usyskin 	}
1206d49ed64aSAlexander Usyskin 	return 0;
12079ca9050bSTomas Winkler }
12089ca9050bSTomas Winkler 
12099ca9050bSTomas Winkler /**
12104034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits
12114034b81bSTomas Winkler  *   for a client
12129ca9050bSTomas Winkler  *
12134034b81bSTomas Winkler  * @cl: host client
1214393b148fSMasanari Iida  *
1215a8605ea2SAlexander Usyskin  * Return:
12169ca9050bSTomas Winkler  *	0 on success
12179ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
12189ca9050bSTomas Winkler  */
12194034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
12209ca9050bSTomas Winkler {
1221d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
122290e0b5f1STomas Winkler 		return -EINVAL;
122390e0b5f1STomas Winkler 
12241df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12251df629efSAlexander Usyskin 		return 0;
12261df629efSAlexander Usyskin 
1227d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
12284034b81bSTomas Winkler 		if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0))
1229d49ed64aSAlexander Usyskin 			return -EINVAL;
12304034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds--;
12319ca9050bSTomas Winkler 	} else {
12324034b81bSTomas Winkler 		if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
1233d49ed64aSAlexander Usyskin 			return -EINVAL;
12344034b81bSTomas Winkler 		cl->tx_flow_ctrl_creds--;
12359ca9050bSTomas Winkler 	}
1236d49ed64aSAlexander Usyskin 	return 0;
12379ca9050bSTomas Winkler }
12389ca9050bSTomas Winkler 
12399ca9050bSTomas Winkler /**
124051678ccbSTomas Winkler  *  mei_cl_notify_fop2req - convert fop to proper request
124151678ccbSTomas Winkler  *
124251678ccbSTomas Winkler  * @fop: client notification start response command
124351678ccbSTomas Winkler  *
124451678ccbSTomas Winkler  * Return:  MEI_HBM_NOTIFICATION_START/STOP
124551678ccbSTomas Winkler  */
124651678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
124751678ccbSTomas Winkler {
124851678ccbSTomas Winkler 	if (fop == MEI_FOP_NOTIFY_START)
124951678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_START;
125051678ccbSTomas Winkler 	else
125151678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_STOP;
125251678ccbSTomas Winkler }
125351678ccbSTomas Winkler 
125451678ccbSTomas Winkler /**
125551678ccbSTomas Winkler  *  mei_cl_notify_req2fop - convert notification request top file operation type
125651678ccbSTomas Winkler  *
125751678ccbSTomas Winkler  * @req: hbm notification request type
125851678ccbSTomas Winkler  *
125951678ccbSTomas Winkler  * Return:  MEI_FOP_NOTIFY_START/STOP
126051678ccbSTomas Winkler  */
126151678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
126251678ccbSTomas Winkler {
126351678ccbSTomas Winkler 	if (req == MEI_HBM_NOTIFICATION_START)
126451678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_START;
126551678ccbSTomas Winkler 	else
126651678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_STOP;
126751678ccbSTomas Winkler }
126851678ccbSTomas Winkler 
126951678ccbSTomas Winkler /**
127051678ccbSTomas Winkler  * mei_cl_irq_notify - send notification request in irq_thread context
127151678ccbSTomas Winkler  *
127251678ccbSTomas Winkler  * @cl: client
127351678ccbSTomas Winkler  * @cb: callback block.
127451678ccbSTomas Winkler  * @cmpl_list: complete list.
127551678ccbSTomas Winkler  *
127651678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
127751678ccbSTomas Winkler  */
127851678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
1279962ff7bcSAlexander Usyskin 		      struct list_head *cmpl_list)
128051678ccbSTomas Winkler {
128151678ccbSTomas Winkler 	struct mei_device *dev = cl->dev;
128251678ccbSTomas Winkler 	u32 msg_slots;
128351678ccbSTomas Winkler 	int slots;
128451678ccbSTomas Winkler 	int ret;
128551678ccbSTomas Winkler 	bool request;
128651678ccbSTomas Winkler 
128751678ccbSTomas Winkler 	msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request));
128851678ccbSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
128951678ccbSTomas Winkler 
129051678ccbSTomas Winkler 	if (slots < msg_slots)
129151678ccbSTomas Winkler 		return -EMSGSIZE;
129251678ccbSTomas Winkler 
129351678ccbSTomas Winkler 	request = mei_cl_notify_fop2req(cb->fop_type);
129451678ccbSTomas Winkler 	ret = mei_hbm_cl_notify_req(dev, cl, request);
129551678ccbSTomas Winkler 	if (ret) {
129651678ccbSTomas Winkler 		cl->status = ret;
1297962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
129851678ccbSTomas Winkler 		return ret;
129951678ccbSTomas Winkler 	}
130051678ccbSTomas Winkler 
1301962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
130251678ccbSTomas Winkler 	return 0;
130351678ccbSTomas Winkler }
130451678ccbSTomas Winkler 
130551678ccbSTomas Winkler /**
130651678ccbSTomas Winkler  * mei_cl_notify_request - send notification stop/start request
130751678ccbSTomas Winkler  *
130851678ccbSTomas Winkler  * @cl: host client
13093030dc05STomas Winkler  * @fp: associate request with file
131051678ccbSTomas Winkler  * @request: 1 for start or 0 for stop
131151678ccbSTomas Winkler  *
131251678ccbSTomas Winkler  * Locking: called under "dev->device_lock" lock
131351678ccbSTomas Winkler  *
131451678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
131551678ccbSTomas Winkler  */
1316f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl,
13173030dc05STomas Winkler 			  const struct file *fp, u8 request)
131851678ccbSTomas Winkler {
131951678ccbSTomas Winkler 	struct mei_device *dev;
132051678ccbSTomas Winkler 	struct mei_cl_cb *cb;
132151678ccbSTomas Winkler 	enum mei_cb_file_ops fop_type;
132251678ccbSTomas Winkler 	int rets;
132351678ccbSTomas Winkler 
132451678ccbSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
132551678ccbSTomas Winkler 		return -ENODEV;
132651678ccbSTomas Winkler 
132751678ccbSTomas Winkler 	dev = cl->dev;
132851678ccbSTomas Winkler 
132951678ccbSTomas Winkler 	if (!dev->hbm_f_ev_supported) {
133051678ccbSTomas Winkler 		cl_dbg(dev, cl, "notifications not supported\n");
133151678ccbSTomas Winkler 		return -EOPNOTSUPP;
133251678ccbSTomas Winkler 	}
133351678ccbSTomas Winkler 
13347c47d2caSAlexander Usyskin 	if (!mei_cl_is_connected(cl))
13357c47d2caSAlexander Usyskin 		return -ENODEV;
13367c47d2caSAlexander Usyskin 
133751678ccbSTomas Winkler 	rets = pm_runtime_get(dev->dev);
133851678ccbSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
133951678ccbSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
134051678ccbSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
134151678ccbSTomas Winkler 		return rets;
134251678ccbSTomas Winkler 	}
134351678ccbSTomas Winkler 
134451678ccbSTomas Winkler 	fop_type = mei_cl_notify_req2fop(request);
13453030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp);
134651678ccbSTomas Winkler 	if (!cb) {
134751678ccbSTomas Winkler 		rets = -ENOMEM;
134851678ccbSTomas Winkler 		goto out;
134951678ccbSTomas Winkler 	}
135051678ccbSTomas Winkler 
135151678ccbSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
135251678ccbSTomas Winkler 		if (mei_hbm_cl_notify_req(dev, cl, request)) {
135351678ccbSTomas Winkler 			rets = -ENODEV;
135451678ccbSTomas Winkler 			goto out;
135551678ccbSTomas Winkler 		}
1356962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, &dev->ctrl_rd_list);
135751678ccbSTomas Winkler 	}
135851678ccbSTomas Winkler 
135951678ccbSTomas Winkler 	mutex_unlock(&dev->device_lock);
13607ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
13617ff4bdd4SAlexander Usyskin 			   cl->notify_en == request || !mei_cl_is_connected(cl),
136251678ccbSTomas Winkler 			   mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
136351678ccbSTomas Winkler 	mutex_lock(&dev->device_lock);
136451678ccbSTomas Winkler 
13654a8eaa96SAlexander Usyskin 	if (cl->notify_en != request && !cl->status)
136651678ccbSTomas Winkler 		cl->status = -EFAULT;
136751678ccbSTomas Winkler 
136851678ccbSTomas Winkler 	rets = cl->status;
136951678ccbSTomas Winkler 
137051678ccbSTomas Winkler out:
137151678ccbSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
137251678ccbSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
137351678ccbSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
137451678ccbSTomas Winkler 
137551678ccbSTomas Winkler 	mei_io_cb_free(cb);
137651678ccbSTomas Winkler 	return rets;
137751678ccbSTomas Winkler }
137851678ccbSTomas Winkler 
137951678ccbSTomas Winkler /**
1380237092bfSTomas Winkler  * mei_cl_notify - raise notification
1381237092bfSTomas Winkler  *
1382237092bfSTomas Winkler  * @cl: host client
1383237092bfSTomas Winkler  *
1384237092bfSTomas Winkler  * Locking: called under "dev->device_lock" lock
1385237092bfSTomas Winkler  */
1386237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl)
1387237092bfSTomas Winkler {
1388237092bfSTomas Winkler 	struct mei_device *dev;
1389237092bfSTomas Winkler 
1390237092bfSTomas Winkler 	if (!cl || !cl->dev)
1391237092bfSTomas Winkler 		return;
1392237092bfSTomas Winkler 
1393237092bfSTomas Winkler 	dev = cl->dev;
1394237092bfSTomas Winkler 
1395237092bfSTomas Winkler 	if (!cl->notify_en)
1396237092bfSTomas Winkler 		return;
1397237092bfSTomas Winkler 
1398237092bfSTomas Winkler 	cl_dbg(dev, cl, "notify event");
1399237092bfSTomas Winkler 	cl->notify_ev = true;
1400850f8940STomas Winkler 	if (!mei_cl_bus_notify_event(cl))
1401850f8940STomas Winkler 		wake_up_interruptible(&cl->ev_wait);
1402237092bfSTomas Winkler 
1403237092bfSTomas Winkler 	if (cl->ev_async)
1404237092bfSTomas Winkler 		kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
1405bb2ef9c3SAlexander Usyskin 
1406237092bfSTomas Winkler }
1407237092bfSTomas Winkler 
1408237092bfSTomas Winkler /**
1409b38a362fSTomas Winkler  * mei_cl_notify_get - get or wait for notification event
1410b38a362fSTomas Winkler  *
1411b38a362fSTomas Winkler  * @cl: host client
1412b38a362fSTomas Winkler  * @block: this request is blocking
1413b38a362fSTomas Winkler  * @notify_ev: true if notification event was received
1414b38a362fSTomas Winkler  *
1415b38a362fSTomas Winkler  * Locking: called under "dev->device_lock" lock
1416b38a362fSTomas Winkler  *
1417b38a362fSTomas Winkler  * Return: 0 on such and error otherwise.
1418b38a362fSTomas Winkler  */
1419b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
1420b38a362fSTomas Winkler {
1421b38a362fSTomas Winkler 	struct mei_device *dev;
1422b38a362fSTomas Winkler 	int rets;
1423b38a362fSTomas Winkler 
1424b38a362fSTomas Winkler 	*notify_ev = false;
1425b38a362fSTomas Winkler 
1426b38a362fSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1427b38a362fSTomas Winkler 		return -ENODEV;
1428b38a362fSTomas Winkler 
1429b38a362fSTomas Winkler 	dev = cl->dev;
1430b38a362fSTomas Winkler 
14316c0d6701SAlexander Usyskin 	if (!dev->hbm_f_ev_supported) {
14326c0d6701SAlexander Usyskin 		cl_dbg(dev, cl, "notifications not supported\n");
14336c0d6701SAlexander Usyskin 		return -EOPNOTSUPP;
14346c0d6701SAlexander Usyskin 	}
14356c0d6701SAlexander Usyskin 
1436b38a362fSTomas Winkler 	if (!mei_cl_is_connected(cl))
1437b38a362fSTomas Winkler 		return -ENODEV;
1438b38a362fSTomas Winkler 
1439b38a362fSTomas Winkler 	if (cl->notify_ev)
1440b38a362fSTomas Winkler 		goto out;
1441b38a362fSTomas Winkler 
1442b38a362fSTomas Winkler 	if (!block)
1443b38a362fSTomas Winkler 		return -EAGAIN;
1444b38a362fSTomas Winkler 
1445b38a362fSTomas Winkler 	mutex_unlock(&dev->device_lock);
1446b38a362fSTomas Winkler 	rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev);
1447b38a362fSTomas Winkler 	mutex_lock(&dev->device_lock);
1448b38a362fSTomas Winkler 
1449b38a362fSTomas Winkler 	if (rets < 0)
1450b38a362fSTomas Winkler 		return rets;
1451b38a362fSTomas Winkler 
1452b38a362fSTomas Winkler out:
1453b38a362fSTomas Winkler 	*notify_ev = cl->notify_ev;
1454b38a362fSTomas Winkler 	cl->notify_ev = false;
1455b38a362fSTomas Winkler 	return 0;
1456b38a362fSTomas Winkler }
1457b38a362fSTomas Winkler 
1458b38a362fSTomas Winkler /**
1459393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
14609ca9050bSTomas Winkler  *
146190e0b5f1STomas Winkler  * @cl: host client
1462ce23139cSAlexander Usyskin  * @length: number of bytes to read
1463bca67d68STomas Winkler  * @fp: pointer to file structure
14649ca9050bSTomas Winkler  *
1465a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
14669ca9050bSTomas Winkler  */
1467f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
14689ca9050bSTomas Winkler {
146990e0b5f1STomas Winkler 	struct mei_device *dev;
14709ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
14719ca9050bSTomas Winkler 	int rets;
14729ca9050bSTomas Winkler 
147390e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
147490e0b5f1STomas Winkler 		return -ENODEV;
147590e0b5f1STomas Winkler 
147690e0b5f1STomas Winkler 	dev = cl->dev;
147790e0b5f1STomas Winkler 
1478b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
14799ca9050bSTomas Winkler 		return -ENODEV;
14809ca9050bSTomas Winkler 
1481d49ed64aSAlexander Usyskin 	if (!mei_me_cl_is_active(cl->me_cl)) {
1482d49ed64aSAlexander Usyskin 		cl_err(dev, cl, "no such me client\n");
14837ca96aa2SAlexander Usyskin 		return  -ENOTTY;
14849ca9050bSTomas Winkler 	}
14851df629efSAlexander Usyskin 
14869d27e73cSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl)
1487e51dfa5aSAlexander Usyskin 		return 0;
1488e51dfa5aSAlexander Usyskin 
148946978adaSAlexander Usyskin 	/* HW currently supports only one pending read */
149046978adaSAlexander Usyskin 	if (cl->rx_flow_ctrl_creds)
149146978adaSAlexander Usyskin 		return -EBUSY;
149246978adaSAlexander Usyskin 
14933030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
14941df629efSAlexander Usyskin 	if (!cb)
14951df629efSAlexander Usyskin 		return -ENOMEM;
14961df629efSAlexander Usyskin 
14972bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
149804bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
14992bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
150004bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
15011df629efSAlexander Usyskin 		goto nortpm;
150204bb139aSTomas Winkler 	}
150304bb139aSTomas Winkler 
150446978adaSAlexander Usyskin 	rets = 0;
15056aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
150686113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
150786113500SAlexander Usyskin 		if (rets < 0)
150804bb139aSTomas Winkler 			goto out;
150904bb139aSTomas Winkler 
151046978adaSAlexander Usyskin 		list_move_tail(&cb->list, &cl->rd_pending);
15119ca9050bSTomas Winkler 	}
151246978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds++;
1513accb884bSChao Bi 
151404bb139aSTomas Winkler out:
151504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
15162bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
15172bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
15181df629efSAlexander Usyskin nortpm:
151904bb139aSTomas Winkler 	if (rets)
15209ca9050bSTomas Winkler 		mei_io_cb_free(cb);
152104bb139aSTomas Winkler 
15229ca9050bSTomas Winkler 	return rets;
15239ca9050bSTomas Winkler }
15249ca9050bSTomas Winkler 
1525074b4c01STomas Winkler /**
15269d098192STomas Winkler  * mei_cl_irq_write - write a message to device
152721767546STomas Winkler  *	from the interrupt thread context
152821767546STomas Winkler  *
152921767546STomas Winkler  * @cl: client
153021767546STomas Winkler  * @cb: callback block.
153121767546STomas Winkler  * @cmpl_list: complete list.
153221767546STomas Winkler  *
1533a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
153421767546STomas Winkler  */
15359d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
1536962ff7bcSAlexander Usyskin 		     struct list_head *cmpl_list)
153721767546STomas Winkler {
1538136698e5STomas Winkler 	struct mei_device *dev;
1539136698e5STomas Winkler 	struct mei_msg_data *buf;
154021767546STomas Winkler 	struct mei_msg_hdr mei_hdr;
1541136698e5STomas Winkler 	size_t len;
1542136698e5STomas Winkler 	u32 msg_slots;
15439d098192STomas Winkler 	int slots;
15442ebf8c94STomas Winkler 	int rets;
1545b8b73035SAlexander Usyskin 	bool first_chunk;
154621767546STomas Winkler 
1547136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1548136698e5STomas Winkler 		return -ENODEV;
1549136698e5STomas Winkler 
1550136698e5STomas Winkler 	dev = cl->dev;
1551136698e5STomas Winkler 
15525db7514dSTomas Winkler 	buf = &cb->buf;
1553136698e5STomas Winkler 
1554b8b73035SAlexander Usyskin 	first_chunk = cb->buf_idx == 0;
1555b8b73035SAlexander Usyskin 
15564034b81bSTomas Winkler 	rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
1557136698e5STomas Winkler 	if (rets < 0)
1558e09ee853SAlexander Usyskin 		goto err;
1559136698e5STomas Winkler 
1560136698e5STomas Winkler 	if (rets == 0) {
1561136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1562136698e5STomas Winkler 		return 0;
1563136698e5STomas Winkler 	}
1564136698e5STomas Winkler 
15659d098192STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1566136698e5STomas Winkler 	len = buf->size - cb->buf_idx;
1567136698e5STomas Winkler 	msg_slots = mei_data2slots(len);
1568136698e5STomas Winkler 
15691df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1570d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
157121767546STomas Winkler 	mei_hdr.reserved = 0;
1572479327fcSTomas Winkler 	mei_hdr.internal = cb->internal;
157321767546STomas Winkler 
15749d098192STomas Winkler 	if (slots >= msg_slots) {
157521767546STomas Winkler 		mei_hdr.length = len;
157621767546STomas Winkler 		mei_hdr.msg_complete = 1;
157721767546STomas Winkler 	/* Split the message only if we can write the whole host buffer */
15789d098192STomas Winkler 	} else if (slots == dev->hbuf_depth) {
15799d098192STomas Winkler 		msg_slots = slots;
15809d098192STomas Winkler 		len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
158121767546STomas Winkler 		mei_hdr.length = len;
158221767546STomas Winkler 		mei_hdr.msg_complete = 0;
158321767546STomas Winkler 	} else {
158421767546STomas Winkler 		/* wait for next time the host buffer is empty */
158521767546STomas Winkler 		return 0;
158621767546STomas Winkler 	}
158721767546STomas Winkler 
158835bf7692SAlexander Usyskin 	cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n",
15895db7514dSTomas Winkler 			cb->buf.size, cb->buf_idx);
159021767546STomas Winkler 
1591136698e5STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
1592e09ee853SAlexander Usyskin 	if (rets)
1593e09ee853SAlexander Usyskin 		goto err;
159421767546STomas Winkler 
159521767546STomas Winkler 	cl->status = 0;
15964dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
159721767546STomas Winkler 	cb->buf_idx += mei_hdr.length;
15988660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
15994dfaa9f7STomas Winkler 
1600b8b73035SAlexander Usyskin 	if (first_chunk) {
1601e09ee853SAlexander Usyskin 		if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
1602e09ee853SAlexander Usyskin 			rets = -EIO;
1603e09ee853SAlexander Usyskin 			goto err;
1604e09ee853SAlexander Usyskin 		}
160521767546STomas Winkler 	}
160621767546STomas Winkler 
1607b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
1608962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, &dev->write_waiting_list);
1609b8b73035SAlexander Usyskin 
161021767546STomas Winkler 	return 0;
1611e09ee853SAlexander Usyskin 
1612e09ee853SAlexander Usyskin err:
1613e09ee853SAlexander Usyskin 	cl->status = rets;
1614962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, cmpl_list);
1615e09ee853SAlexander Usyskin 	return rets;
161621767546STomas Winkler }
161721767546STomas Winkler 
161821767546STomas Winkler /**
16194234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1620a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
16214234a6deSTomas Winkler  *
16224234a6deSTomas Winkler  * @cl: host client
1623a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
16244234a6deSTomas Winkler  *
1625a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
16264234a6deSTomas Winkler  */
1627e0cb6b2fSAlexander Usyskin int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
16284234a6deSTomas Winkler {
16294234a6deSTomas Winkler 	struct mei_device *dev;
16304234a6deSTomas Winkler 	struct mei_msg_data *buf;
16314234a6deSTomas Winkler 	struct mei_msg_hdr mei_hdr;
163223253c31SAlexander Usyskin 	int size;
16334234a6deSTomas Winkler 	int rets;
1634e0cb6b2fSAlexander Usyskin 	bool blocking;
16354234a6deSTomas Winkler 
16364234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
16374234a6deSTomas Winkler 		return -ENODEV;
16384234a6deSTomas Winkler 
16394234a6deSTomas Winkler 	if (WARN_ON(!cb))
16404234a6deSTomas Winkler 		return -EINVAL;
16414234a6deSTomas Winkler 
16424234a6deSTomas Winkler 	dev = cl->dev;
16434234a6deSTomas Winkler 
16445db7514dSTomas Winkler 	buf = &cb->buf;
164523253c31SAlexander Usyskin 	size = buf->size;
1646e0cb6b2fSAlexander Usyskin 	blocking = cb->blocking;
16474234a6deSTomas Winkler 
164823253c31SAlexander Usyskin 	cl_dbg(dev, cl, "size=%d\n", size);
16494234a6deSTomas Winkler 
16502bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
165104bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
16522bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
165304bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
16546cbb097fSAlexander Usyskin 		goto free;
165504bb139aSTomas Winkler 	}
16564234a6deSTomas Winkler 
16576aae48ffSTomas Winkler 	cb->buf_idx = 0;
16586aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
16596aae48ffSTomas Winkler 
16601df629efSAlexander Usyskin 	mei_hdr.host_addr = mei_cl_host_addr(cl);
1661d49ed64aSAlexander Usyskin 	mei_hdr.me_addr = mei_cl_me_id(cl);
16626aae48ffSTomas Winkler 	mei_hdr.reserved = 0;
16636aae48ffSTomas Winkler 	mei_hdr.msg_complete = 0;
16646aae48ffSTomas Winkler 	mei_hdr.internal = cb->internal;
16654234a6deSTomas Winkler 
16664034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds(cl);
16674234a6deSTomas Winkler 	if (rets < 0)
16684234a6deSTomas Winkler 		goto err;
16694234a6deSTomas Winkler 
16706aae48ffSTomas Winkler 	if (rets == 0) {
16716aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
167223253c31SAlexander Usyskin 		rets = size;
16734234a6deSTomas Winkler 		goto out;
16744234a6deSTomas Winkler 	}
16756aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
16766aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
167723253c31SAlexander Usyskin 		rets = size;
16786aae48ffSTomas Winkler 		goto out;
16796aae48ffSTomas Winkler 	}
16804234a6deSTomas Winkler 
16814234a6deSTomas Winkler 	/* Check for a maximum length */
168223253c31SAlexander Usyskin 	if (size > mei_hbuf_max_len(dev)) {
16834234a6deSTomas Winkler 		mei_hdr.length = mei_hbuf_max_len(dev);
16844234a6deSTomas Winkler 		mei_hdr.msg_complete = 0;
16854234a6deSTomas Winkler 	} else {
168623253c31SAlexander Usyskin 		mei_hdr.length = size;
16874234a6deSTomas Winkler 		mei_hdr.msg_complete = 1;
16884234a6deSTomas Winkler 	}
16894234a6deSTomas Winkler 
16902ebf8c94STomas Winkler 	rets = mei_write_message(dev, &mei_hdr, buf->data);
16912ebf8c94STomas Winkler 	if (rets)
16924234a6deSTomas Winkler 		goto err;
16934234a6deSTomas Winkler 
16944034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
1695b8b73035SAlexander Usyskin 	if (rets)
1696b8b73035SAlexander Usyskin 		goto err;
1697b8b73035SAlexander Usyskin 
16984234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
16994234a6deSTomas Winkler 	cb->buf_idx = mei_hdr.length;
17008660172eSTomas Winkler 	cb->completed = mei_hdr.msg_complete == 1;
17014234a6deSTomas Winkler 
17024234a6deSTomas Winkler out:
1703b8b73035SAlexander Usyskin 	if (mei_hdr.msg_complete)
1704962ff7bcSAlexander Usyskin 		list_add_tail(&cb->list, &dev->write_waiting_list);
1705b8b73035SAlexander Usyskin 	else
1706962ff7bcSAlexander Usyskin 		list_add_tail(&cb->list, &dev->write_list);
17074234a6deSTomas Winkler 
170823253c31SAlexander Usyskin 	cb = NULL;
17094234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
17104234a6deSTomas Winkler 
17114234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
17127ca96aa2SAlexander Usyskin 		rets = wait_event_interruptible(cl->tx_wait,
17130faf6a3bSAlexander Usyskin 				cl->writing_state == MEI_WRITE_COMPLETE ||
17140faf6a3bSAlexander Usyskin 				(!mei_cl_is_connected(cl)));
17157ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
17167ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
17177ca96aa2SAlexander Usyskin 		if (rets) {
17184234a6deSTomas Winkler 			if (signal_pending(current))
17194234a6deSTomas Winkler 				rets = -EINTR;
17207ca96aa2SAlexander Usyskin 			goto err;
17214234a6deSTomas Winkler 		}
17220faf6a3bSAlexander Usyskin 		if (cl->writing_state != MEI_WRITE_COMPLETE) {
17230faf6a3bSAlexander Usyskin 			rets = -EFAULT;
17240faf6a3bSAlexander Usyskin 			goto err;
17250faf6a3bSAlexander Usyskin 		}
17264234a6deSTomas Winkler 	}
17277ca96aa2SAlexander Usyskin 
172823253c31SAlexander Usyskin 	rets = size;
17294234a6deSTomas Winkler err:
173004bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
17312bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
17322bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
17336cbb097fSAlexander Usyskin free:
17346cbb097fSAlexander Usyskin 	mei_io_cb_free(cb);
173504bb139aSTomas Winkler 
17364234a6deSTomas Winkler 	return rets;
17374234a6deSTomas Winkler }
17384234a6deSTomas Winkler 
17394234a6deSTomas Winkler 
1740db086fa9STomas Winkler /**
1741db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
1742db086fa9STomas Winkler  *
1743db086fa9STomas Winkler  * @cl: private data of the file object.
1744db086fa9STomas Winkler  * @cb: callback block.
1745db086fa9STomas Winkler  */
1746db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
1747db086fa9STomas Winkler {
1748a1809d38SAlexander Usyskin 	struct mei_device *dev = cl->dev;
1749a1809d38SAlexander Usyskin 
17503c666182STomas Winkler 	switch (cb->fop_type) {
17513c666182STomas Winkler 	case MEI_FOP_WRITE:
1752db086fa9STomas Winkler 		mei_io_cb_free(cb);
1753db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
1754a1809d38SAlexander Usyskin 		if (waitqueue_active(&cl->tx_wait)) {
1755db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
1756a1809d38SAlexander Usyskin 		} else {
1757a1809d38SAlexander Usyskin 			pm_runtime_mark_last_busy(dev->dev);
1758a1809d38SAlexander Usyskin 			pm_request_autosuspend(dev->dev);
1759a1809d38SAlexander Usyskin 		}
17603c666182STomas Winkler 		break;
1761db086fa9STomas Winkler 
17623c666182STomas Winkler 	case MEI_FOP_READ:
1763a9bed610STomas Winkler 		list_add_tail(&cb->list, &cl->rd_completed);
176446978adaSAlexander Usyskin 		if (!mei_cl_is_fixed_address(cl) &&
176546978adaSAlexander Usyskin 		    !WARN_ON(!cl->rx_flow_ctrl_creds))
176646978adaSAlexander Usyskin 			cl->rx_flow_ctrl_creds--;
1767a1f9ae2bSTomas Winkler 		if (!mei_cl_bus_rx_event(cl))
1768a1f9ae2bSTomas Winkler 			wake_up_interruptible(&cl->rx_wait);
17693c666182STomas Winkler 		break;
1770db086fa9STomas Winkler 
17713c666182STomas Winkler 	case MEI_FOP_CONNECT:
17723c666182STomas Winkler 	case MEI_FOP_DISCONNECT:
177351678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_STOP:
177451678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_START:
17753c666182STomas Winkler 		if (waitqueue_active(&cl->wait))
17763c666182STomas Winkler 			wake_up(&cl->wait);
17773c666182STomas Winkler 
17783c666182STomas Winkler 		break;
17796a8d648cSAlexander Usyskin 	case MEI_FOP_DISCONNECT_RSP:
17806a8d648cSAlexander Usyskin 		mei_io_cb_free(cb);
17816a8d648cSAlexander Usyskin 		mei_cl_set_disconnected(cl);
17826a8d648cSAlexander Usyskin 		break;
17833c666182STomas Winkler 	default:
17843c666182STomas Winkler 		BUG_ON(0);
1785db086fa9STomas Winkler 	}
1786db086fa9STomas Winkler }
1787db086fa9STomas Winkler 
17884234a6deSTomas Winkler 
17894234a6deSTomas Winkler /**
1790074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
1791074b4c01STomas Winkler  *
1792a8605ea2SAlexander Usyskin  * @dev: mei device
1793074b4c01STomas Winkler  */
1794074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
1795074b4c01STomas Winkler {
179631f88f57STomas Winkler 	struct mei_cl *cl;
1797074b4c01STomas Winkler 
17983c666182STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link)
17993c666182STomas Winkler 		mei_cl_set_disconnected(cl);
1800074b4c01STomas Winkler }
1801