xref: /openbmc/linux/drivers/misc/mei/client.c (revision f78fff46)
19fff0425STomas Winkler // SPDX-License-Identifier: GPL-2.0
29ca9050bSTomas Winkler /*
395953618SAlexander Usyskin  * Copyright (c) 2003-2022, Intel Corporation. All rights reserved.
49ca9050bSTomas Winkler  * Intel Management Engine Interface (Intel MEI) Linux driver
59ca9050bSTomas Winkler  */
69ca9050bSTomas Winkler 
7174cd4b1SIngo Molnar #include <linux/sched/signal.h>
89ca9050bSTomas Winkler #include <linux/wait.h>
99ca9050bSTomas Winkler #include <linux/delay.h>
101f180359STomas Winkler #include <linux/slab.h>
1104bb139aSTomas Winkler #include <linux/pm_runtime.h>
12369aea84SAlexander Usyskin #include <linux/dma-mapping.h>
139ca9050bSTomas Winkler 
149ca9050bSTomas Winkler #include <linux/mei.h>
159ca9050bSTomas Winkler 
169ca9050bSTomas Winkler #include "mei_dev.h"
179ca9050bSTomas Winkler #include "hbm.h"
1890e0b5f1STomas Winkler #include "client.h"
1990e0b5f1STomas Winkler 
2090e0b5f1STomas Winkler /**
2179563db9STomas Winkler  * mei_me_cl_init - initialize me client
2279563db9STomas Winkler  *
2379563db9STomas Winkler  * @me_cl: me client
2479563db9STomas Winkler  */
mei_me_cl_init(struct mei_me_client * me_cl)2579563db9STomas Winkler void mei_me_cl_init(struct mei_me_client *me_cl)
2679563db9STomas Winkler {
2779563db9STomas Winkler 	INIT_LIST_HEAD(&me_cl->list);
2879563db9STomas Winkler 	kref_init(&me_cl->refcnt);
2979563db9STomas Winkler }
3079563db9STomas Winkler 
3179563db9STomas Winkler /**
3279563db9STomas Winkler  * mei_me_cl_get - increases me client refcount
3379563db9STomas Winkler  *
3479563db9STomas Winkler  * @me_cl: me client
3579563db9STomas Winkler  *
3679563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
3779563db9STomas Winkler  *
3879563db9STomas Winkler  * Return: me client or NULL
3979563db9STomas Winkler  */
mei_me_cl_get(struct mei_me_client * me_cl)4079563db9STomas Winkler struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
4179563db9STomas Winkler {
42b7d88514STomas Winkler 	if (me_cl && kref_get_unless_zero(&me_cl->refcnt))
4379563db9STomas Winkler 		return me_cl;
44b7d88514STomas Winkler 
45b7d88514STomas Winkler 	return NULL;
4679563db9STomas Winkler }
4779563db9STomas Winkler 
4879563db9STomas Winkler /**
49b7d88514STomas Winkler  * mei_me_cl_release - free me client
5079563db9STomas Winkler  *
5179563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
5279563db9STomas Winkler  *
5379563db9STomas Winkler  * @ref: me_client refcount
5479563db9STomas Winkler  */
mei_me_cl_release(struct kref * ref)5579563db9STomas Winkler static void mei_me_cl_release(struct kref *ref)
5679563db9STomas Winkler {
5779563db9STomas Winkler 	struct mei_me_client *me_cl =
5879563db9STomas Winkler 		container_of(ref, struct mei_me_client, refcnt);
59b7d88514STomas Winkler 
6079563db9STomas Winkler 	kfree(me_cl);
6179563db9STomas Winkler }
62b7d88514STomas Winkler 
6379563db9STomas Winkler /**
6479563db9STomas Winkler  * mei_me_cl_put - decrease me client refcount and free client if necessary
6579563db9STomas Winkler  *
6679563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
6779563db9STomas Winkler  *
6879563db9STomas Winkler  * @me_cl: me client
6979563db9STomas Winkler  */
mei_me_cl_put(struct mei_me_client * me_cl)7079563db9STomas Winkler void mei_me_cl_put(struct mei_me_client *me_cl)
7179563db9STomas Winkler {
7279563db9STomas Winkler 	if (me_cl)
7379563db9STomas Winkler 		kref_put(&me_cl->refcnt, mei_me_cl_release);
7479563db9STomas Winkler }
7579563db9STomas Winkler 
7679563db9STomas Winkler /**
77d49ed64aSAlexander Usyskin  * __mei_me_cl_del  - delete me client from the list and decrease
78b7d88514STomas Winkler  *     reference counter
79b7d88514STomas Winkler  *
80b7d88514STomas Winkler  * @dev: mei device
81b7d88514STomas Winkler  * @me_cl: me client
82b7d88514STomas Winkler  *
83b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
84b7d88514STomas Winkler  */
__mei_me_cl_del(struct mei_device * dev,struct mei_me_client * me_cl)85b7d88514STomas Winkler static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
86b7d88514STomas Winkler {
87b7d88514STomas Winkler 	if (!me_cl)
88b7d88514STomas Winkler 		return;
89b7d88514STomas Winkler 
90d49ed64aSAlexander Usyskin 	list_del_init(&me_cl->list);
91b7d88514STomas Winkler 	mei_me_cl_put(me_cl);
92b7d88514STomas Winkler }
93b7d88514STomas Winkler 
94b7d88514STomas Winkler /**
95d49ed64aSAlexander Usyskin  * mei_me_cl_del - delete me client from the list and decrease
96d49ed64aSAlexander Usyskin  *     reference counter
97d49ed64aSAlexander Usyskin  *
98d49ed64aSAlexander Usyskin  * @dev: mei device
99d49ed64aSAlexander Usyskin  * @me_cl: me client
100d49ed64aSAlexander Usyskin  */
mei_me_cl_del(struct mei_device * dev,struct mei_me_client * me_cl)101d49ed64aSAlexander Usyskin void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
102d49ed64aSAlexander Usyskin {
103d49ed64aSAlexander Usyskin 	down_write(&dev->me_clients_rwsem);
104d49ed64aSAlexander Usyskin 	__mei_me_cl_del(dev, me_cl);
105d49ed64aSAlexander Usyskin 	up_write(&dev->me_clients_rwsem);
106d49ed64aSAlexander Usyskin }
107d49ed64aSAlexander Usyskin 
108d49ed64aSAlexander Usyskin /**
109b7d88514STomas Winkler  * mei_me_cl_add - add me client to the list
110b7d88514STomas Winkler  *
111b7d88514STomas Winkler  * @dev: mei device
112b7d88514STomas Winkler  * @me_cl: me client
113b7d88514STomas Winkler  */
mei_me_cl_add(struct mei_device * dev,struct mei_me_client * me_cl)114b7d88514STomas Winkler void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl)
115b7d88514STomas Winkler {
116b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
117b7d88514STomas Winkler 	list_add(&me_cl->list, &dev->me_clients);
118b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
119b7d88514STomas Winkler }
120b7d88514STomas Winkler 
121b7d88514STomas Winkler /**
122b7d88514STomas Winkler  * __mei_me_cl_by_uuid - locate me client by uuid
123b7d88514STomas Winkler  *	increases ref count
124b7d88514STomas Winkler  *
125b7d88514STomas Winkler  * @dev: mei device
126b7d88514STomas Winkler  * @uuid: me client uuid
127b7d88514STomas Winkler  *
128b7d88514STomas Winkler  * Return: me client or NULL if not found
129b7d88514STomas Winkler  *
130b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
131b7d88514STomas Winkler  */
__mei_me_cl_by_uuid(struct mei_device * dev,const uuid_le * uuid)132b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev,
133b7d88514STomas Winkler 					const uuid_le *uuid)
134b7d88514STomas Winkler {
135b7d88514STomas Winkler 	struct mei_me_client *me_cl;
136b7d88514STomas Winkler 	const uuid_le *pn;
137b7d88514STomas Winkler 
138b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
139b7d88514STomas Winkler 
140b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
141b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
142b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0)
143b7d88514STomas Winkler 			return mei_me_cl_get(me_cl);
144b7d88514STomas Winkler 	}
145b7d88514STomas Winkler 
146b7d88514STomas Winkler 	return NULL;
147b7d88514STomas Winkler }
148b7d88514STomas Winkler 
149b7d88514STomas Winkler /**
150a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid - locate me client by uuid
15179563db9STomas Winkler  *	increases ref count
15290e0b5f1STomas Winkler  *
15390e0b5f1STomas Winkler  * @dev: mei device
154a8605ea2SAlexander Usyskin  * @uuid: me client uuid
155a27a76d3SAlexander Usyskin  *
156a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
157b7d88514STomas Winkler  *
158b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
15990e0b5f1STomas Winkler  */
mei_me_cl_by_uuid(struct mei_device * dev,const uuid_le * uuid)160b7d88514STomas Winkler struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev,
161d320832fSTomas Winkler 					const uuid_le *uuid)
16290e0b5f1STomas Winkler {
1635ca2d388STomas Winkler 	struct mei_me_client *me_cl;
16490e0b5f1STomas Winkler 
165b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
166b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
167b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
16890e0b5f1STomas Winkler 
169b7d88514STomas Winkler 	return me_cl;
17090e0b5f1STomas Winkler }
17190e0b5f1STomas Winkler 
17290e0b5f1STomas Winkler /**
173a8605ea2SAlexander Usyskin  * mei_me_cl_by_id - locate me client by client id
17479563db9STomas Winkler  *	increases ref count
17590e0b5f1STomas Winkler  *
17690e0b5f1STomas Winkler  * @dev: the device structure
17790e0b5f1STomas Winkler  * @client_id: me client id
17890e0b5f1STomas Winkler  *
179a8605ea2SAlexander Usyskin  * Return: me client or NULL if not found
180b7d88514STomas Winkler  *
181b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
18290e0b5f1STomas Winkler  */
mei_me_cl_by_id(struct mei_device * dev,u8 client_id)183d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
18490e0b5f1STomas Winkler {
185a27a76d3SAlexander Usyskin 
186b7d88514STomas Winkler 	struct mei_me_client *__me_cl, *me_cl = NULL;
187a27a76d3SAlexander Usyskin 
188b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
189b7d88514STomas Winkler 	list_for_each_entry(__me_cl, &dev->me_clients, list) {
190b7d88514STomas Winkler 		if (__me_cl->client_id == client_id) {
191b7d88514STomas Winkler 			me_cl = mei_me_cl_get(__me_cl);
192b7d88514STomas Winkler 			break;
193b7d88514STomas Winkler 		}
194b7d88514STomas Winkler 	}
195b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
196b7d88514STomas Winkler 
197b7d88514STomas Winkler 	return me_cl;
198b7d88514STomas Winkler }
199b7d88514STomas Winkler 
200b7d88514STomas Winkler /**
201b7d88514STomas Winkler  * __mei_me_cl_by_uuid_id - locate me client by client id and uuid
202b7d88514STomas Winkler  *	increases ref count
203b7d88514STomas Winkler  *
204b7d88514STomas Winkler  * @dev: the device structure
205b7d88514STomas Winkler  * @uuid: me client uuid
206b7d88514STomas Winkler  * @client_id: me client id
207b7d88514STomas Winkler  *
208b7d88514STomas Winkler  * Return: me client or null if not found
209b7d88514STomas Winkler  *
210b7d88514STomas Winkler  * Locking: dev->me_clients_rwsem
211b7d88514STomas Winkler  */
__mei_me_cl_by_uuid_id(struct mei_device * dev,const uuid_le * uuid,u8 client_id)212b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev,
213b7d88514STomas Winkler 					   const uuid_le *uuid, u8 client_id)
214b7d88514STomas Winkler {
215b7d88514STomas Winkler 	struct mei_me_client *me_cl;
216b7d88514STomas Winkler 	const uuid_le *pn;
217b7d88514STomas Winkler 
218b7d88514STomas Winkler 	WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
219b7d88514STomas Winkler 
220b7d88514STomas Winkler 	list_for_each_entry(me_cl, &dev->me_clients, list) {
221b7d88514STomas Winkler 		pn = &me_cl->props.protocol_name;
222b7d88514STomas Winkler 		if (uuid_le_cmp(*uuid, *pn) == 0 &&
223b7d88514STomas Winkler 		    me_cl->client_id == client_id)
22479563db9STomas Winkler 			return mei_me_cl_get(me_cl);
225b7d88514STomas Winkler 	}
22679563db9STomas Winkler 
227d320832fSTomas Winkler 	return NULL;
22890e0b5f1STomas Winkler }
2299ca9050bSTomas Winkler 
230b7d88514STomas Winkler 
231a8605ea2SAlexander Usyskin /**
232a8605ea2SAlexander Usyskin  * mei_me_cl_by_uuid_id - locate me client by client id and uuid
23379563db9STomas Winkler  *	increases ref count
234a8605ea2SAlexander Usyskin  *
235a8605ea2SAlexander Usyskin  * @dev: the device structure
236a8605ea2SAlexander Usyskin  * @uuid: me client uuid
237a8605ea2SAlexander Usyskin  * @client_id: me client id
238a8605ea2SAlexander Usyskin  *
239b7d88514STomas Winkler  * Return: me client or null if not found
240a8605ea2SAlexander Usyskin  */
mei_me_cl_by_uuid_id(struct mei_device * dev,const uuid_le * uuid,u8 client_id)241d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
242d880f329STomas Winkler 					   const uuid_le *uuid, u8 client_id)
243d880f329STomas Winkler {
244d880f329STomas Winkler 	struct mei_me_client *me_cl;
245d880f329STomas Winkler 
246b7d88514STomas Winkler 	down_read(&dev->me_clients_rwsem);
247b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id);
248b7d88514STomas Winkler 	up_read(&dev->me_clients_rwsem);
24979563db9STomas Winkler 
250b7d88514STomas Winkler 	return me_cl;
251d880f329STomas Winkler }
252d880f329STomas Winkler 
25325ca6472STomas Winkler /**
25479563db9STomas Winkler  * mei_me_cl_rm_by_uuid - remove all me clients matching uuid
25525ca6472STomas Winkler  *
25625ca6472STomas Winkler  * @dev: the device structure
25725ca6472STomas Winkler  * @uuid: me client uuid
25879563db9STomas Winkler  *
25979563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
26025ca6472STomas Winkler  */
mei_me_cl_rm_by_uuid(struct mei_device * dev,const uuid_le * uuid)26179563db9STomas Winkler void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
26225ca6472STomas Winkler {
263b7d88514STomas Winkler 	struct mei_me_client *me_cl;
26425ca6472STomas Winkler 
26579563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl\n", uuid);
266b7d88514STomas Winkler 
267b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
268b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid(dev, uuid);
269b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
270fc9c03ceSAlexander Usyskin 	mei_me_cl_put(me_cl);
271b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
27279563db9STomas Winkler }
27379563db9STomas Winkler 
27479563db9STomas Winkler /**
27579563db9STomas Winkler  * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id
27679563db9STomas Winkler  *
27779563db9STomas Winkler  * @dev: the device structure
27879563db9STomas Winkler  * @uuid: me client uuid
27979563db9STomas Winkler  * @id: me client id
28079563db9STomas Winkler  *
28179563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
28279563db9STomas Winkler  */
mei_me_cl_rm_by_uuid_id(struct mei_device * dev,const uuid_le * uuid,u8 id)28379563db9STomas Winkler void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
28479563db9STomas Winkler {
285b7d88514STomas Winkler 	struct mei_me_client *me_cl;
28679563db9STomas Winkler 
28779563db9STomas Winkler 	dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
288b7d88514STomas Winkler 
289b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
290b7d88514STomas Winkler 	me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id);
291b7d88514STomas Winkler 	__mei_me_cl_del(dev, me_cl);
292fc9c03ceSAlexander Usyskin 	mei_me_cl_put(me_cl);
293b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
29425ca6472STomas Winkler }
29579563db9STomas Winkler 
29679563db9STomas Winkler /**
29779563db9STomas Winkler  * mei_me_cl_rm_all - remove all me clients
29879563db9STomas Winkler  *
29979563db9STomas Winkler  * @dev: the device structure
30079563db9STomas Winkler  *
30179563db9STomas Winkler  * Locking: called under "dev->device_lock" lock
30279563db9STomas Winkler  */
mei_me_cl_rm_all(struct mei_device * dev)30379563db9STomas Winkler void mei_me_cl_rm_all(struct mei_device *dev)
30479563db9STomas Winkler {
30579563db9STomas Winkler 	struct mei_me_client *me_cl, *next;
30679563db9STomas Winkler 
307b7d88514STomas Winkler 	down_write(&dev->me_clients_rwsem);
30879563db9STomas Winkler 	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
309b7d88514STomas Winkler 		__mei_me_cl_del(dev, me_cl);
310b7d88514STomas Winkler 	up_write(&dev->me_clients_rwsem);
31125ca6472STomas Winkler }
31225ca6472STomas Winkler 
3139ca9050bSTomas Winkler /**
3149ca9050bSTomas Winkler  * mei_io_cb_free - free mei_cb_private related memory
3159ca9050bSTomas Winkler  *
3169ca9050bSTomas Winkler  * @cb: mei callback struct
3179ca9050bSTomas Winkler  */
mei_io_cb_free(struct mei_cl_cb * cb)3189ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb)
3199ca9050bSTomas Winkler {
3209ca9050bSTomas Winkler 	if (cb == NULL)
3219ca9050bSTomas Winkler 		return;
3229ca9050bSTomas Winkler 
323928fa666STomas Winkler 	list_del(&cb->list);
3245db7514dSTomas Winkler 	kfree(cb->buf.data);
3254ed1cc99STomas Winkler 	kfree(cb->ext_hdr);
3269ca9050bSTomas Winkler 	kfree(cb);
3279ca9050bSTomas Winkler }
3289ca9050bSTomas Winkler 
3299ca9050bSTomas Winkler /**
33009f8c33aSTamar Mashiah  * mei_tx_cb_enqueue - queue tx callback
331af336cabSAlexander Usyskin  *
332af336cabSAlexander Usyskin  * Locking: called under "dev->device_lock" lock
333af336cabSAlexander Usyskin  *
334af336cabSAlexander Usyskin  * @cb: mei callback struct
335af336cabSAlexander Usyskin  * @head: an instance of list to queue on
336af336cabSAlexander Usyskin  */
mei_tx_cb_enqueue(struct mei_cl_cb * cb,struct list_head * head)337af336cabSAlexander Usyskin static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb,
338af336cabSAlexander Usyskin 				     struct list_head *head)
339af336cabSAlexander Usyskin {
340af336cabSAlexander Usyskin 	list_add_tail(&cb->list, head);
341af336cabSAlexander Usyskin 	cb->cl->tx_cb_queued++;
342af336cabSAlexander Usyskin }
343af336cabSAlexander Usyskin 
344af336cabSAlexander Usyskin /**
345af336cabSAlexander Usyskin  * mei_tx_cb_dequeue - dequeue tx callback
346af336cabSAlexander Usyskin  *
347af336cabSAlexander Usyskin  * Locking: called under "dev->device_lock" lock
348af336cabSAlexander Usyskin  *
349af336cabSAlexander Usyskin  * @cb: mei callback struct to dequeue and free
350af336cabSAlexander Usyskin  */
mei_tx_cb_dequeue(struct mei_cl_cb * cb)351af336cabSAlexander Usyskin static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
352af336cabSAlexander Usyskin {
353af336cabSAlexander Usyskin 	if (!WARN_ON(cb->cl->tx_cb_queued == 0))
354af336cabSAlexander Usyskin 		cb->cl->tx_cb_queued--;
355af336cabSAlexander Usyskin 
356af336cabSAlexander Usyskin 	mei_io_cb_free(cb);
357af336cabSAlexander Usyskin }
358af336cabSAlexander Usyskin 
359af336cabSAlexander Usyskin /**
360f35fe5f4SAlexander Usyskin  * mei_cl_set_read_by_fp - set pending_read flag to vtag struct for given fp
361f35fe5f4SAlexander Usyskin  *
362f35fe5f4SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
363f35fe5f4SAlexander Usyskin  *
364f35fe5f4SAlexander Usyskin  * @cl: mei client
365f35fe5f4SAlexander Usyskin  * @fp: pointer to file structure
366f35fe5f4SAlexander Usyskin  */
mei_cl_set_read_by_fp(const struct mei_cl * cl,const struct file * fp)367f35fe5f4SAlexander Usyskin static void mei_cl_set_read_by_fp(const struct mei_cl *cl,
368f35fe5f4SAlexander Usyskin 				  const struct file *fp)
369f35fe5f4SAlexander Usyskin {
370f35fe5f4SAlexander Usyskin 	struct mei_cl_vtag *cl_vtag;
371f35fe5f4SAlexander Usyskin 
372f35fe5f4SAlexander Usyskin 	list_for_each_entry(cl_vtag, &cl->vtag_map, list) {
373f35fe5f4SAlexander Usyskin 		if (cl_vtag->fp == fp) {
374f35fe5f4SAlexander Usyskin 			cl_vtag->pending_read = true;
375f35fe5f4SAlexander Usyskin 			return;
376f35fe5f4SAlexander Usyskin 		}
377f35fe5f4SAlexander Usyskin 	}
378f35fe5f4SAlexander Usyskin }
379f35fe5f4SAlexander Usyskin 
380f35fe5f4SAlexander Usyskin /**
3819ca9050bSTomas Winkler  * mei_io_cb_init - allocate and initialize io callback
3829ca9050bSTomas Winkler  *
383a8605ea2SAlexander Usyskin  * @cl: mei client
384bca67d68STomas Winkler  * @type: operation type
385393b148fSMasanari Iida  * @fp: pointer to file structure
3869ca9050bSTomas Winkler  *
387a8605ea2SAlexander Usyskin  * Return: mei_cl_cb pointer or NULL;
3889ca9050bSTomas Winkler  */
mei_io_cb_init(struct mei_cl * cl,enum mei_cb_file_ops type,const struct file * fp)3893030dc05STomas Winkler static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
3903030dc05STomas Winkler 					enum mei_cb_file_ops type,
391f23e2cc4STomas Winkler 					const struct file *fp)
3929ca9050bSTomas Winkler {
3939ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
3949ca9050bSTomas Winkler 
3954b40b225STomas Winkler 	cb = kzalloc(sizeof(*cb), GFP_KERNEL);
3969ca9050bSTomas Winkler 	if (!cb)
3979ca9050bSTomas Winkler 		return NULL;
3989ca9050bSTomas Winkler 
399928fa666STomas Winkler 	INIT_LIST_HEAD(&cb->list);
40062e8e6adSTomas Winkler 	cb->fp = fp;
4019ca9050bSTomas Winkler 	cb->cl = cl;
4029ca9050bSTomas Winkler 	cb->buf_idx = 0;
403bca67d68STomas Winkler 	cb->fop_type = type;
4040cd7c01aSTomas Winkler 	cb->vtag = 0;
4054ed1cc99STomas Winkler 	cb->ext_hdr = NULL;
4060cd7c01aSTomas Winkler 
4079ca9050bSTomas Winkler 	return cb;
4089ca9050bSTomas Winkler }
4099ca9050bSTomas Winkler 
4109ca9050bSTomas Winkler /**
411af336cabSAlexander Usyskin  * mei_io_list_flush_cl - removes cbs belonging to the cl.
412928fa666STomas Winkler  *
413962ff7bcSAlexander Usyskin  * @head:  an instance of our list structure
414af336cabSAlexander Usyskin  * @cl:    host client
415928fa666STomas Winkler  */
mei_io_list_flush_cl(struct list_head * head,const struct mei_cl * cl)416af336cabSAlexander Usyskin static void mei_io_list_flush_cl(struct list_head *head,
417af336cabSAlexander Usyskin 				 const struct mei_cl *cl)
418928fa666STomas Winkler {
419928fa666STomas Winkler 	struct mei_cl_cb *cb, *next;
420928fa666STomas Winkler 
421962ff7bcSAlexander Usyskin 	list_for_each_entry_safe(cb, next, head, list) {
422cee4c4d6SAlexander Usyskin 		if (cl == cb->cl) {
423928fa666STomas Winkler 			list_del_init(&cb->list);
424cee4c4d6SAlexander Usyskin 			if (cb->fop_type == MEI_FOP_READ)
425cee4c4d6SAlexander Usyskin 				mei_io_cb_free(cb);
426cee4c4d6SAlexander Usyskin 		}
427928fa666STomas Winkler 	}
428928fa666STomas Winkler }
429928fa666STomas Winkler 
430928fa666STomas Winkler /**
431af336cabSAlexander Usyskin  * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them
432928fa666STomas Winkler  *
433962ff7bcSAlexander Usyskin  * @head: An instance of our list structure
434928fa666STomas Winkler  * @cl: host client
43515ffa991SAlexander Usyskin  * @fp: file pointer (matching cb file object), may be NULL
436928fa666STomas Winkler  */
mei_io_tx_list_free_cl(struct list_head * head,const struct mei_cl * cl,const struct file * fp)437af336cabSAlexander Usyskin static void mei_io_tx_list_free_cl(struct list_head *head,
43815ffa991SAlexander Usyskin 				   const struct mei_cl *cl,
43915ffa991SAlexander Usyskin 				   const struct file *fp)
440928fa666STomas Winkler {
441af336cabSAlexander Usyskin 	struct mei_cl_cb *cb, *next;
442928fa666STomas Winkler 
443af336cabSAlexander Usyskin 	list_for_each_entry_safe(cb, next, head, list) {
44415ffa991SAlexander Usyskin 		if (cl == cb->cl && (!fp || fp == cb->fp))
445af336cabSAlexander Usyskin 			mei_tx_cb_dequeue(cb);
446af336cabSAlexander Usyskin 	}
447f046192dSTomas Winkler }
448f046192dSTomas Winkler 
449f046192dSTomas Winkler /**
450f046192dSTomas Winkler  * mei_io_list_free_fp - free cb from a list that matches file pointer
451f046192dSTomas Winkler  *
452f046192dSTomas Winkler  * @head: io list
453f046192dSTomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
454f046192dSTomas Winkler  */
mei_io_list_free_fp(struct list_head * head,const struct file * fp)455394a77d0SAlexander Usyskin static void mei_io_list_free_fp(struct list_head *head, const struct file *fp)
456f046192dSTomas Winkler {
457f046192dSTomas Winkler 	struct mei_cl_cb *cb, *next;
458f046192dSTomas Winkler 
459f046192dSTomas Winkler 	list_for_each_entry_safe(cb, next, head, list)
460f046192dSTomas Winkler 		if (!fp || fp == cb->fp)
461f046192dSTomas Winkler 			mei_io_cb_free(cb);
462928fa666STomas Winkler }
463928fa666STomas Winkler 
464928fa666STomas Winkler /**
465f35fe5f4SAlexander Usyskin  * mei_cl_free_pending - free pending cb
466f35fe5f4SAlexander Usyskin  *
467f35fe5f4SAlexander Usyskin  * @cl: host client
468f35fe5f4SAlexander Usyskin  */
mei_cl_free_pending(struct mei_cl * cl)469f35fe5f4SAlexander Usyskin static void mei_cl_free_pending(struct mei_cl *cl)
470f35fe5f4SAlexander Usyskin {
471f35fe5f4SAlexander Usyskin 	struct mei_cl_cb *cb;
472f35fe5f4SAlexander Usyskin 
473f35fe5f4SAlexander Usyskin 	cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
474f35fe5f4SAlexander Usyskin 	mei_io_cb_free(cb);
475f35fe5f4SAlexander Usyskin }
476f35fe5f4SAlexander Usyskin 
477f35fe5f4SAlexander Usyskin /**
478bca67d68STomas Winkler  * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
479bca67d68STomas Winkler  *
480bca67d68STomas Winkler  * @cl: host client
481bca67d68STomas Winkler  * @length: size of the buffer
482967b274eSAlexander Usyskin  * @fop_type: operation type
483bca67d68STomas Winkler  * @fp: associated file pointer (might be NULL)
484bca67d68STomas Winkler  *
485bca67d68STomas Winkler  * Return: cb on success and NULL on failure
486bca67d68STomas Winkler  */
mei_cl_alloc_cb(struct mei_cl * cl,size_t length,enum mei_cb_file_ops fop_type,const struct file * fp)487bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
4883030dc05STomas Winkler 				  enum mei_cb_file_ops fop_type,
489f23e2cc4STomas Winkler 				  const struct file *fp)
490bca67d68STomas Winkler {
491bca67d68STomas Winkler 	struct mei_cl_cb *cb;
492bca67d68STomas Winkler 
4933030dc05STomas Winkler 	cb = mei_io_cb_init(cl, fop_type, fp);
494bca67d68STomas Winkler 	if (!cb)
495bca67d68STomas Winkler 		return NULL;
496bca67d68STomas Winkler 
497aab3b1a3SAlexander Usyskin 	if (length == 0)
498aab3b1a3SAlexander Usyskin 		return cb;
499aab3b1a3SAlexander Usyskin 
5006316321fSTomas Winkler 	cb->buf.data = kmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL);
501aab3b1a3SAlexander Usyskin 	if (!cb->buf.data) {
502bca67d68STomas Winkler 		mei_io_cb_free(cb);
503bca67d68STomas Winkler 		return NULL;
504bca67d68STomas Winkler 	}
505aab3b1a3SAlexander Usyskin 	cb->buf.size = length;
506bca67d68STomas Winkler 
507bca67d68STomas Winkler 	return cb;
508bca67d68STomas Winkler }
509bca67d68STomas Winkler 
510bca67d68STomas Winkler /**
5113030dc05STomas Winkler  * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating
5123030dc05STomas Winkler  *     and enqueuing of the control commands cb
5133030dc05STomas Winkler  *
5143030dc05STomas Winkler  * @cl: host client
5153030dc05STomas Winkler  * @length: size of the buffer
516967b274eSAlexander Usyskin  * @fop_type: operation type
5173030dc05STomas Winkler  * @fp: associated file pointer (might be NULL)
5183030dc05STomas Winkler  *
5193030dc05STomas Winkler  * Return: cb on success and NULL on failure
5203030dc05STomas Winkler  * Locking: called under "dev->device_lock" lock
5213030dc05STomas Winkler  */
mei_cl_enqueue_ctrl_wr_cb(struct mei_cl * cl,size_t length,enum mei_cb_file_ops fop_type,const struct file * fp)5223030dc05STomas Winkler struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
5233030dc05STomas Winkler 					    enum mei_cb_file_ops fop_type,
5243030dc05STomas Winkler 					    const struct file *fp)
5253030dc05STomas Winkler {
5263030dc05STomas Winkler 	struct mei_cl_cb *cb;
5273030dc05STomas Winkler 
5283030dc05STomas Winkler 	/* for RX always allocate at least client's mtu */
5293030dc05STomas Winkler 	if (length)
5303030dc05STomas Winkler 		length = max_t(size_t, length, mei_cl_mtu(cl));
5313030dc05STomas Winkler 
5323030dc05STomas Winkler 	cb = mei_cl_alloc_cb(cl, length, fop_type, fp);
5333030dc05STomas Winkler 	if (!cb)
5343030dc05STomas Winkler 		return NULL;
5353030dc05STomas Winkler 
536962ff7bcSAlexander Usyskin 	list_add_tail(&cb->list, &cl->dev->ctrl_wr_list);
5373030dc05STomas Winkler 	return cb;
5383030dc05STomas Winkler }
5393030dc05STomas Winkler 
5403030dc05STomas Winkler /**
541a9bed610STomas Winkler  * mei_cl_read_cb - find this cl's callback in the read list
542a9bed610STomas Winkler  *     for a specific file
543a9bed610STomas Winkler  *
544a9bed610STomas Winkler  * @cl: host client
545a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
546a9bed610STomas Winkler  *
547a9bed610STomas Winkler  * Return: cb on success, NULL if cb is not found
548a9bed610STomas Winkler  */
mei_cl_read_cb(struct mei_cl * cl,const struct file * fp)549d1376f3dSAlexander Usyskin struct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp)
550a9bed610STomas Winkler {
551a9bed610STomas Winkler 	struct mei_cl_cb *cb;
552d1376f3dSAlexander Usyskin 	struct mei_cl_cb *ret_cb = NULL;
553a9bed610STomas Winkler 
554d1376f3dSAlexander Usyskin 	spin_lock(&cl->rd_completed_lock);
555a9bed610STomas Winkler 	list_for_each_entry(cb, &cl->rd_completed, list)
556d1376f3dSAlexander Usyskin 		if (!fp || fp == cb->fp) {
557d1376f3dSAlexander Usyskin 			ret_cb = cb;
558d1376f3dSAlexander Usyskin 			break;
559d1376f3dSAlexander Usyskin 		}
560d1376f3dSAlexander Usyskin 	spin_unlock(&cl->rd_completed_lock);
561d1376f3dSAlexander Usyskin 	return ret_cb;
562a9bed610STomas Winkler }
563a9bed610STomas Winkler 
564a9bed610STomas Winkler /**
5659ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
5669ca9050bSTomas Winkler  *
5679ca9050bSTomas Winkler  * @cl: host client
568a9bed610STomas Winkler  * @fp: file pointer (matching cb file object), may be NULL
569ce23139cSAlexander Usyskin  *
570ce23139cSAlexander Usyskin  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
5719ca9050bSTomas Winkler  */
mei_cl_flush_queues(struct mei_cl * cl,const struct file * fp)572a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
5739ca9050bSTomas Winkler {
574c0abffbdSAlexander Usyskin 	struct mei_device *dev;
575c0abffbdSAlexander Usyskin 
57690e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
5779ca9050bSTomas Winkler 		return -EINVAL;
5789ca9050bSTomas Winkler 
579c0abffbdSAlexander Usyskin 	dev = cl->dev;
580c0abffbdSAlexander Usyskin 
581c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "remove list entry belonging to cl\n");
58215ffa991SAlexander Usyskin 	mei_io_tx_list_free_cl(&cl->dev->write_list, cl, fp);
58315ffa991SAlexander Usyskin 	mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl, fp);
58415ffa991SAlexander Usyskin 	/* free pending and control cb only in final flush */
58515ffa991SAlexander Usyskin 	if (!fp) {
586f046192dSTomas Winkler 		mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
587f046192dSTomas Winkler 		mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
588f35fe5f4SAlexander Usyskin 		mei_cl_free_pending(cl);
58915ffa991SAlexander Usyskin 	}
590d1376f3dSAlexander Usyskin 	spin_lock(&cl->rd_completed_lock);
591f046192dSTomas Winkler 	mei_io_list_free_fp(&cl->rd_completed, fp);
592d1376f3dSAlexander Usyskin 	spin_unlock(&cl->rd_completed_lock);
593a9bed610STomas Winkler 
5949ca9050bSTomas Winkler 	return 0;
5959ca9050bSTomas Winkler }
5969ca9050bSTomas Winkler 
5979ca9050bSTomas Winkler /**
59883ce0741SAlexander Usyskin  * mei_cl_init - initializes cl.
5999ca9050bSTomas Winkler  *
6009ca9050bSTomas Winkler  * @cl: host client to be initialized
6019ca9050bSTomas Winkler  * @dev: mei device
6029ca9050bSTomas Winkler  */
mei_cl_init(struct mei_cl * cl,struct mei_device * dev)603394a77d0SAlexander Usyskin static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
6049ca9050bSTomas Winkler {
6054b40b225STomas Winkler 	memset(cl, 0, sizeof(*cl));
6069ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
6079ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
6089ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
609b38a362fSTomas Winkler 	init_waitqueue_head(&cl->ev_wait);
610f35fe5f4SAlexander Usyskin 	INIT_LIST_HEAD(&cl->vtag_map);
611d1376f3dSAlexander Usyskin 	spin_lock_init(&cl->rd_completed_lock);
612a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_completed);
613a9bed610STomas Winkler 	INIT_LIST_HEAD(&cl->rd_pending);
6149ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
6159ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
616bd47b526SAlexander Usyskin 	cl->state = MEI_FILE_UNINITIALIZED;
6179ca9050bSTomas Winkler 	cl->dev = dev;
6189ca9050bSTomas Winkler }
6199ca9050bSTomas Winkler 
6209ca9050bSTomas Winkler /**
6219ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
6229ca9050bSTomas Winkler  *
6239ca9050bSTomas Winkler  * @dev: mei device
624a8605ea2SAlexander Usyskin  * Return:  The allocated file or NULL on failure
6259ca9050bSTomas Winkler  */
mei_cl_allocate(struct mei_device * dev)6269ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
6279ca9050bSTomas Winkler {
6289ca9050bSTomas Winkler 	struct mei_cl *cl;
6299ca9050bSTomas Winkler 
6304b40b225STomas Winkler 	cl = kmalloc(sizeof(*cl), GFP_KERNEL);
6319ca9050bSTomas Winkler 	if (!cl)
6329ca9050bSTomas Winkler 		return NULL;
6339ca9050bSTomas Winkler 
6349ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
6359ca9050bSTomas Winkler 
6369ca9050bSTomas Winkler 	return cl;
6379ca9050bSTomas Winkler }
6389ca9050bSTomas Winkler 
63990e0b5f1STomas Winkler /**
6403908be6fSAlexander Usyskin  * mei_cl_link - allocate host id in the host map
6419ca9050bSTomas Winkler  *
6423908be6fSAlexander Usyskin  * @cl: host client
643393b148fSMasanari Iida  *
644a8605ea2SAlexander Usyskin  * Return: 0 on success
6459ca9050bSTomas Winkler  *	-EINVAL on incorrect values
64603b8d341STomas Winkler  *	-EMFILE if open count exceeded.
6479ca9050bSTomas Winkler  */
mei_cl_link(struct mei_cl * cl)6487851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl)
6499ca9050bSTomas Winkler {
65090e0b5f1STomas Winkler 	struct mei_device *dev;
6517851e008SAlexander Usyskin 	int id;
6529ca9050bSTomas Winkler 
653781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
6549ca9050bSTomas Winkler 		return -EINVAL;
6559ca9050bSTomas Winkler 
65690e0b5f1STomas Winkler 	dev = cl->dev;
65790e0b5f1STomas Winkler 
6587851e008SAlexander Usyskin 	id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
659781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
6602bf94cabSTomas Winkler 		dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
661e036cc57STomas Winkler 		return -EMFILE;
662e036cc57STomas Winkler 	}
663e036cc57STomas Winkler 
664394a77d0SAlexander Usyskin 	if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
6652bf94cabSTomas Winkler 		dev_err(dev->dev, "open_handle_count exceeded %d",
666e036cc57STomas Winkler 			MEI_MAX_OPEN_HANDLE_COUNT);
667e036cc57STomas Winkler 		return -EMFILE;
6689ca9050bSTomas Winkler 	}
669781d0d89STomas Winkler 
670781d0d89STomas Winkler 	dev->open_handle_count++;
671781d0d89STomas Winkler 
672781d0d89STomas Winkler 	cl->host_client_id = id;
673781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
674781d0d89STomas Winkler 
675781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
676781d0d89STomas Winkler 
677781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
678781d0d89STomas Winkler 
679c0abffbdSAlexander Usyskin 	cl_dbg(dev, cl, "link cl\n");
680781d0d89STomas Winkler 	return 0;
681781d0d89STomas Winkler }
682781d0d89STomas Winkler 
6839ca9050bSTomas Winkler /**
684d49ed64aSAlexander Usyskin  * mei_cl_unlink - remove host client from the list
6859ca9050bSTomas Winkler  *
686393b148fSMasanari Iida  * @cl: host client
687ce23139cSAlexander Usyskin  *
688ce23139cSAlexander Usyskin  * Return: always 0
6899ca9050bSTomas Winkler  */
mei_cl_unlink(struct mei_cl * cl)69090e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
6919ca9050bSTomas Winkler {
69290e0b5f1STomas Winkler 	struct mei_device *dev;
69390e0b5f1STomas Winkler 
694781d0d89STomas Winkler 	/* don't shout on error exit path */
695781d0d89STomas Winkler 	if (!cl)
696781d0d89STomas Winkler 		return 0;
697781d0d89STomas Winkler 
698394a77d0SAlexander Usyskin 	if (WARN_ON(!cl->dev))
6998e9a4a9aSTomas Winkler 		return 0;
70090e0b5f1STomas Winkler 
70190e0b5f1STomas Winkler 	dev = cl->dev;
70290e0b5f1STomas Winkler 
703a14c44d8STomas Winkler 	cl_dbg(dev, cl, "unlink client");
704a14c44d8STomas Winkler 
7052cca3465SAlexander Usyskin 	if (cl->state == MEI_FILE_UNINITIALIZED)
7062cca3465SAlexander Usyskin 		return 0;
7072cca3465SAlexander Usyskin 
70822f96a0eSTomas Winkler 	if (dev->open_handle_count > 0)
70922f96a0eSTomas Winkler 		dev->open_handle_count--;
71022f96a0eSTomas Winkler 
71122f96a0eSTomas Winkler 	/* never clear the 0 bit */
71222f96a0eSTomas Winkler 	if (cl->host_client_id)
71322f96a0eSTomas Winkler 		clear_bit(cl->host_client_id, dev->host_clients_map);
71422f96a0eSTomas Winkler 
71522f96a0eSTomas Winkler 	list_del_init(&cl->link);
71622f96a0eSTomas Winkler 
717bd47b526SAlexander Usyskin 	cl->state = MEI_FILE_UNINITIALIZED;
7187c7a6077SAlexander Usyskin 	cl->writing_state = MEI_IDLE;
7197c7a6077SAlexander Usyskin 
7207c7a6077SAlexander Usyskin 	WARN_ON(!list_empty(&cl->rd_completed) ||
7217c7a6077SAlexander Usyskin 		!list_empty(&cl->rd_pending) ||
7227c7a6077SAlexander Usyskin 		!list_empty(&cl->link));
72322f96a0eSTomas Winkler 
72490e0b5f1STomas Winkler 	return 0;
7259ca9050bSTomas Winkler }
7269ca9050bSTomas Winkler 
mei_host_client_init(struct mei_device * dev)727025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev)
7289ca9050bSTomas Winkler {
72943b8a7edSAlexander Usyskin 	mei_set_devstate(dev, MEI_DEV_ENABLED);
7306adb8efbSTomas Winkler 	dev->reset_count = 0;
73104bb139aSTomas Winkler 
732025fb792SAlexander Usyskin 	schedule_work(&dev->bus_rescan_work);
7336009595aSTomas Winkler 
7342bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
7352bf94cabSTomas Winkler 	dev_dbg(dev->dev, "rpm: autosuspend\n");
736d5f8e166SAlexander Usyskin 	pm_request_autosuspend(dev->dev);
7379ca9050bSTomas Winkler }
7389ca9050bSTomas Winkler 
7396aae48ffSTomas Winkler /**
740a8605ea2SAlexander Usyskin  * mei_hbuf_acquire - try to acquire host buffer
7416aae48ffSTomas Winkler  *
7426aae48ffSTomas Winkler  * @dev: the device structure
743a8605ea2SAlexander Usyskin  * Return: true if host buffer was acquired
7446aae48ffSTomas Winkler  */
mei_hbuf_acquire(struct mei_device * dev)7456aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev)
7466aae48ffSTomas Winkler {
74704bb139aSTomas Winkler 	if (mei_pg_state(dev) == MEI_PG_ON ||
7483dc196eaSAlexander Usyskin 	    mei_pg_in_transition(dev)) {
7492bf94cabSTomas Winkler 		dev_dbg(dev->dev, "device is in pg\n");
75004bb139aSTomas Winkler 		return false;
75104bb139aSTomas Winkler 	}
75204bb139aSTomas Winkler 
7536aae48ffSTomas Winkler 	if (!dev->hbuf_is_ready) {
7542bf94cabSTomas Winkler 		dev_dbg(dev->dev, "hbuf is not ready\n");
7556aae48ffSTomas Winkler 		return false;
7566aae48ffSTomas Winkler 	}
7576aae48ffSTomas Winkler 
7586aae48ffSTomas Winkler 	dev->hbuf_is_ready = false;
7596aae48ffSTomas Winkler 
7606aae48ffSTomas Winkler 	return true;
7616aae48ffSTomas Winkler }
7629ca9050bSTomas Winkler 
7639ca9050bSTomas Winkler /**
764a4307fe4SAlexander Usyskin  * mei_cl_wake_all - wake up readers, writers and event waiters so
765a4307fe4SAlexander Usyskin  *                 they can be interrupted
766a4307fe4SAlexander Usyskin  *
767a4307fe4SAlexander Usyskin  * @cl: host client
768a4307fe4SAlexander Usyskin  */
mei_cl_wake_all(struct mei_cl * cl)769a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl)
770a4307fe4SAlexander Usyskin {
771a4307fe4SAlexander Usyskin 	struct mei_device *dev = cl->dev;
772a4307fe4SAlexander Usyskin 
773a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
774a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->rx_wait)) {
775a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up reading client!\n");
776a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->rx_wait);
777a4307fe4SAlexander Usyskin 	}
778a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
779a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->tx_wait)) {
780a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up writing client!\n");
781a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->tx_wait);
782a4307fe4SAlexander Usyskin 	}
783a4307fe4SAlexander Usyskin 	/* synchronized under device mutex */
784a4307fe4SAlexander Usyskin 	if (waitqueue_active(&cl->ev_wait)) {
785a4307fe4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up waiting for event clients!\n");
786a4307fe4SAlexander Usyskin 		wake_up_interruptible(&cl->ev_wait);
787a4307fe4SAlexander Usyskin 	}
7887ff4bdd4SAlexander Usyskin 	/* synchronized under device mutex */
7897ff4bdd4SAlexander Usyskin 	if (waitqueue_active(&cl->wait)) {
7907ff4bdd4SAlexander Usyskin 		cl_dbg(dev, cl, "Waking up ctrl write clients!\n");
79169f1804aSAlexander Usyskin 		wake_up(&cl->wait);
7927ff4bdd4SAlexander Usyskin 	}
793a4307fe4SAlexander Usyskin }
794a4307fe4SAlexander Usyskin 
795a4307fe4SAlexander Usyskin /**
7963c666182STomas Winkler  * mei_cl_set_disconnected - set disconnected state and clear
7973c666182STomas Winkler  *   associated states and resources
7983c666182STomas Winkler  *
7993c666182STomas Winkler  * @cl: host client
8003c666182STomas Winkler  */
mei_cl_set_disconnected(struct mei_cl * cl)801669c256cSAlexander Usyskin static void mei_cl_set_disconnected(struct mei_cl *cl)
8023c666182STomas Winkler {
8033c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8043c666182STomas Winkler 
8053c666182STomas Winkler 	if (cl->state == MEI_FILE_DISCONNECTED ||
806bd47b526SAlexander Usyskin 	    cl->state <= MEI_FILE_INITIALIZING)
8073c666182STomas Winkler 		return;
8083c666182STomas Winkler 
8093c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTED;
81015ffa991SAlexander Usyskin 	mei_io_tx_list_free_cl(&dev->write_list, cl, NULL);
81115ffa991SAlexander Usyskin 	mei_io_tx_list_free_cl(&dev->write_waiting_list, cl, NULL);
812f046192dSTomas Winkler 	mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
813f046192dSTomas Winkler 	mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
814a4307fe4SAlexander Usyskin 	mei_cl_wake_all(cl);
81546978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds = 0;
8164034b81bSTomas Winkler 	cl->tx_flow_ctrl_creds = 0;
8173c666182STomas Winkler 	cl->timer_count = 0;
818d49ed64aSAlexander Usyskin 
819a03d77f6SAlexander Usyskin 	if (!cl->me_cl)
820a03d77f6SAlexander Usyskin 		return;
821a03d77f6SAlexander Usyskin 
822a03d77f6SAlexander Usyskin 	if (!WARN_ON(cl->me_cl->connect_count == 0))
823a03d77f6SAlexander Usyskin 		cl->me_cl->connect_count--;
824a03d77f6SAlexander Usyskin 
825c241e9b1SAlexander Usyskin 	if (cl->me_cl->connect_count == 0)
8264034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds = 0;
827c241e9b1SAlexander Usyskin 
828d49ed64aSAlexander Usyskin 	mei_me_cl_put(cl->me_cl);
829d49ed64aSAlexander Usyskin 	cl->me_cl = NULL;
8303c666182STomas Winkler }
8313c666182STomas Winkler 
mei_cl_set_connecting(struct mei_cl * cl,struct mei_me_client * me_cl)832a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl)
833a03d77f6SAlexander Usyskin {
8341df629efSAlexander Usyskin 	if (!mei_me_cl_get(me_cl))
835a03d77f6SAlexander Usyskin 		return -ENOENT;
836a03d77f6SAlexander Usyskin 
8371df629efSAlexander Usyskin 	/* only one connection is allowed for fixed address clients */
8381df629efSAlexander Usyskin 	if (me_cl->props.fixed_address) {
8391df629efSAlexander Usyskin 		if (me_cl->connect_count) {
8401df629efSAlexander Usyskin 			mei_me_cl_put(me_cl);
8411df629efSAlexander Usyskin 			return -EBUSY;
8421df629efSAlexander Usyskin 		}
8431df629efSAlexander Usyskin 	}
8441df629efSAlexander Usyskin 
8451df629efSAlexander Usyskin 	cl->me_cl = me_cl;
846a03d77f6SAlexander Usyskin 	cl->state = MEI_FILE_CONNECTING;
847a03d77f6SAlexander Usyskin 	cl->me_cl->connect_count++;
848a03d77f6SAlexander Usyskin 
849a03d77f6SAlexander Usyskin 	return 0;
850a03d77f6SAlexander Usyskin }
851a03d77f6SAlexander Usyskin 
8523c666182STomas Winkler /*
8533c666182STomas Winkler  * mei_cl_send_disconnect - send disconnect request
8543c666182STomas Winkler  *
8553c666182STomas Winkler  * @cl: host client
8563c666182STomas Winkler  * @cb: callback block
8573c666182STomas Winkler  *
8583c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8593c666182STomas Winkler  */
mei_cl_send_disconnect(struct mei_cl * cl,struct mei_cl_cb * cb)8603c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
8613c666182STomas Winkler {
8623c666182STomas Winkler 	struct mei_device *dev;
8633c666182STomas Winkler 	int ret;
8643c666182STomas Winkler 
8653c666182STomas Winkler 	dev = cl->dev;
8663c666182STomas Winkler 
8673c666182STomas Winkler 	ret = mei_hbm_cl_disconnect_req(dev, cl);
8683c666182STomas Winkler 	cl->status = ret;
8693c666182STomas Winkler 	if (ret) {
8703c666182STomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
8713c666182STomas Winkler 		return ret;
8723c666182STomas Winkler 	}
8733c666182STomas Winkler 
874962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
87595953618SAlexander Usyskin 	cl->timer_count = dev->timeouts.connect;
8761892fc2eSAlexander Usyskin 	mei_schedule_stall_timer(dev);
8773c666182STomas Winkler 
8783c666182STomas Winkler 	return 0;
8793c666182STomas Winkler }
8803c666182STomas Winkler 
8813c666182STomas Winkler /**
8823c666182STomas Winkler  * mei_cl_irq_disconnect - processes close related operation from
8833c666182STomas Winkler  *	interrupt thread context - send disconnect request
8843c666182STomas Winkler  *
8853c666182STomas Winkler  * @cl: client
8863c666182STomas Winkler  * @cb: callback block.
8873c666182STomas Winkler  * @cmpl_list: complete list.
8883c666182STomas Winkler  *
8893c666182STomas Winkler  * Return: 0, OK; otherwise, error.
8903c666182STomas Winkler  */
mei_cl_irq_disconnect(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)8913c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
892962ff7bcSAlexander Usyskin 			  struct list_head *cmpl_list)
8933c666182STomas Winkler {
8943c666182STomas Winkler 	struct mei_device *dev = cl->dev;
8953c666182STomas Winkler 	u32 msg_slots;
8963c666182STomas Winkler 	int slots;
8973c666182STomas Winkler 	int ret;
8983c666182STomas Winkler 
89998e70866STomas Winkler 	msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
9003c666182STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
901de877437STomas Winkler 	if (slots < 0)
902de877437STomas Winkler 		return -EOVERFLOW;
9033c666182STomas Winkler 
904de877437STomas Winkler 	if ((u32)slots < msg_slots)
9053c666182STomas Winkler 		return -EMSGSIZE;
9063c666182STomas Winkler 
9073c666182STomas Winkler 	ret = mei_cl_send_disconnect(cl, cb);
9083c666182STomas Winkler 	if (ret)
909962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
9103c666182STomas Winkler 
9113c666182STomas Winkler 	return ret;
9123c666182STomas Winkler }
9133c666182STomas Winkler 
9143c666182STomas Winkler /**
91518901357SAlexander Usyskin  * __mei_cl_disconnect - disconnect host client from the me one
91618901357SAlexander Usyskin  *     internal function runtime pm has to be already acquired
9179ca9050bSTomas Winkler  *
91890e0b5f1STomas Winkler  * @cl: host client
9199ca9050bSTomas Winkler  *
920a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
9219ca9050bSTomas Winkler  */
__mei_cl_disconnect(struct mei_cl * cl)92218901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl)
9239ca9050bSTomas Winkler {
92490e0b5f1STomas Winkler 	struct mei_device *dev;
9259ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
926fe2f17ebSAlexander Usyskin 	int rets;
9279ca9050bSTomas Winkler 
92890e0b5f1STomas Winkler 	dev = cl->dev;
92990e0b5f1STomas Winkler 
9303c666182STomas Winkler 	cl->state = MEI_FILE_DISCONNECTING;
9313c666182STomas Winkler 
9323030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
9333030dc05STomas Winkler 	if (!cb) {
9343030dc05STomas Winkler 		rets = -ENOMEM;
9353c666182STomas Winkler 		goto out;
9363030dc05STomas Winkler 	}
9379ca9050bSTomas Winkler 
9383c666182STomas Winkler 	if (mei_hbuf_acquire(dev)) {
9393c666182STomas Winkler 		rets = mei_cl_send_disconnect(cl, cb);
9403c666182STomas Winkler 		if (rets) {
9413c666182STomas Winkler 			cl_err(dev, cl, "failed to disconnect.\n");
9423c666182STomas Winkler 			goto out;
9439ca9050bSTomas Winkler 		}
9443c666182STomas Winkler 	}
9453c666182STomas Winkler 
9469ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
9477ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
9487ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECT_REPLY ||
9497ff4bdd4SAlexander Usyskin 			   cl->state == MEI_FILE_DISCONNECTED,
95095953618SAlexander Usyskin 			   dev->timeouts.cl_connect);
9519ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
952fe2f17ebSAlexander Usyskin 
9533c666182STomas Winkler 	rets = cl->status;
9547ff4bdd4SAlexander Usyskin 	if (cl->state != MEI_FILE_DISCONNECT_REPLY &&
9557ff4bdd4SAlexander Usyskin 	    cl->state != MEI_FILE_DISCONNECTED) {
956fe2f17ebSAlexander Usyskin 		cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
957fe2f17ebSAlexander Usyskin 		rets = -ETIME;
9589ca9050bSTomas Winkler 	}
9599ca9050bSTomas Winkler 
9603c666182STomas Winkler out:
9613c666182STomas Winkler 	/* we disconnect also on error */
9623c666182STomas Winkler 	mei_cl_set_disconnected(cl);
9633c666182STomas Winkler 	if (!rets)
9643c666182STomas Winkler 		cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
9653c666182STomas Winkler 
96618901357SAlexander Usyskin 	mei_io_cb_free(cb);
96718901357SAlexander Usyskin 	return rets;
96818901357SAlexander Usyskin }
96918901357SAlexander Usyskin 
97018901357SAlexander Usyskin /**
97118901357SAlexander Usyskin  * mei_cl_disconnect - disconnect host client from the me one
97218901357SAlexander Usyskin  *
97318901357SAlexander Usyskin  * @cl: host client
97418901357SAlexander Usyskin  *
97518901357SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
97618901357SAlexander Usyskin  *
97718901357SAlexander Usyskin  * Return: 0 on success, <0 on failure.
97818901357SAlexander Usyskin  */
mei_cl_disconnect(struct mei_cl * cl)97918901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl)
98018901357SAlexander Usyskin {
98118901357SAlexander Usyskin 	struct mei_device *dev;
98218901357SAlexander Usyskin 	int rets;
98318901357SAlexander Usyskin 
98418901357SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
98518901357SAlexander Usyskin 		return -ENODEV;
98618901357SAlexander Usyskin 
98718901357SAlexander Usyskin 	dev = cl->dev;
98818901357SAlexander Usyskin 
98918901357SAlexander Usyskin 	cl_dbg(dev, cl, "disconnecting");
99018901357SAlexander Usyskin 
99118901357SAlexander Usyskin 	if (!mei_cl_is_connected(cl))
99218901357SAlexander Usyskin 		return 0;
99318901357SAlexander Usyskin 
99418901357SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
99518901357SAlexander Usyskin 		mei_cl_set_disconnected(cl);
99618901357SAlexander Usyskin 		return 0;
99718901357SAlexander Usyskin 	}
99818901357SAlexander Usyskin 
99936edb140SAlexander Usyskin 	if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
100036edb140SAlexander Usyskin 	    dev->dev_state == MEI_DEV_POWER_DOWN) {
10017ae079acSTomas Winkler 		cl_dbg(dev, cl, "Device is powering down, don't bother with disconnection\n");
10027ae079acSTomas Winkler 		mei_cl_set_disconnected(cl);
10037ae079acSTomas Winkler 		return 0;
10047ae079acSTomas Winkler 	}
10057ae079acSTomas Winkler 
100618901357SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
100718901357SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
100818901357SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
100918901357SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
101018901357SAlexander Usyskin 		return rets;
101118901357SAlexander Usyskin 	}
101218901357SAlexander Usyskin 
101318901357SAlexander Usyskin 	rets = __mei_cl_disconnect(cl);
101418901357SAlexander Usyskin 
101504bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
10162bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
10172bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
101804bb139aSTomas Winkler 
10199ca9050bSTomas Winkler 	return rets;
10209ca9050bSTomas Winkler }
10219ca9050bSTomas Winkler 
10229ca9050bSTomas Winkler 
10239ca9050bSTomas Winkler /**
102490e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
102590e0b5f1STomas Winkler  *    client with the same me client id is connecting
10269ca9050bSTomas Winkler  *
10279ca9050bSTomas Winkler  * @cl: private data of the file object
10289ca9050bSTomas Winkler  *
1029a8605ea2SAlexander Usyskin  * Return: true if other client is connected, false - otherwise.
10309ca9050bSTomas Winkler  */
mei_cl_is_other_connecting(struct mei_cl * cl)10310c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl)
10329ca9050bSTomas Winkler {
103390e0b5f1STomas Winkler 	struct mei_device *dev;
10340c53357cSTomas Winkler 	struct mei_cl_cb *cb;
103590e0b5f1STomas Winkler 
103690e0b5f1STomas Winkler 	dev = cl->dev;
103790e0b5f1STomas Winkler 
1038962ff7bcSAlexander Usyskin 	list_for_each_entry(cb, &dev->ctrl_rd_list, list) {
10390c53357cSTomas Winkler 		if (cb->fop_type == MEI_FOP_CONNECT &&
1040d49ed64aSAlexander Usyskin 		    mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
104190e0b5f1STomas Winkler 			return true;
10429ca9050bSTomas Winkler 	}
104390e0b5f1STomas Winkler 
104490e0b5f1STomas Winkler 	return false;
10459ca9050bSTomas Winkler }
10469ca9050bSTomas Winkler 
10479ca9050bSTomas Winkler /**
10480c53357cSTomas Winkler  * mei_cl_send_connect - send connect request
10490c53357cSTomas Winkler  *
10500c53357cSTomas Winkler  * @cl: host client
10510c53357cSTomas Winkler  * @cb: callback block
10520c53357cSTomas Winkler  *
10530c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10540c53357cSTomas Winkler  */
mei_cl_send_connect(struct mei_cl * cl,struct mei_cl_cb * cb)10550c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
10560c53357cSTomas Winkler {
10570c53357cSTomas Winkler 	struct mei_device *dev;
10580c53357cSTomas Winkler 	int ret;
10590c53357cSTomas Winkler 
10600c53357cSTomas Winkler 	dev = cl->dev;
10610c53357cSTomas Winkler 
10620c53357cSTomas Winkler 	ret = mei_hbm_cl_connect_req(dev, cl);
10630c53357cSTomas Winkler 	cl->status = ret;
10640c53357cSTomas Winkler 	if (ret) {
10650c53357cSTomas Winkler 		cl->state = MEI_FILE_DISCONNECT_REPLY;
10660c53357cSTomas Winkler 		return ret;
10670c53357cSTomas Winkler 	}
10680c53357cSTomas Winkler 
1069962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
107095953618SAlexander Usyskin 	cl->timer_count = dev->timeouts.connect;
10711892fc2eSAlexander Usyskin 	mei_schedule_stall_timer(dev);
10720c53357cSTomas Winkler 	return 0;
10730c53357cSTomas Winkler }
10740c53357cSTomas Winkler 
10750c53357cSTomas Winkler /**
10760c53357cSTomas Winkler  * mei_cl_irq_connect - send connect request in irq_thread context
10770c53357cSTomas Winkler  *
10780c53357cSTomas Winkler  * @cl: host client
10790c53357cSTomas Winkler  * @cb: callback block
10800c53357cSTomas Winkler  * @cmpl_list: complete list
10810c53357cSTomas Winkler  *
10820c53357cSTomas Winkler  * Return: 0, OK; otherwise, error.
10830c53357cSTomas Winkler  */
mei_cl_irq_connect(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)10840c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
1085962ff7bcSAlexander Usyskin 		       struct list_head *cmpl_list)
10860c53357cSTomas Winkler {
10870c53357cSTomas Winkler 	struct mei_device *dev = cl->dev;
10880c53357cSTomas Winkler 	u32 msg_slots;
10890c53357cSTomas Winkler 	int slots;
10900c53357cSTomas Winkler 	int rets;
10910c53357cSTomas Winkler 
10920c53357cSTomas Winkler 	if (mei_cl_is_other_connecting(cl))
10930c53357cSTomas Winkler 		return 0;
10940c53357cSTomas Winkler 
109598e70866STomas Winkler 	msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
1096de877437STomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1097de877437STomas Winkler 	if (slots < 0)
1098de877437STomas Winkler 		return -EOVERFLOW;
1099de877437STomas Winkler 
1100de877437STomas Winkler 	if ((u32)slots < msg_slots)
11010c53357cSTomas Winkler 		return -EMSGSIZE;
11020c53357cSTomas Winkler 
11030c53357cSTomas Winkler 	rets = mei_cl_send_connect(cl, cb);
11040c53357cSTomas Winkler 	if (rets)
1105962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
11060c53357cSTomas Winkler 
11070c53357cSTomas Winkler 	return rets;
11080c53357cSTomas Winkler }
11090c53357cSTomas Winkler 
11100c53357cSTomas Winkler /**
111183ce0741SAlexander Usyskin  * mei_cl_connect - connect host client to the me one
11129f81abdaSTomas Winkler  *
11139f81abdaSTomas Winkler  * @cl: host client
1114d49ed64aSAlexander Usyskin  * @me_cl: me client
11153030dc05STomas Winkler  * @fp: pointer to file structure
11169f81abdaSTomas Winkler  *
11179f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
11189f81abdaSTomas Winkler  *
1119a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
11209f81abdaSTomas Winkler  */
mei_cl_connect(struct mei_cl * cl,struct mei_me_client * me_cl,const struct file * fp)1121d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
11223030dc05STomas Winkler 		   const struct file *fp)
11239f81abdaSTomas Winkler {
11249f81abdaSTomas Winkler 	struct mei_device *dev;
11259f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
11269f81abdaSTomas Winkler 	int rets;
11279f81abdaSTomas Winkler 
11281df629efSAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev || !me_cl))
11299f81abdaSTomas Winkler 		return -ENODEV;
11309f81abdaSTomas Winkler 
11319f81abdaSTomas Winkler 	dev = cl->dev;
11329f81abdaSTomas Winkler 
11331df629efSAlexander Usyskin 	rets = mei_cl_set_connecting(cl, me_cl);
11341df629efSAlexander Usyskin 	if (rets)
11355d882460SAlexander Usyskin 		goto nortpm;
11361df629efSAlexander Usyskin 
11371df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl)) {
11381df629efSAlexander Usyskin 		cl->state = MEI_FILE_CONNECTED;
11395d882460SAlexander Usyskin 		rets = 0;
11405d882460SAlexander Usyskin 		goto nortpm;
11411df629efSAlexander Usyskin 	}
11421df629efSAlexander Usyskin 
11432bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
114404bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
11452bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
114604bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
11471df629efSAlexander Usyskin 		goto nortpm;
114804bb139aSTomas Winkler 	}
114904bb139aSTomas Winkler 
11503030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
11513030dc05STomas Winkler 	if (!cb) {
11523030dc05STomas Winkler 		rets = -ENOMEM;
11539f81abdaSTomas Winkler 		goto out;
11543030dc05STomas Winkler 	}
11550c53357cSTomas Winkler 
11566aae48ffSTomas Winkler 	/* run hbuf acquire last so we don't have to undo */
11576aae48ffSTomas Winkler 	if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
11580c53357cSTomas Winkler 		rets = mei_cl_send_connect(cl, cb);
11590c53357cSTomas Winkler 		if (rets)
11609f81abdaSTomas Winkler 			goto out;
11619f81abdaSTomas Winkler 	}
11629f81abdaSTomas Winkler 
11639f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
116412f45ed4STomas Winkler 	wait_event_timeout(cl->wait,
11659f81abdaSTomas Winkler 			(cl->state == MEI_FILE_CONNECTED ||
11667ff4bdd4SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECTED ||
116718901357SAlexander Usyskin 			 cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
11683c666182STomas Winkler 			 cl->state == MEI_FILE_DISCONNECT_REPLY),
116995953618SAlexander Usyskin 			dev->timeouts.cl_connect);
11709f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
11719f81abdaSTomas Winkler 
1172f3de9b63STomas Winkler 	if (!mei_cl_is_connected(cl)) {
117318901357SAlexander Usyskin 		if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
1174f046192dSTomas Winkler 			mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
1175f046192dSTomas Winkler 			mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
117618901357SAlexander Usyskin 			 /* ignore disconnect return valuue;
117718901357SAlexander Usyskin 			  * in case of failure reset will be invoked
117818901357SAlexander Usyskin 			  */
117918901357SAlexander Usyskin 			__mei_cl_disconnect(cl);
118018901357SAlexander Usyskin 			rets = -EFAULT;
118118901357SAlexander Usyskin 			goto out;
118218901357SAlexander Usyskin 		}
118318901357SAlexander Usyskin 
11840c53357cSTomas Winkler 		/* timeout or something went really wrong */
1185285e2996SAlexander Usyskin 		if (!cl->status)
1186285e2996SAlexander Usyskin 			cl->status = -EFAULT;
11879f81abdaSTomas Winkler 	}
11889f81abdaSTomas Winkler 
11899f81abdaSTomas Winkler 	rets = cl->status;
11909f81abdaSTomas Winkler out:
119104bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
11922bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
11932bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
119404bb139aSTomas Winkler 
11959f81abdaSTomas Winkler 	mei_io_cb_free(cb);
11960c53357cSTomas Winkler 
11971df629efSAlexander Usyskin nortpm:
11980c53357cSTomas Winkler 	if (!mei_cl_is_connected(cl))
11990c53357cSTomas Winkler 		mei_cl_set_disconnected(cl);
12000c53357cSTomas Winkler 
12019f81abdaSTomas Winkler 	return rets;
12029f81abdaSTomas Winkler }
12039f81abdaSTomas Winkler 
12049f81abdaSTomas Winkler /**
120503b8d341STomas Winkler  * mei_cl_alloc_linked - allocate and link host client
120603b8d341STomas Winkler  *
120703b8d341STomas Winkler  * @dev: the device structure
120803b8d341STomas Winkler  *
120903b8d341STomas Winkler  * Return: cl on success ERR_PTR on failure
121003b8d341STomas Winkler  */
mei_cl_alloc_linked(struct mei_device * dev)12117851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev)
121203b8d341STomas Winkler {
121303b8d341STomas Winkler 	struct mei_cl *cl;
121403b8d341STomas Winkler 	int ret;
121503b8d341STomas Winkler 
121603b8d341STomas Winkler 	cl = mei_cl_allocate(dev);
121703b8d341STomas Winkler 	if (!cl) {
121803b8d341STomas Winkler 		ret = -ENOMEM;
121903b8d341STomas Winkler 		goto err;
122003b8d341STomas Winkler 	}
122103b8d341STomas Winkler 
12227851e008SAlexander Usyskin 	ret = mei_cl_link(cl);
122303b8d341STomas Winkler 	if (ret)
122403b8d341STomas Winkler 		goto err;
122503b8d341STomas Winkler 
122603b8d341STomas Winkler 	return cl;
122703b8d341STomas Winkler err:
122803b8d341STomas Winkler 	kfree(cl);
122903b8d341STomas Winkler 	return ERR_PTR(ret);
123003b8d341STomas Winkler }
123103b8d341STomas Winkler 
123203b8d341STomas Winkler /**
12334034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl.
12349ca9050bSTomas Winkler  *
123506ee536bSAlexander Usyskin  * @cl: host client
12369ca9050bSTomas Winkler  *
12374034b81bSTomas Winkler  * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise.
12389ca9050bSTomas Winkler  */
mei_cl_tx_flow_ctrl_creds(struct mei_cl * cl)12394034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl)
12409ca9050bSTomas Winkler {
1241d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
124290e0b5f1STomas Winkler 		return -EINVAL;
124390e0b5f1STomas Winkler 
12444034b81bSTomas Winkler 	if (cl->tx_flow_ctrl_creds > 0)
12459ca9050bSTomas Winkler 		return 1;
12469ca9050bSTomas Winkler 
1247a808c80cSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12481df629efSAlexander Usyskin 		return 1;
12491df629efSAlexander Usyskin 
1250d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
12514034b81bSTomas Winkler 		if (cl->me_cl->tx_flow_ctrl_creds > 0)
1252d49ed64aSAlexander Usyskin 			return 1;
125312d00665SAlexander Usyskin 	}
1254d49ed64aSAlexander Usyskin 	return 0;
12559ca9050bSTomas Winkler }
12569ca9050bSTomas Winkler 
12579ca9050bSTomas Winkler /**
12584034b81bSTomas Winkler  * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits
12594034b81bSTomas Winkler  *   for a client
12609ca9050bSTomas Winkler  *
12614034b81bSTomas Winkler  * @cl: host client
1262393b148fSMasanari Iida  *
1263a8605ea2SAlexander Usyskin  * Return:
12649ca9050bSTomas Winkler  *	0 on success
12659ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
12669ca9050bSTomas Winkler  */
mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl * cl)12674034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
12689ca9050bSTomas Winkler {
1269d49ed64aSAlexander Usyskin 	if (WARN_ON(!cl || !cl->me_cl))
127090e0b5f1STomas Winkler 		return -EINVAL;
127190e0b5f1STomas Winkler 
12721df629efSAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
12731df629efSAlexander Usyskin 		return 0;
12741df629efSAlexander Usyskin 
1275d49ed64aSAlexander Usyskin 	if (mei_cl_is_single_recv_buf(cl)) {
12764034b81bSTomas Winkler 		if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0))
1277d49ed64aSAlexander Usyskin 			return -EINVAL;
12784034b81bSTomas Winkler 		cl->me_cl->tx_flow_ctrl_creds--;
12799ca9050bSTomas Winkler 	} else {
12804034b81bSTomas Winkler 		if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
1281d49ed64aSAlexander Usyskin 			return -EINVAL;
12824034b81bSTomas Winkler 		cl->tx_flow_ctrl_creds--;
12839ca9050bSTomas Winkler 	}
1284d49ed64aSAlexander Usyskin 	return 0;
12859ca9050bSTomas Winkler }
12869ca9050bSTomas Winkler 
12879ca9050bSTomas Winkler /**
1288f35fe5f4SAlexander Usyskin  * mei_cl_vtag_alloc - allocate and fill the vtag structure
1289f35fe5f4SAlexander Usyskin  *
1290f35fe5f4SAlexander Usyskin  * @fp: pointer to file structure
1291f35fe5f4SAlexander Usyskin  * @vtag: vm tag
1292f35fe5f4SAlexander Usyskin  *
1293f35fe5f4SAlexander Usyskin  * Return:
1294f35fe5f4SAlexander Usyskin  * * Pointer to allocated struct - on success
1295f35fe5f4SAlexander Usyskin  * * ERR_PTR(-ENOMEM) on memory allocation failure
1296f35fe5f4SAlexander Usyskin  */
mei_cl_vtag_alloc(struct file * fp,u8 vtag)1297f35fe5f4SAlexander Usyskin struct mei_cl_vtag *mei_cl_vtag_alloc(struct file *fp, u8 vtag)
1298f35fe5f4SAlexander Usyskin {
1299f35fe5f4SAlexander Usyskin 	struct mei_cl_vtag *cl_vtag;
1300f35fe5f4SAlexander Usyskin 
1301f35fe5f4SAlexander Usyskin 	cl_vtag = kzalloc(sizeof(*cl_vtag), GFP_KERNEL);
1302f35fe5f4SAlexander Usyskin 	if (!cl_vtag)
1303f35fe5f4SAlexander Usyskin 		return ERR_PTR(-ENOMEM);
1304f35fe5f4SAlexander Usyskin 
1305f35fe5f4SAlexander Usyskin 	INIT_LIST_HEAD(&cl_vtag->list);
1306f35fe5f4SAlexander Usyskin 	cl_vtag->vtag = vtag;
1307f35fe5f4SAlexander Usyskin 	cl_vtag->fp = fp;
1308f35fe5f4SAlexander Usyskin 
1309f35fe5f4SAlexander Usyskin 	return cl_vtag;
1310f35fe5f4SAlexander Usyskin }
1311f35fe5f4SAlexander Usyskin 
1312f35fe5f4SAlexander Usyskin /**
1313f35fe5f4SAlexander Usyskin  * mei_cl_fp_by_vtag - obtain the file pointer by vtag
1314f35fe5f4SAlexander Usyskin  *
1315f35fe5f4SAlexander Usyskin  * @cl: host client
131685261c1fSAlexander Usyskin  * @vtag: virtual tag
1317f35fe5f4SAlexander Usyskin  *
1318f35fe5f4SAlexander Usyskin  * Return:
1319f35fe5f4SAlexander Usyskin  * * A file pointer - on success
1320f35fe5f4SAlexander Usyskin  * * ERR_PTR(-ENOENT) if vtag is not found in the client vtag list
1321f35fe5f4SAlexander Usyskin  */
mei_cl_fp_by_vtag(const struct mei_cl * cl,u8 vtag)1322f35fe5f4SAlexander Usyskin const struct file *mei_cl_fp_by_vtag(const struct mei_cl *cl, u8 vtag)
1323f35fe5f4SAlexander Usyskin {
1324f35fe5f4SAlexander Usyskin 	struct mei_cl_vtag *vtag_l;
1325f35fe5f4SAlexander Usyskin 
1326f35fe5f4SAlexander Usyskin 	list_for_each_entry(vtag_l, &cl->vtag_map, list)
132785261c1fSAlexander Usyskin 		/* The client on bus has one fixed fp */
132885261c1fSAlexander Usyskin 		if ((cl->cldev && mei_cldev_enabled(cl->cldev)) ||
132985261c1fSAlexander Usyskin 		    vtag_l->vtag == vtag)
1330f35fe5f4SAlexander Usyskin 			return vtag_l->fp;
1331f35fe5f4SAlexander Usyskin 
1332f35fe5f4SAlexander Usyskin 	return ERR_PTR(-ENOENT);
1333f35fe5f4SAlexander Usyskin }
1334f35fe5f4SAlexander Usyskin 
1335f35fe5f4SAlexander Usyskin /**
1336f35fe5f4SAlexander Usyskin  * mei_cl_reset_read_by_vtag - reset pending_read flag by given vtag
1337f35fe5f4SAlexander Usyskin  *
1338f35fe5f4SAlexander Usyskin  * @cl: host client
1339f35fe5f4SAlexander Usyskin  * @vtag: vm tag
1340f35fe5f4SAlexander Usyskin  */
mei_cl_reset_read_by_vtag(const struct mei_cl * cl,u8 vtag)1341f35fe5f4SAlexander Usyskin static void mei_cl_reset_read_by_vtag(const struct mei_cl *cl, u8 vtag)
1342f35fe5f4SAlexander Usyskin {
1343f35fe5f4SAlexander Usyskin 	struct mei_cl_vtag *vtag_l;
1344f35fe5f4SAlexander Usyskin 
1345f35fe5f4SAlexander Usyskin 	list_for_each_entry(vtag_l, &cl->vtag_map, list) {
1346f17ef47bSAlexander Usyskin 		/* The client on bus has one fixed vtag map */
1347f17ef47bSAlexander Usyskin 		if ((cl->cldev && mei_cldev_enabled(cl->cldev)) ||
1348f17ef47bSAlexander Usyskin 		    vtag_l->vtag == vtag) {
1349f35fe5f4SAlexander Usyskin 			vtag_l->pending_read = false;
1350f35fe5f4SAlexander Usyskin 			break;
1351f35fe5f4SAlexander Usyskin 		}
1352f35fe5f4SAlexander Usyskin 	}
1353f35fe5f4SAlexander Usyskin }
1354f35fe5f4SAlexander Usyskin 
1355f35fe5f4SAlexander Usyskin /**
1356f35fe5f4SAlexander Usyskin  * mei_cl_read_vtag_add_fc - add flow control for next pending reader
1357f35fe5f4SAlexander Usyskin  *                           in the vtag list
1358f35fe5f4SAlexander Usyskin  *
1359f35fe5f4SAlexander Usyskin  * @cl: host client
1360f35fe5f4SAlexander Usyskin  */
mei_cl_read_vtag_add_fc(struct mei_cl * cl)1361f35fe5f4SAlexander Usyskin static void mei_cl_read_vtag_add_fc(struct mei_cl *cl)
1362f35fe5f4SAlexander Usyskin {
1363f35fe5f4SAlexander Usyskin 	struct mei_cl_vtag *cl_vtag;
1364f35fe5f4SAlexander Usyskin 
1365f35fe5f4SAlexander Usyskin 	list_for_each_entry(cl_vtag, &cl->vtag_map, list) {
1366f35fe5f4SAlexander Usyskin 		if (cl_vtag->pending_read) {
1367f35fe5f4SAlexander Usyskin 			if (mei_cl_enqueue_ctrl_wr_cb(cl,
1368f35fe5f4SAlexander Usyskin 						      mei_cl_mtu(cl),
1369f35fe5f4SAlexander Usyskin 						      MEI_FOP_READ,
1370f35fe5f4SAlexander Usyskin 						      cl_vtag->fp))
1371f35fe5f4SAlexander Usyskin 				cl->rx_flow_ctrl_creds++;
1372f35fe5f4SAlexander Usyskin 			break;
1373f35fe5f4SAlexander Usyskin 		}
1374f35fe5f4SAlexander Usyskin 	}
1375f35fe5f4SAlexander Usyskin }
1376f35fe5f4SAlexander Usyskin 
1377f35fe5f4SAlexander Usyskin /**
1378f35fe5f4SAlexander Usyskin  * mei_cl_vt_support_check - check if client support vtags
1379f35fe5f4SAlexander Usyskin  *
1380f35fe5f4SAlexander Usyskin  * @cl: host client
1381f35fe5f4SAlexander Usyskin  *
1382f35fe5f4SAlexander Usyskin  * Return:
1383f35fe5f4SAlexander Usyskin  * * 0 - supported, or not connected at all
1384f35fe5f4SAlexander Usyskin  * * -EOPNOTSUPP - vtags are not supported by client
1385f35fe5f4SAlexander Usyskin  */
mei_cl_vt_support_check(const struct mei_cl * cl)1386f35fe5f4SAlexander Usyskin int mei_cl_vt_support_check(const struct mei_cl *cl)
1387f35fe5f4SAlexander Usyskin {
1388f35fe5f4SAlexander Usyskin 	struct mei_device *dev = cl->dev;
1389f35fe5f4SAlexander Usyskin 
1390f35fe5f4SAlexander Usyskin 	if (!dev->hbm_f_vt_supported)
1391f35fe5f4SAlexander Usyskin 		return -EOPNOTSUPP;
1392f35fe5f4SAlexander Usyskin 
1393f35fe5f4SAlexander Usyskin 	if (!cl->me_cl)
1394f35fe5f4SAlexander Usyskin 		return 0;
1395f35fe5f4SAlexander Usyskin 
1396f35fe5f4SAlexander Usyskin 	return cl->me_cl->props.vt_supported ? 0 : -EOPNOTSUPP;
1397f35fe5f4SAlexander Usyskin }
1398f35fe5f4SAlexander Usyskin 
1399f35fe5f4SAlexander Usyskin /**
1400d1376f3dSAlexander Usyskin  * mei_cl_add_rd_completed - add read completed callback to list with lock
1401f35fe5f4SAlexander Usyskin  *                           and vtag check
1402d1376f3dSAlexander Usyskin  *
1403d1376f3dSAlexander Usyskin  * @cl: host client
1404d1376f3dSAlexander Usyskin  * @cb: callback block
1405d1376f3dSAlexander Usyskin  *
1406d1376f3dSAlexander Usyskin  */
mei_cl_add_rd_completed(struct mei_cl * cl,struct mei_cl_cb * cb)1407d1376f3dSAlexander Usyskin void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
1408d1376f3dSAlexander Usyskin {
1409f35fe5f4SAlexander Usyskin 	const struct file *fp;
1410f35fe5f4SAlexander Usyskin 
1411f35fe5f4SAlexander Usyskin 	if (!mei_cl_vt_support_check(cl)) {
1412f35fe5f4SAlexander Usyskin 		fp = mei_cl_fp_by_vtag(cl, cb->vtag);
1413f35fe5f4SAlexander Usyskin 		if (IS_ERR(fp)) {
1414f35fe5f4SAlexander Usyskin 			/* client already disconnected, discarding */
1415f35fe5f4SAlexander Usyskin 			mei_io_cb_free(cb);
1416f35fe5f4SAlexander Usyskin 			return;
1417f35fe5f4SAlexander Usyskin 		}
1418f35fe5f4SAlexander Usyskin 		cb->fp = fp;
1419f35fe5f4SAlexander Usyskin 		mei_cl_reset_read_by_vtag(cl, cb->vtag);
1420f35fe5f4SAlexander Usyskin 		mei_cl_read_vtag_add_fc(cl);
1421f35fe5f4SAlexander Usyskin 	}
1422f35fe5f4SAlexander Usyskin 
1423d1376f3dSAlexander Usyskin 	spin_lock(&cl->rd_completed_lock);
1424d1376f3dSAlexander Usyskin 	list_add_tail(&cb->list, &cl->rd_completed);
1425d1376f3dSAlexander Usyskin 	spin_unlock(&cl->rd_completed_lock);
1426d1376f3dSAlexander Usyskin }
1427d1376f3dSAlexander Usyskin 
1428d1376f3dSAlexander Usyskin /**
1429d1376f3dSAlexander Usyskin  * mei_cl_del_rd_completed - free read completed callback with lock
1430d1376f3dSAlexander Usyskin  *
1431d1376f3dSAlexander Usyskin  * @cl: host client
1432d1376f3dSAlexander Usyskin  * @cb: callback block
1433d1376f3dSAlexander Usyskin  *
1434d1376f3dSAlexander Usyskin  */
mei_cl_del_rd_completed(struct mei_cl * cl,struct mei_cl_cb * cb)1435d1376f3dSAlexander Usyskin void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
1436d1376f3dSAlexander Usyskin {
1437d1376f3dSAlexander Usyskin 	spin_lock(&cl->rd_completed_lock);
1438d1376f3dSAlexander Usyskin 	mei_io_cb_free(cb);
1439d1376f3dSAlexander Usyskin 	spin_unlock(&cl->rd_completed_lock);
1440d1376f3dSAlexander Usyskin }
1441d1376f3dSAlexander Usyskin 
1442d1376f3dSAlexander Usyskin /**
144351678ccbSTomas Winkler  *  mei_cl_notify_fop2req - convert fop to proper request
144451678ccbSTomas Winkler  *
144551678ccbSTomas Winkler  * @fop: client notification start response command
144651678ccbSTomas Winkler  *
144751678ccbSTomas Winkler  * Return:  MEI_HBM_NOTIFICATION_START/STOP
144851678ccbSTomas Winkler  */
mei_cl_notify_fop2req(enum mei_cb_file_ops fop)144951678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop)
145051678ccbSTomas Winkler {
145151678ccbSTomas Winkler 	if (fop == MEI_FOP_NOTIFY_START)
145251678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_START;
145351678ccbSTomas Winkler 	else
145451678ccbSTomas Winkler 		return MEI_HBM_NOTIFICATION_STOP;
145551678ccbSTomas Winkler }
145651678ccbSTomas Winkler 
145751678ccbSTomas Winkler /**
145851678ccbSTomas Winkler  *  mei_cl_notify_req2fop - convert notification request top file operation type
145951678ccbSTomas Winkler  *
146051678ccbSTomas Winkler  * @req: hbm notification request type
146151678ccbSTomas Winkler  *
146251678ccbSTomas Winkler  * Return:  MEI_FOP_NOTIFY_START/STOP
146351678ccbSTomas Winkler  */
mei_cl_notify_req2fop(u8 req)146451678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
146551678ccbSTomas Winkler {
146651678ccbSTomas Winkler 	if (req == MEI_HBM_NOTIFICATION_START)
146751678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_START;
146851678ccbSTomas Winkler 	else
146951678ccbSTomas Winkler 		return MEI_FOP_NOTIFY_STOP;
147051678ccbSTomas Winkler }
147151678ccbSTomas Winkler 
147251678ccbSTomas Winkler /**
147351678ccbSTomas Winkler  * mei_cl_irq_notify - send notification request in irq_thread context
147451678ccbSTomas Winkler  *
147551678ccbSTomas Winkler  * @cl: client
147651678ccbSTomas Winkler  * @cb: callback block.
147751678ccbSTomas Winkler  * @cmpl_list: complete list.
147851678ccbSTomas Winkler  *
147951678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
148051678ccbSTomas Winkler  */
mei_cl_irq_notify(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)148151678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
1482962ff7bcSAlexander Usyskin 		      struct list_head *cmpl_list)
148351678ccbSTomas Winkler {
148451678ccbSTomas Winkler 	struct mei_device *dev = cl->dev;
148551678ccbSTomas Winkler 	u32 msg_slots;
148651678ccbSTomas Winkler 	int slots;
148751678ccbSTomas Winkler 	int ret;
148851678ccbSTomas Winkler 	bool request;
148951678ccbSTomas Winkler 
149098e70866STomas Winkler 	msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request));
149151678ccbSTomas Winkler 	slots = mei_hbuf_empty_slots(dev);
1492de877437STomas Winkler 	if (slots < 0)
1493de877437STomas Winkler 		return -EOVERFLOW;
149451678ccbSTomas Winkler 
1495de877437STomas Winkler 	if ((u32)slots < msg_slots)
149651678ccbSTomas Winkler 		return -EMSGSIZE;
149751678ccbSTomas Winkler 
149851678ccbSTomas Winkler 	request = mei_cl_notify_fop2req(cb->fop_type);
149951678ccbSTomas Winkler 	ret = mei_hbm_cl_notify_req(dev, cl, request);
150051678ccbSTomas Winkler 	if (ret) {
150151678ccbSTomas Winkler 		cl->status = ret;
1502962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
150351678ccbSTomas Winkler 		return ret;
150451678ccbSTomas Winkler 	}
150551678ccbSTomas Winkler 
1506962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
150751678ccbSTomas Winkler 	return 0;
150851678ccbSTomas Winkler }
150951678ccbSTomas Winkler 
151051678ccbSTomas Winkler /**
151151678ccbSTomas Winkler  * mei_cl_notify_request - send notification stop/start request
151251678ccbSTomas Winkler  *
151351678ccbSTomas Winkler  * @cl: host client
15143030dc05STomas Winkler  * @fp: associate request with file
151551678ccbSTomas Winkler  * @request: 1 for start or 0 for stop
151651678ccbSTomas Winkler  *
151751678ccbSTomas Winkler  * Locking: called under "dev->device_lock" lock
151851678ccbSTomas Winkler  *
151951678ccbSTomas Winkler  * Return: 0 on such and error otherwise.
152051678ccbSTomas Winkler  */
mei_cl_notify_request(struct mei_cl * cl,const struct file * fp,u8 request)1521f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl,
15223030dc05STomas Winkler 			  const struct file *fp, u8 request)
152351678ccbSTomas Winkler {
152451678ccbSTomas Winkler 	struct mei_device *dev;
152551678ccbSTomas Winkler 	struct mei_cl_cb *cb;
152651678ccbSTomas Winkler 	enum mei_cb_file_ops fop_type;
152751678ccbSTomas Winkler 	int rets;
152851678ccbSTomas Winkler 
152951678ccbSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
153051678ccbSTomas Winkler 		return -ENODEV;
153151678ccbSTomas Winkler 
153251678ccbSTomas Winkler 	dev = cl->dev;
153351678ccbSTomas Winkler 
153451678ccbSTomas Winkler 	if (!dev->hbm_f_ev_supported) {
153551678ccbSTomas Winkler 		cl_dbg(dev, cl, "notifications not supported\n");
153651678ccbSTomas Winkler 		return -EOPNOTSUPP;
153751678ccbSTomas Winkler 	}
153851678ccbSTomas Winkler 
15397c47d2caSAlexander Usyskin 	if (!mei_cl_is_connected(cl))
15407c47d2caSAlexander Usyskin 		return -ENODEV;
15417c47d2caSAlexander Usyskin 
154251678ccbSTomas Winkler 	rets = pm_runtime_get(dev->dev);
154351678ccbSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
154451678ccbSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
154551678ccbSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
154651678ccbSTomas Winkler 		return rets;
154751678ccbSTomas Winkler 	}
154851678ccbSTomas Winkler 
154951678ccbSTomas Winkler 	fop_type = mei_cl_notify_req2fop(request);
15503030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp);
155151678ccbSTomas Winkler 	if (!cb) {
155251678ccbSTomas Winkler 		rets = -ENOMEM;
155351678ccbSTomas Winkler 		goto out;
155451678ccbSTomas Winkler 	}
155551678ccbSTomas Winkler 
155651678ccbSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
155751678ccbSTomas Winkler 		if (mei_hbm_cl_notify_req(dev, cl, request)) {
155851678ccbSTomas Winkler 			rets = -ENODEV;
155951678ccbSTomas Winkler 			goto out;
156051678ccbSTomas Winkler 		}
1561962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, &dev->ctrl_rd_list);
156251678ccbSTomas Winkler 	}
156351678ccbSTomas Winkler 
156451678ccbSTomas Winkler 	mutex_unlock(&dev->device_lock);
15657ff4bdd4SAlexander Usyskin 	wait_event_timeout(cl->wait,
1566a19bf053SAlexander Usyskin 			   cl->notify_en == request ||
1567a19bf053SAlexander Usyskin 			   cl->status ||
1568a19bf053SAlexander Usyskin 			   !mei_cl_is_connected(cl),
156995953618SAlexander Usyskin 			   dev->timeouts.cl_connect);
157051678ccbSTomas Winkler 	mutex_lock(&dev->device_lock);
157151678ccbSTomas Winkler 
15724a8eaa96SAlexander Usyskin 	if (cl->notify_en != request && !cl->status)
157351678ccbSTomas Winkler 		cl->status = -EFAULT;
157451678ccbSTomas Winkler 
157551678ccbSTomas Winkler 	rets = cl->status;
157651678ccbSTomas Winkler 
157751678ccbSTomas Winkler out:
157851678ccbSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
157951678ccbSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
158051678ccbSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
158151678ccbSTomas Winkler 
158251678ccbSTomas Winkler 	mei_io_cb_free(cb);
158351678ccbSTomas Winkler 	return rets;
158451678ccbSTomas Winkler }
158551678ccbSTomas Winkler 
158651678ccbSTomas Winkler /**
1587237092bfSTomas Winkler  * mei_cl_notify - raise notification
1588237092bfSTomas Winkler  *
1589237092bfSTomas Winkler  * @cl: host client
1590237092bfSTomas Winkler  *
1591237092bfSTomas Winkler  * Locking: called under "dev->device_lock" lock
1592237092bfSTomas Winkler  */
mei_cl_notify(struct mei_cl * cl)1593237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl)
1594237092bfSTomas Winkler {
1595237092bfSTomas Winkler 	struct mei_device *dev;
1596237092bfSTomas Winkler 
1597237092bfSTomas Winkler 	if (!cl || !cl->dev)
1598237092bfSTomas Winkler 		return;
1599237092bfSTomas Winkler 
1600237092bfSTomas Winkler 	dev = cl->dev;
1601237092bfSTomas Winkler 
1602237092bfSTomas Winkler 	if (!cl->notify_en)
1603237092bfSTomas Winkler 		return;
1604237092bfSTomas Winkler 
1605237092bfSTomas Winkler 	cl_dbg(dev, cl, "notify event");
1606237092bfSTomas Winkler 	cl->notify_ev = true;
1607850f8940STomas Winkler 	if (!mei_cl_bus_notify_event(cl))
1608850f8940STomas Winkler 		wake_up_interruptible(&cl->ev_wait);
1609237092bfSTomas Winkler 
1610237092bfSTomas Winkler 	if (cl->ev_async)
1611237092bfSTomas Winkler 		kill_fasync(&cl->ev_async, SIGIO, POLL_PRI);
1612bb2ef9c3SAlexander Usyskin 
1613237092bfSTomas Winkler }
1614237092bfSTomas Winkler 
1615237092bfSTomas Winkler /**
1616b38a362fSTomas Winkler  * mei_cl_notify_get - get or wait for notification event
1617b38a362fSTomas Winkler  *
1618b38a362fSTomas Winkler  * @cl: host client
1619b38a362fSTomas Winkler  * @block: this request is blocking
1620b38a362fSTomas Winkler  * @notify_ev: true if notification event was received
1621b38a362fSTomas Winkler  *
1622b38a362fSTomas Winkler  * Locking: called under "dev->device_lock" lock
1623b38a362fSTomas Winkler  *
1624b38a362fSTomas Winkler  * Return: 0 on such and error otherwise.
1625b38a362fSTomas Winkler  */
mei_cl_notify_get(struct mei_cl * cl,bool block,bool * notify_ev)1626b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
1627b38a362fSTomas Winkler {
1628b38a362fSTomas Winkler 	struct mei_device *dev;
1629b38a362fSTomas Winkler 	int rets;
1630b38a362fSTomas Winkler 
1631b38a362fSTomas Winkler 	*notify_ev = false;
1632b38a362fSTomas Winkler 
1633b38a362fSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1634b38a362fSTomas Winkler 		return -ENODEV;
1635b38a362fSTomas Winkler 
1636b38a362fSTomas Winkler 	dev = cl->dev;
1637b38a362fSTomas Winkler 
16386c0d6701SAlexander Usyskin 	if (!dev->hbm_f_ev_supported) {
16396c0d6701SAlexander Usyskin 		cl_dbg(dev, cl, "notifications not supported\n");
16406c0d6701SAlexander Usyskin 		return -EOPNOTSUPP;
16416c0d6701SAlexander Usyskin 	}
16426c0d6701SAlexander Usyskin 
1643b38a362fSTomas Winkler 	if (!mei_cl_is_connected(cl))
1644b38a362fSTomas Winkler 		return -ENODEV;
1645b38a362fSTomas Winkler 
1646b38a362fSTomas Winkler 	if (cl->notify_ev)
1647b38a362fSTomas Winkler 		goto out;
1648b38a362fSTomas Winkler 
1649b38a362fSTomas Winkler 	if (!block)
1650b38a362fSTomas Winkler 		return -EAGAIN;
1651b38a362fSTomas Winkler 
1652b38a362fSTomas Winkler 	mutex_unlock(&dev->device_lock);
1653b38a362fSTomas Winkler 	rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev);
1654b38a362fSTomas Winkler 	mutex_lock(&dev->device_lock);
1655b38a362fSTomas Winkler 
1656b38a362fSTomas Winkler 	if (rets < 0)
1657b38a362fSTomas Winkler 		return rets;
1658b38a362fSTomas Winkler 
1659b38a362fSTomas Winkler out:
1660b38a362fSTomas Winkler 	*notify_ev = cl->notify_ev;
1661b38a362fSTomas Winkler 	cl->notify_ev = false;
1662b38a362fSTomas Winkler 	return 0;
1663b38a362fSTomas Winkler }
1664b38a362fSTomas Winkler 
1665b38a362fSTomas Winkler /**
1666393b148fSMasanari Iida  * mei_cl_read_start - the start read client message function.
16679ca9050bSTomas Winkler  *
166890e0b5f1STomas Winkler  * @cl: host client
1669ce23139cSAlexander Usyskin  * @length: number of bytes to read
1670bca67d68STomas Winkler  * @fp: pointer to file structure
16719ca9050bSTomas Winkler  *
1672a8605ea2SAlexander Usyskin  * Return: 0 on success, <0 on failure.
16739ca9050bSTomas Winkler  */
mei_cl_read_start(struct mei_cl * cl,size_t length,const struct file * fp)1674f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
16759ca9050bSTomas Winkler {
167690e0b5f1STomas Winkler 	struct mei_device *dev;
16779ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
16789ca9050bSTomas Winkler 	int rets;
16799ca9050bSTomas Winkler 
168090e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
168190e0b5f1STomas Winkler 		return -ENODEV;
168290e0b5f1STomas Winkler 
168390e0b5f1STomas Winkler 	dev = cl->dev;
168490e0b5f1STomas Winkler 
1685b950ac1dSTomas Winkler 	if (!mei_cl_is_connected(cl))
16869ca9050bSTomas Winkler 		return -ENODEV;
16879ca9050bSTomas Winkler 
1688d49ed64aSAlexander Usyskin 	if (!mei_me_cl_is_active(cl->me_cl)) {
1689d49ed64aSAlexander Usyskin 		cl_err(dev, cl, "no such me client\n");
16907ca96aa2SAlexander Usyskin 		return  -ENOTTY;
16919ca9050bSTomas Winkler 	}
16921df629efSAlexander Usyskin 
1693394a77d0SAlexander Usyskin 	if (mei_cl_is_fixed_address(cl))
1694e51dfa5aSAlexander Usyskin 		return 0;
1695e51dfa5aSAlexander Usyskin 
169646978adaSAlexander Usyskin 	/* HW currently supports only one pending read */
1697f35fe5f4SAlexander Usyskin 	if (cl->rx_flow_ctrl_creds) {
1698f35fe5f4SAlexander Usyskin 		mei_cl_set_read_by_fp(cl, fp);
169946978adaSAlexander Usyskin 		return -EBUSY;
1700f35fe5f4SAlexander Usyskin 	}
170146978adaSAlexander Usyskin 
17023030dc05STomas Winkler 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
17031df629efSAlexander Usyskin 	if (!cb)
17041df629efSAlexander Usyskin 		return -ENOMEM;
17051df629efSAlexander Usyskin 
1706f35fe5f4SAlexander Usyskin 	mei_cl_set_read_by_fp(cl, fp);
1707f35fe5f4SAlexander Usyskin 
17082bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
170904bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
17102bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
171104bb139aSTomas Winkler 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
17121df629efSAlexander Usyskin 		goto nortpm;
171304bb139aSTomas Winkler 	}
171404bb139aSTomas Winkler 
171546978adaSAlexander Usyskin 	rets = 0;
17166aae48ffSTomas Winkler 	if (mei_hbuf_acquire(dev)) {
171786113500SAlexander Usyskin 		rets = mei_hbm_cl_flow_control_req(dev, cl);
171886113500SAlexander Usyskin 		if (rets < 0)
171904bb139aSTomas Winkler 			goto out;
172004bb139aSTomas Winkler 
172146978adaSAlexander Usyskin 		list_move_tail(&cb->list, &cl->rd_pending);
17229ca9050bSTomas Winkler 	}
172346978adaSAlexander Usyskin 	cl->rx_flow_ctrl_creds++;
1724accb884bSChao Bi 
172504bb139aSTomas Winkler out:
172604bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
17272bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
17282bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
17291df629efSAlexander Usyskin nortpm:
173004bb139aSTomas Winkler 	if (rets)
17319ca9050bSTomas Winkler 		mei_io_cb_free(cb);
173204bb139aSTomas Winkler 
17339ca9050bSTomas Winkler 	return rets;
17349ca9050bSTomas Winkler }
17359ca9050bSTomas Winkler 
mei_ext_hdr_set_vtag(void * ext,u8 vtag)173640292383STomas Winkler static inline u8 mei_ext_hdr_set_vtag(void *ext, u8 vtag)
1737a1c4d08bSTomas Winkler {
173840292383STomas Winkler 	struct mei_ext_hdr_vtag *vtag_hdr = ext;
173940292383STomas Winkler 
174040292383STomas Winkler 	vtag_hdr->hdr.type = MEI_EXT_HDR_VTAG;
174140292383STomas Winkler 	vtag_hdr->hdr.length = mei_data2slots(sizeof(*vtag_hdr));
174240292383STomas Winkler 	vtag_hdr->vtag = vtag;
174340292383STomas Winkler 	vtag_hdr->reserved = 0;
174440292383STomas Winkler 	return vtag_hdr->hdr.length;
17450cd7c01aSTomas Winkler }
17460cd7c01aSTomas Winkler 
mei_ext_hdr_is_gsc(struct mei_ext_hdr * ext)17474ed1cc99STomas Winkler static inline bool mei_ext_hdr_is_gsc(struct mei_ext_hdr *ext)
17484ed1cc99STomas Winkler {
17494ed1cc99STomas Winkler 	return ext && ext->type == MEI_EXT_HDR_GSC;
17504ed1cc99STomas Winkler }
17514ed1cc99STomas Winkler 
mei_ext_hdr_set_gsc(struct mei_ext_hdr * ext,struct mei_ext_hdr * gsc_hdr)17524ed1cc99STomas Winkler static inline u8 mei_ext_hdr_set_gsc(struct mei_ext_hdr *ext, struct mei_ext_hdr *gsc_hdr)
17534ed1cc99STomas Winkler {
17544ed1cc99STomas Winkler 	memcpy(ext, gsc_hdr, mei_ext_hdr_len(gsc_hdr));
17554ed1cc99STomas Winkler 	return ext->length;
17564ed1cc99STomas Winkler }
17574ed1cc99STomas Winkler 
17580cd7c01aSTomas Winkler /**
17590cd7c01aSTomas Winkler  * mei_msg_hdr_init - allocate and initialize mei message header
17600cd7c01aSTomas Winkler  *
17610cd7c01aSTomas Winkler  * @cb: message callback structure
17620cd7c01aSTomas Winkler  *
17637615da2bSTomas Winkler  * Return: a pointer to initialized header or ERR_PTR on failure
17640cd7c01aSTomas Winkler  */
mei_msg_hdr_init(const struct mei_cl_cb * cb)17650cd7c01aSTomas Winkler static struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb)
17660cd7c01aSTomas Winkler {
17670cd7c01aSTomas Winkler 	size_t hdr_len;
17680cd7c01aSTomas Winkler 	struct mei_ext_meta_hdr *meta;
17690cd7c01aSTomas Winkler 	struct mei_msg_hdr *mei_hdr;
17704ed1cc99STomas Winkler 	bool is_ext, is_hbm, is_gsc, is_vtag;
17714ed1cc99STomas Winkler 	struct mei_ext_hdr *next_ext;
17720cd7c01aSTomas Winkler 
17730cd7c01aSTomas Winkler 	if (!cb)
17740cd7c01aSTomas Winkler 		return ERR_PTR(-EINVAL);
17750cd7c01aSTomas Winkler 
17760cd7c01aSTomas Winkler 	/* Extended header for vtag is attached only on the first fragment */
17770cd7c01aSTomas Winkler 	is_vtag = (cb->vtag && cb->buf_idx == 0);
17784ed1cc99STomas Winkler 	is_hbm = cb->cl->me_cl->client_id == 0;
17794ed1cc99STomas Winkler 	is_gsc = ((!is_hbm) && cb->cl->dev->hbm_f_gsc_supported && mei_ext_hdr_is_gsc(cb->ext_hdr));
17804ed1cc99STomas Winkler 	is_ext = is_vtag || is_gsc;
17810cd7c01aSTomas Winkler 
17820cd7c01aSTomas Winkler 	/* Compute extended header size */
17830cd7c01aSTomas Winkler 	hdr_len = sizeof(*mei_hdr);
17840cd7c01aSTomas Winkler 
17850cd7c01aSTomas Winkler 	if (!is_ext)
17860cd7c01aSTomas Winkler 		goto setup_hdr;
17870cd7c01aSTomas Winkler 
17880cd7c01aSTomas Winkler 	hdr_len += sizeof(*meta);
17890cd7c01aSTomas Winkler 	if (is_vtag)
179040292383STomas Winkler 		hdr_len += sizeof(struct mei_ext_hdr_vtag);
17910cd7c01aSTomas Winkler 
17924ed1cc99STomas Winkler 	if (is_gsc)
17934ed1cc99STomas Winkler 		hdr_len += mei_ext_hdr_len(cb->ext_hdr);
17944ed1cc99STomas Winkler 
17950cd7c01aSTomas Winkler setup_hdr:
17960cd7c01aSTomas Winkler 	mei_hdr = kzalloc(hdr_len, GFP_KERNEL);
17970cd7c01aSTomas Winkler 	if (!mei_hdr)
17980cd7c01aSTomas Winkler 		return ERR_PTR(-ENOMEM);
17990cd7c01aSTomas Winkler 
1800a1c4d08bSTomas Winkler 	mei_hdr->host_addr = mei_cl_host_addr(cb->cl);
1801a1c4d08bSTomas Winkler 	mei_hdr->me_addr = mei_cl_me_id(cb->cl);
1802a1c4d08bSTomas Winkler 	mei_hdr->internal = cb->internal;
18030cd7c01aSTomas Winkler 	mei_hdr->extended = is_ext;
18040cd7c01aSTomas Winkler 
18050cd7c01aSTomas Winkler 	if (!is_ext)
18060cd7c01aSTomas Winkler 		goto out;
18070cd7c01aSTomas Winkler 
18080cd7c01aSTomas Winkler 	meta = (struct mei_ext_meta_hdr *)mei_hdr->extension;
18094ed1cc99STomas Winkler 	meta->size = 0;
18104ed1cc99STomas Winkler 	next_ext = (struct mei_ext_hdr *)meta->hdrs;
18110cd7c01aSTomas Winkler 	if (is_vtag) {
18120cd7c01aSTomas Winkler 		meta->count++;
18134ed1cc99STomas Winkler 		meta->size += mei_ext_hdr_set_vtag(next_ext, cb->vtag);
18144ed1cc99STomas Winkler 		next_ext = mei_ext_next(next_ext);
18150cd7c01aSTomas Winkler 	}
18164ed1cc99STomas Winkler 
18174ed1cc99STomas Winkler 	if (is_gsc) {
18184ed1cc99STomas Winkler 		meta->count++;
18194ed1cc99STomas Winkler 		meta->size += mei_ext_hdr_set_gsc(next_ext, cb->ext_hdr);
18204ed1cc99STomas Winkler 		next_ext = mei_ext_next(next_ext);
18214ed1cc99STomas Winkler 	}
18224ed1cc99STomas Winkler 
18230cd7c01aSTomas Winkler out:
18240cd7c01aSTomas Winkler 	mei_hdr->length = hdr_len - sizeof(*mei_hdr);
18250cd7c01aSTomas Winkler 	return mei_hdr;
1826a1c4d08bSTomas Winkler }
1827a1c4d08bSTomas Winkler 
1828a1c4d08bSTomas Winkler /**
18299d098192STomas Winkler  * mei_cl_irq_write - write a message to device
183021767546STomas Winkler  *	from the interrupt thread context
183121767546STomas Winkler  *
183221767546STomas Winkler  * @cl: client
183321767546STomas Winkler  * @cb: callback block.
183421767546STomas Winkler  * @cmpl_list: complete list.
183521767546STomas Winkler  *
1836a8605ea2SAlexander Usyskin  * Return: 0, OK; otherwise error.
183721767546STomas Winkler  */
mei_cl_irq_write(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)18389d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
1839962ff7bcSAlexander Usyskin 		     struct list_head *cmpl_list)
184021767546STomas Winkler {
1841136698e5STomas Winkler 	struct mei_device *dev;
1842136698e5STomas Winkler 	struct mei_msg_data *buf;
18430cd7c01aSTomas Winkler 	struct mei_msg_hdr *mei_hdr = NULL;
18440cd7c01aSTomas Winkler 	size_t hdr_len;
1845c30362ccSTomas Winkler 	size_t hbuf_len, dr_len;
18464ed1cc99STomas Winkler 	size_t buf_len = 0;
18470cd7c01aSTomas Winkler 	size_t data_len;
18488c8d964cSTomas Winkler 	int hbuf_slots;
1849c30362ccSTomas Winkler 	u32 dr_slots;
1850c30362ccSTomas Winkler 	u32 dma_len;
18512ebf8c94STomas Winkler 	int rets;
1852b8b73035SAlexander Usyskin 	bool first_chunk;
18534ed1cc99STomas Winkler 	const void *data = NULL;
185421767546STomas Winkler 
1855136698e5STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1856136698e5STomas Winkler 		return -ENODEV;
1857136698e5STomas Winkler 
1858136698e5STomas Winkler 	dev = cl->dev;
1859136698e5STomas Winkler 
18605db7514dSTomas Winkler 	buf = &cb->buf;
1861136698e5STomas Winkler 
1862b8b73035SAlexander Usyskin 	first_chunk = cb->buf_idx == 0;
1863b8b73035SAlexander Usyskin 
18644034b81bSTomas Winkler 	rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
1865136698e5STomas Winkler 	if (rets < 0)
1866e09ee853SAlexander Usyskin 		goto err;
1867136698e5STomas Winkler 
1868136698e5STomas Winkler 	if (rets == 0) {
1869136698e5STomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
1870136698e5STomas Winkler 		return 0;
1871136698e5STomas Winkler 	}
1872136698e5STomas Winkler 
18734ed1cc99STomas Winkler 	if (buf->data) {
18740cd7c01aSTomas Winkler 		buf_len = buf->size - cb->buf_idx;
1875c30362ccSTomas Winkler 		data = buf->data + cb->buf_idx;
18764ed1cc99STomas Winkler 	}
18778c8d964cSTomas Winkler 	hbuf_slots = mei_hbuf_empty_slots(dev);
18788c8d964cSTomas Winkler 	if (hbuf_slots < 0) {
18798c8d964cSTomas Winkler 		rets = -EOVERFLOW;
18808c8d964cSTomas Winkler 		goto err;
18818c8d964cSTomas Winkler 	}
188298e70866STomas Winkler 
18833aef021bSTomas Winkler 	hbuf_len = mei_slots2data(hbuf_slots) & MEI_MSG_MAX_LEN_MASK;
1884c30362ccSTomas Winkler 	dr_slots = mei_dma_ring_empty_slots(dev);
1885c30362ccSTomas Winkler 	dr_len = mei_slots2data(dr_slots);
18868c8d964cSTomas Winkler 
18870cd7c01aSTomas Winkler 	mei_hdr = mei_msg_hdr_init(cb);
18880cd7c01aSTomas Winkler 	if (IS_ERR(mei_hdr)) {
18890cd7c01aSTomas Winkler 		rets = PTR_ERR(mei_hdr);
18900cd7c01aSTomas Winkler 		mei_hdr = NULL;
18910cd7c01aSTomas Winkler 		goto err;
18920cd7c01aSTomas Winkler 	}
18930cd7c01aSTomas Winkler 
18940cd7c01aSTomas Winkler 	hdr_len = sizeof(*mei_hdr) + mei_hdr->length;
1895a1c4d08bSTomas Winkler 
18968c8d964cSTomas Winkler 	/**
18978c8d964cSTomas Winkler 	 * Split the message only if we can write the whole host buffer
18988c8d964cSTomas Winkler 	 * otherwise wait for next time the host buffer is empty.
18998c8d964cSTomas Winkler 	 */
19000cd7c01aSTomas Winkler 	if (hdr_len + buf_len <= hbuf_len) {
19010cd7c01aSTomas Winkler 		data_len = buf_len;
19020cd7c01aSTomas Winkler 		mei_hdr->msg_complete = 1;
1903c30362ccSTomas Winkler 	} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
19040cd7c01aSTomas Winkler 		mei_hdr->dma_ring = 1;
19050cd7c01aSTomas Winkler 		if (buf_len > dr_len)
19060cd7c01aSTomas Winkler 			buf_len = dr_len;
1907c30362ccSTomas Winkler 		else
19080cd7c01aSTomas Winkler 			mei_hdr->msg_complete = 1;
1909c30362ccSTomas Winkler 
19100cd7c01aSTomas Winkler 		data_len = sizeof(dma_len);
19110cd7c01aSTomas Winkler 		dma_len = buf_len;
1912c30362ccSTomas Winkler 		data = &dma_len;
19138c8d964cSTomas Winkler 	} else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) {
19140cd7c01aSTomas Winkler 		buf_len = hbuf_len - hdr_len;
19150cd7c01aSTomas Winkler 		data_len = buf_len;
191621767546STomas Winkler 	} else {
19170cd7c01aSTomas Winkler 		kfree(mei_hdr);
191821767546STomas Winkler 		return 0;
191921767546STomas Winkler 	}
19200cd7c01aSTomas Winkler 	mei_hdr->length += data_len;
192121767546STomas Winkler 
19224ed1cc99STomas Winkler 	if (mei_hdr->dma_ring && buf->data)
19230cd7c01aSTomas Winkler 		mei_dma_ring_write(dev, buf->data + cb->buf_idx, buf_len);
19240cd7c01aSTomas Winkler 	rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len);
192521767546STomas Winkler 
1926e09ee853SAlexander Usyskin 	if (rets)
1927e09ee853SAlexander Usyskin 		goto err;
192821767546STomas Winkler 
192921767546STomas Winkler 	cl->status = 0;
19304dfaa9f7STomas Winkler 	cl->writing_state = MEI_WRITING;
19310cd7c01aSTomas Winkler 	cb->buf_idx += buf_len;
19324dfaa9f7STomas Winkler 
1933b8b73035SAlexander Usyskin 	if (first_chunk) {
1934e09ee853SAlexander Usyskin 		if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
1935e09ee853SAlexander Usyskin 			rets = -EIO;
1936e09ee853SAlexander Usyskin 			goto err;
1937e09ee853SAlexander Usyskin 		}
193821767546STomas Winkler 	}
193921767546STomas Winkler 
19400cd7c01aSTomas Winkler 	if (mei_hdr->msg_complete)
1941962ff7bcSAlexander Usyskin 		list_move_tail(&cb->list, &dev->write_waiting_list);
1942b8b73035SAlexander Usyskin 
19430cd7c01aSTomas Winkler 	kfree(mei_hdr);
194421767546STomas Winkler 	return 0;
1945e09ee853SAlexander Usyskin 
1946e09ee853SAlexander Usyskin err:
19470cd7c01aSTomas Winkler 	kfree(mei_hdr);
1948e09ee853SAlexander Usyskin 	cl->status = rets;
1949962ff7bcSAlexander Usyskin 	list_move_tail(&cb->list, cmpl_list);
1950e09ee853SAlexander Usyskin 	return rets;
195121767546STomas Winkler }
195221767546STomas Winkler 
195321767546STomas Winkler /**
19544234a6deSTomas Winkler  * mei_cl_write - submit a write cb to mei device
1955a8605ea2SAlexander Usyskin  *	assumes device_lock is locked
19564234a6deSTomas Winkler  *
19574234a6deSTomas Winkler  * @cl: host client
1958a8605ea2SAlexander Usyskin  * @cb: write callback with filled data
195983f47eeaSAlexander Usyskin  * @timeout: send timeout in milliseconds.
196083f47eeaSAlexander Usyskin  *           effective only for blocking writes: the cb->blocking is set.
196183f47eeaSAlexander Usyskin  *           set timeout to the MAX_SCHEDULE_TIMEOUT to maixum allowed wait.
19624234a6deSTomas Winkler  *
1963a8605ea2SAlexander Usyskin  * Return: number of bytes sent on success, <0 on failure.
19644234a6deSTomas Winkler  */
mei_cl_write(struct mei_cl * cl,struct mei_cl_cb * cb,unsigned long timeout)196583f47eeaSAlexander Usyskin ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, unsigned long timeout)
19664234a6deSTomas Winkler {
19674234a6deSTomas Winkler 	struct mei_device *dev;
19684234a6deSTomas Winkler 	struct mei_msg_data *buf;
19690cd7c01aSTomas Winkler 	struct mei_msg_hdr *mei_hdr = NULL;
19700cd7c01aSTomas Winkler 	size_t hdr_len;
19710cd7c01aSTomas Winkler 	size_t hbuf_len, dr_len;
19720cd7c01aSTomas Winkler 	size_t buf_len;
19730cd7c01aSTomas Winkler 	size_t data_len;
19748c8d964cSTomas Winkler 	int hbuf_slots;
1975c30362ccSTomas Winkler 	u32 dr_slots;
1976c30362ccSTomas Winkler 	u32 dma_len;
19775151e2b5STomas Winkler 	ssize_t rets;
1978e0cb6b2fSAlexander Usyskin 	bool blocking;
1979c30362ccSTomas Winkler 	const void *data;
19804234a6deSTomas Winkler 
19814234a6deSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
19824234a6deSTomas Winkler 		return -ENODEV;
19834234a6deSTomas Winkler 
19844234a6deSTomas Winkler 	if (WARN_ON(!cb))
19854234a6deSTomas Winkler 		return -EINVAL;
19864234a6deSTomas Winkler 
19874234a6deSTomas Winkler 	dev = cl->dev;
19884234a6deSTomas Winkler 
19895db7514dSTomas Winkler 	buf = &cb->buf;
19900cd7c01aSTomas Winkler 	buf_len = buf->size;
19914234a6deSTomas Winkler 
19920cd7c01aSTomas Winkler 	cl_dbg(dev, cl, "buf_len=%zd\n", buf_len);
19934234a6deSTomas Winkler 
1994c30362ccSTomas Winkler 	blocking = cb->blocking;
1995c30362ccSTomas Winkler 	data = buf->data;
1996c30362ccSTomas Winkler 
19972bf94cabSTomas Winkler 	rets = pm_runtime_get(dev->dev);
199804bb139aSTomas Winkler 	if (rets < 0 && rets != -EINPROGRESS) {
19992bf94cabSTomas Winkler 		pm_runtime_put_noidle(dev->dev);
20005151e2b5STomas Winkler 		cl_err(dev, cl, "rpm: get failed %zd\n", rets);
20016cbb097fSAlexander Usyskin 		goto free;
200204bb139aSTomas Winkler 	}
20034234a6deSTomas Winkler 
20046aae48ffSTomas Winkler 	cb->buf_idx = 0;
20056aae48ffSTomas Winkler 	cl->writing_state = MEI_IDLE;
20066aae48ffSTomas Winkler 
20074234a6deSTomas Winkler 
20084034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds(cl);
20094234a6deSTomas Winkler 	if (rets < 0)
20104234a6deSTomas Winkler 		goto err;
20114234a6deSTomas Winkler 
20120cd7c01aSTomas Winkler 	mei_hdr = mei_msg_hdr_init(cb);
20130cd7c01aSTomas Winkler 	if (IS_ERR(mei_hdr)) {
2014e2365eadSSu Hui 		rets = PTR_ERR(mei_hdr);
20150cd7c01aSTomas Winkler 		mei_hdr = NULL;
20160cd7c01aSTomas Winkler 		goto err;
20170cd7c01aSTomas Winkler 	}
20180cd7c01aSTomas Winkler 
20190cd7c01aSTomas Winkler 	hdr_len = sizeof(*mei_hdr) + mei_hdr->length;
2020a1c4d08bSTomas Winkler 
20216aae48ffSTomas Winkler 	if (rets == 0) {
20226aae48ffSTomas Winkler 		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
20230cd7c01aSTomas Winkler 		rets = buf_len;
20244234a6deSTomas Winkler 		goto out;
20254234a6deSTomas Winkler 	}
20268c8d964cSTomas Winkler 
20276aae48ffSTomas Winkler 	if (!mei_hbuf_acquire(dev)) {
20286aae48ffSTomas Winkler 		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
20290cd7c01aSTomas Winkler 		rets = buf_len;
20306aae48ffSTomas Winkler 		goto out;
20316aae48ffSTomas Winkler 	}
20324234a6deSTomas Winkler 
20338c8d964cSTomas Winkler 	hbuf_slots = mei_hbuf_empty_slots(dev);
20348c8d964cSTomas Winkler 	if (hbuf_slots < 0) {
2035*f78fff46SSu Hui 		buf_len = -EOVERFLOW;
20368c8d964cSTomas Winkler 		goto out;
20378c8d964cSTomas Winkler 	}
20388c8d964cSTomas Winkler 
20393aef021bSTomas Winkler 	hbuf_len = mei_slots2data(hbuf_slots) & MEI_MSG_MAX_LEN_MASK;
2040c30362ccSTomas Winkler 	dr_slots = mei_dma_ring_empty_slots(dev);
2041c30362ccSTomas Winkler 	dr_len =  mei_slots2data(dr_slots);
204298e70866STomas Winkler 
20430cd7c01aSTomas Winkler 	if (hdr_len + buf_len <= hbuf_len) {
20440cd7c01aSTomas Winkler 		data_len = buf_len;
20450cd7c01aSTomas Winkler 		mei_hdr->msg_complete = 1;
2046c30362ccSTomas Winkler 	} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
20470cd7c01aSTomas Winkler 		mei_hdr->dma_ring = 1;
20480cd7c01aSTomas Winkler 		if (buf_len > dr_len)
20490cd7c01aSTomas Winkler 			buf_len = dr_len;
2050c30362ccSTomas Winkler 		else
20510cd7c01aSTomas Winkler 			mei_hdr->msg_complete = 1;
2052c30362ccSTomas Winkler 
20530cd7c01aSTomas Winkler 		data_len = sizeof(dma_len);
20540cd7c01aSTomas Winkler 		dma_len = buf_len;
2055c30362ccSTomas Winkler 		data = &dma_len;
20568c8d964cSTomas Winkler 	} else {
20570cd7c01aSTomas Winkler 		buf_len = hbuf_len - hdr_len;
20580cd7c01aSTomas Winkler 		data_len = buf_len;
20594234a6deSTomas Winkler 	}
20604234a6deSTomas Winkler 
20610cd7c01aSTomas Winkler 	mei_hdr->length += data_len;
2062c30362ccSTomas Winkler 
20634ed1cc99STomas Winkler 	if (mei_hdr->dma_ring && buf->data)
20640cd7c01aSTomas Winkler 		mei_dma_ring_write(dev, buf->data, buf_len);
20650cd7c01aSTomas Winkler 	rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len);
20660cd7c01aSTomas Winkler 
20672ebf8c94STomas Winkler 	if (rets)
20684234a6deSTomas Winkler 		goto err;
20694234a6deSTomas Winkler 
20704034b81bSTomas Winkler 	rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
2071b8b73035SAlexander Usyskin 	if (rets)
2072b8b73035SAlexander Usyskin 		goto err;
2073b8b73035SAlexander Usyskin 
20744234a6deSTomas Winkler 	cl->writing_state = MEI_WRITING;
20750cd7c01aSTomas Winkler 	cb->buf_idx = buf_len;
2076c30362ccSTomas Winkler 	/* restore return value */
20770cd7c01aSTomas Winkler 	buf_len = buf->size;
20784234a6deSTomas Winkler 
20794234a6deSTomas Winkler out:
20800cd7c01aSTomas Winkler 	if (mei_hdr->msg_complete)
2081af336cabSAlexander Usyskin 		mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
2082b8b73035SAlexander Usyskin 	else
2083af336cabSAlexander Usyskin 		mei_tx_cb_enqueue(cb, &dev->write_list);
20844234a6deSTomas Winkler 
208523253c31SAlexander Usyskin 	cb = NULL;
20864234a6deSTomas Winkler 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
20874234a6deSTomas Winkler 
20884234a6deSTomas Winkler 		mutex_unlock(&dev->device_lock);
208983f47eeaSAlexander Usyskin 		rets = wait_event_interruptible_timeout(cl->tx_wait,
20900faf6a3bSAlexander Usyskin 							cl->writing_state == MEI_WRITE_COMPLETE ||
209183f47eeaSAlexander Usyskin 							(!mei_cl_is_connected(cl)),
209283f47eeaSAlexander Usyskin 							msecs_to_jiffies(timeout));
20937ca96aa2SAlexander Usyskin 		mutex_lock(&dev->device_lock);
209483f47eeaSAlexander Usyskin 		/* clean all queue on timeout as something fatal happened */
209583f47eeaSAlexander Usyskin 		if (rets == 0) {
209683f47eeaSAlexander Usyskin 			rets = -ETIME;
209783f47eeaSAlexander Usyskin 			mei_io_tx_list_free_cl(&dev->write_list, cl, NULL);
209883f47eeaSAlexander Usyskin 			mei_io_tx_list_free_cl(&dev->write_waiting_list, cl, NULL);
209983f47eeaSAlexander Usyskin 		}
21007ca96aa2SAlexander Usyskin 		/* wait_event_interruptible returns -ERESTARTSYS */
210183f47eeaSAlexander Usyskin 		if (rets > 0)
210283f47eeaSAlexander Usyskin 			rets = 0;
21037ca96aa2SAlexander Usyskin 		if (rets) {
21044234a6deSTomas Winkler 			if (signal_pending(current))
21054234a6deSTomas Winkler 				rets = -EINTR;
21067ca96aa2SAlexander Usyskin 			goto err;
21074234a6deSTomas Winkler 		}
21080faf6a3bSAlexander Usyskin 		if (cl->writing_state != MEI_WRITE_COMPLETE) {
21090faf6a3bSAlexander Usyskin 			rets = -EFAULT;
21100faf6a3bSAlexander Usyskin 			goto err;
21110faf6a3bSAlexander Usyskin 		}
21124234a6deSTomas Winkler 	}
21137ca96aa2SAlexander Usyskin 
21140cd7c01aSTomas Winkler 	rets = buf_len;
21154234a6deSTomas Winkler err:
211604bb139aSTomas Winkler 	cl_dbg(dev, cl, "rpm: autosuspend\n");
21172bf94cabSTomas Winkler 	pm_runtime_mark_last_busy(dev->dev);
21182bf94cabSTomas Winkler 	pm_runtime_put_autosuspend(dev->dev);
21196cbb097fSAlexander Usyskin free:
21206cbb097fSAlexander Usyskin 	mei_io_cb_free(cb);
212104bb139aSTomas Winkler 
21220cd7c01aSTomas Winkler 	kfree(mei_hdr);
21230cd7c01aSTomas Winkler 
21244234a6deSTomas Winkler 	return rets;
21254234a6deSTomas Winkler }
21264234a6deSTomas Winkler 
2127db086fa9STomas Winkler /**
2128db086fa9STomas Winkler  * mei_cl_complete - processes completed operation for a client
2129db086fa9STomas Winkler  *
2130db086fa9STomas Winkler  * @cl: private data of the file object.
2131db086fa9STomas Winkler  * @cb: callback block.
2132db086fa9STomas Winkler  */
mei_cl_complete(struct mei_cl * cl,struct mei_cl_cb * cb)2133db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
2134db086fa9STomas Winkler {
2135a1809d38SAlexander Usyskin 	struct mei_device *dev = cl->dev;
2136a1809d38SAlexander Usyskin 
21373c666182STomas Winkler 	switch (cb->fop_type) {
21383c666182STomas Winkler 	case MEI_FOP_WRITE:
2139af336cabSAlexander Usyskin 		mei_tx_cb_dequeue(cb);
2140db086fa9STomas Winkler 		cl->writing_state = MEI_WRITE_COMPLETE;
2141a1809d38SAlexander Usyskin 		if (waitqueue_active(&cl->tx_wait)) {
2142db086fa9STomas Winkler 			wake_up_interruptible(&cl->tx_wait);
2143a1809d38SAlexander Usyskin 		} else {
2144a1809d38SAlexander Usyskin 			pm_runtime_mark_last_busy(dev->dev);
2145a1809d38SAlexander Usyskin 			pm_request_autosuspend(dev->dev);
2146a1809d38SAlexander Usyskin 		}
21473c666182STomas Winkler 		break;
2148db086fa9STomas Winkler 
21493c666182STomas Winkler 	case MEI_FOP_READ:
2150d1376f3dSAlexander Usyskin 		mei_cl_add_rd_completed(cl, cb);
215146978adaSAlexander Usyskin 		if (!mei_cl_is_fixed_address(cl) &&
215246978adaSAlexander Usyskin 		    !WARN_ON(!cl->rx_flow_ctrl_creds))
215346978adaSAlexander Usyskin 			cl->rx_flow_ctrl_creds--;
2154a1f9ae2bSTomas Winkler 		if (!mei_cl_bus_rx_event(cl))
2155a1f9ae2bSTomas Winkler 			wake_up_interruptible(&cl->rx_wait);
21563c666182STomas Winkler 		break;
2157db086fa9STomas Winkler 
21583c666182STomas Winkler 	case MEI_FOP_CONNECT:
21593c666182STomas Winkler 	case MEI_FOP_DISCONNECT:
216051678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_STOP:
216151678ccbSTomas Winkler 	case MEI_FOP_NOTIFY_START:
2162369aea84SAlexander Usyskin 	case MEI_FOP_DMA_MAP:
2163369aea84SAlexander Usyskin 	case MEI_FOP_DMA_UNMAP:
21643c666182STomas Winkler 		if (waitqueue_active(&cl->wait))
21653c666182STomas Winkler 			wake_up(&cl->wait);
21663c666182STomas Winkler 
21673c666182STomas Winkler 		break;
21686a8d648cSAlexander Usyskin 	case MEI_FOP_DISCONNECT_RSP:
21696a8d648cSAlexander Usyskin 		mei_io_cb_free(cb);
21706a8d648cSAlexander Usyskin 		mei_cl_set_disconnected(cl);
21716a8d648cSAlexander Usyskin 		break;
21723c666182STomas Winkler 	default:
21733c666182STomas Winkler 		BUG_ON(0);
2174db086fa9STomas Winkler 	}
2175db086fa9STomas Winkler }
2176db086fa9STomas Winkler 
21774234a6deSTomas Winkler 
21784234a6deSTomas Winkler /**
2179074b4c01STomas Winkler  * mei_cl_all_disconnect - disconnect forcefully all connected clients
2180074b4c01STomas Winkler  *
2181a8605ea2SAlexander Usyskin  * @dev: mei device
2182074b4c01STomas Winkler  */
mei_cl_all_disconnect(struct mei_device * dev)2183074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev)
2184074b4c01STomas Winkler {
218531f88f57STomas Winkler 	struct mei_cl *cl;
2186074b4c01STomas Winkler 
21873c666182STomas Winkler 	list_for_each_entry(cl, &dev->file_list, link)
21883c666182STomas Winkler 		mei_cl_set_disconnected(cl);
2189074b4c01STomas Winkler }
2190685867f4SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cl_all_disconnect);
2191369aea84SAlexander Usyskin 
mei_cl_dma_map_find(struct mei_device * dev,u8 buffer_id)2192369aea84SAlexander Usyskin static struct mei_cl *mei_cl_dma_map_find(struct mei_device *dev, u8 buffer_id)
2193369aea84SAlexander Usyskin {
2194369aea84SAlexander Usyskin 	struct mei_cl *cl;
2195369aea84SAlexander Usyskin 
2196369aea84SAlexander Usyskin 	list_for_each_entry(cl, &dev->file_list, link)
2197369aea84SAlexander Usyskin 		if (cl->dma.buffer_id == buffer_id)
2198369aea84SAlexander Usyskin 			return cl;
2199369aea84SAlexander Usyskin 	return NULL;
2200369aea84SAlexander Usyskin }
2201369aea84SAlexander Usyskin 
2202369aea84SAlexander Usyskin /**
2203369aea84SAlexander Usyskin  * mei_cl_irq_dma_map - send client dma map request in irq_thread context
2204369aea84SAlexander Usyskin  *
2205369aea84SAlexander Usyskin  * @cl: client
2206369aea84SAlexander Usyskin  * @cb: callback block.
2207369aea84SAlexander Usyskin  * @cmpl_list: complete list.
2208369aea84SAlexander Usyskin  *
2209369aea84SAlexander Usyskin  * Return: 0 on such and error otherwise.
2210369aea84SAlexander Usyskin  */
mei_cl_irq_dma_map(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)2211369aea84SAlexander Usyskin int mei_cl_irq_dma_map(struct mei_cl *cl, struct mei_cl_cb *cb,
2212369aea84SAlexander Usyskin 		       struct list_head *cmpl_list)
2213369aea84SAlexander Usyskin {
2214369aea84SAlexander Usyskin 	struct mei_device *dev = cl->dev;
2215369aea84SAlexander Usyskin 	u32 msg_slots;
2216369aea84SAlexander Usyskin 	int slots;
2217369aea84SAlexander Usyskin 	int ret;
2218369aea84SAlexander Usyskin 
2219369aea84SAlexander Usyskin 	msg_slots = mei_hbm2slots(sizeof(struct hbm_client_dma_map_request));
2220369aea84SAlexander Usyskin 	slots = mei_hbuf_empty_slots(dev);
2221369aea84SAlexander Usyskin 	if (slots < 0)
2222369aea84SAlexander Usyskin 		return -EOVERFLOW;
2223369aea84SAlexander Usyskin 
2224369aea84SAlexander Usyskin 	if ((u32)slots < msg_slots)
2225369aea84SAlexander Usyskin 		return -EMSGSIZE;
2226369aea84SAlexander Usyskin 
2227369aea84SAlexander Usyskin 	ret = mei_hbm_cl_dma_map_req(dev, cl);
2228369aea84SAlexander Usyskin 	if (ret) {
2229369aea84SAlexander Usyskin 		cl->status = ret;
2230369aea84SAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
2231369aea84SAlexander Usyskin 		return ret;
2232369aea84SAlexander Usyskin 	}
2233369aea84SAlexander Usyskin 
2234369aea84SAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
2235369aea84SAlexander Usyskin 	return 0;
2236369aea84SAlexander Usyskin }
2237369aea84SAlexander Usyskin 
2238369aea84SAlexander Usyskin /**
2239369aea84SAlexander Usyskin  * mei_cl_irq_dma_unmap - send client dma unmap request in irq_thread context
2240369aea84SAlexander Usyskin  *
2241369aea84SAlexander Usyskin  * @cl: client
2242369aea84SAlexander Usyskin  * @cb: callback block.
2243369aea84SAlexander Usyskin  * @cmpl_list: complete list.
2244369aea84SAlexander Usyskin  *
2245369aea84SAlexander Usyskin  * Return: 0 on such and error otherwise.
2246369aea84SAlexander Usyskin  */
mei_cl_irq_dma_unmap(struct mei_cl * cl,struct mei_cl_cb * cb,struct list_head * cmpl_list)2247369aea84SAlexander Usyskin int mei_cl_irq_dma_unmap(struct mei_cl *cl, struct mei_cl_cb *cb,
2248369aea84SAlexander Usyskin 			 struct list_head *cmpl_list)
2249369aea84SAlexander Usyskin {
2250369aea84SAlexander Usyskin 	struct mei_device *dev = cl->dev;
2251369aea84SAlexander Usyskin 	u32 msg_slots;
2252369aea84SAlexander Usyskin 	int slots;
2253369aea84SAlexander Usyskin 	int ret;
2254369aea84SAlexander Usyskin 
2255369aea84SAlexander Usyskin 	msg_slots = mei_hbm2slots(sizeof(struct hbm_client_dma_unmap_request));
2256369aea84SAlexander Usyskin 	slots = mei_hbuf_empty_slots(dev);
2257369aea84SAlexander Usyskin 	if (slots < 0)
2258369aea84SAlexander Usyskin 		return -EOVERFLOW;
2259369aea84SAlexander Usyskin 
2260369aea84SAlexander Usyskin 	if ((u32)slots < msg_slots)
2261369aea84SAlexander Usyskin 		return -EMSGSIZE;
2262369aea84SAlexander Usyskin 
2263369aea84SAlexander Usyskin 	ret = mei_hbm_cl_dma_unmap_req(dev, cl);
2264369aea84SAlexander Usyskin 	if (ret) {
2265369aea84SAlexander Usyskin 		cl->status = ret;
2266369aea84SAlexander Usyskin 		list_move_tail(&cb->list, cmpl_list);
2267369aea84SAlexander Usyskin 		return ret;
2268369aea84SAlexander Usyskin 	}
2269369aea84SAlexander Usyskin 
2270369aea84SAlexander Usyskin 	list_move_tail(&cb->list, &dev->ctrl_rd_list);
2271369aea84SAlexander Usyskin 	return 0;
2272369aea84SAlexander Usyskin }
2273369aea84SAlexander Usyskin 
mei_cl_dma_alloc(struct mei_cl * cl,u8 buf_id,size_t size)2274369aea84SAlexander Usyskin static int mei_cl_dma_alloc(struct mei_cl *cl, u8 buf_id, size_t size)
2275369aea84SAlexander Usyskin {
2276369aea84SAlexander Usyskin 	cl->dma.vaddr = dmam_alloc_coherent(cl->dev->dev, size,
2277369aea84SAlexander Usyskin 					    &cl->dma.daddr, GFP_KERNEL);
2278369aea84SAlexander Usyskin 	if (!cl->dma.vaddr)
2279369aea84SAlexander Usyskin 		return -ENOMEM;
2280369aea84SAlexander Usyskin 
2281369aea84SAlexander Usyskin 	cl->dma.buffer_id = buf_id;
2282369aea84SAlexander Usyskin 	cl->dma.size = size;
2283369aea84SAlexander Usyskin 
2284369aea84SAlexander Usyskin 	return 0;
2285369aea84SAlexander Usyskin }
2286369aea84SAlexander Usyskin 
mei_cl_dma_free(struct mei_cl * cl)2287369aea84SAlexander Usyskin static void mei_cl_dma_free(struct mei_cl *cl)
2288369aea84SAlexander Usyskin {
2289369aea84SAlexander Usyskin 	cl->dma.buffer_id = 0;
2290369aea84SAlexander Usyskin 	dmam_free_coherent(cl->dev->dev,
2291369aea84SAlexander Usyskin 			   cl->dma.size, cl->dma.vaddr, cl->dma.daddr);
2292369aea84SAlexander Usyskin 	cl->dma.size = 0;
2293369aea84SAlexander Usyskin 	cl->dma.vaddr = NULL;
2294369aea84SAlexander Usyskin 	cl->dma.daddr = 0;
2295369aea84SAlexander Usyskin }
2296369aea84SAlexander Usyskin 
2297369aea84SAlexander Usyskin /**
229809f8c33aSTamar Mashiah  * mei_cl_dma_alloc_and_map - send client dma map request
2299369aea84SAlexander Usyskin  *
2300369aea84SAlexander Usyskin  * @cl: host client
2301369aea84SAlexander Usyskin  * @fp: pointer to file structure
2302369aea84SAlexander Usyskin  * @buffer_id: id of the mapped buffer
2303369aea84SAlexander Usyskin  * @size: size of the buffer
2304369aea84SAlexander Usyskin  *
2305369aea84SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
2306369aea84SAlexander Usyskin  *
2307369aea84SAlexander Usyskin  * Return:
2308369aea84SAlexander Usyskin  * * -ENODEV
2309369aea84SAlexander Usyskin  * * -EINVAL
2310369aea84SAlexander Usyskin  * * -EOPNOTSUPP
2311369aea84SAlexander Usyskin  * * -EPROTO
2312369aea84SAlexander Usyskin  * * -ENOMEM;
2313369aea84SAlexander Usyskin  */
mei_cl_dma_alloc_and_map(struct mei_cl * cl,const struct file * fp,u8 buffer_id,size_t size)2314369aea84SAlexander Usyskin int mei_cl_dma_alloc_and_map(struct mei_cl *cl, const struct file *fp,
2315369aea84SAlexander Usyskin 			     u8 buffer_id, size_t size)
2316369aea84SAlexander Usyskin {
2317369aea84SAlexander Usyskin 	struct mei_device *dev;
2318369aea84SAlexander Usyskin 	struct mei_cl_cb *cb;
2319369aea84SAlexander Usyskin 	int rets;
2320369aea84SAlexander Usyskin 
2321369aea84SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
2322369aea84SAlexander Usyskin 		return -ENODEV;
2323369aea84SAlexander Usyskin 
2324369aea84SAlexander Usyskin 	dev = cl->dev;
2325369aea84SAlexander Usyskin 
2326369aea84SAlexander Usyskin 	if (!dev->hbm_f_cd_supported) {
2327369aea84SAlexander Usyskin 		cl_dbg(dev, cl, "client dma is not supported\n");
2328369aea84SAlexander Usyskin 		return -EOPNOTSUPP;
2329369aea84SAlexander Usyskin 	}
2330369aea84SAlexander Usyskin 
2331369aea84SAlexander Usyskin 	if (buffer_id == 0)
2332369aea84SAlexander Usyskin 		return -EINVAL;
2333369aea84SAlexander Usyskin 
2334ce068bc7STomas Winkler 	if (mei_cl_is_connected(cl))
2335ce068bc7STomas Winkler 		return -EPROTO;
2336369aea84SAlexander Usyskin 
2337369aea84SAlexander Usyskin 	if (cl->dma_mapped)
2338369aea84SAlexander Usyskin 		return -EPROTO;
2339369aea84SAlexander Usyskin 
2340369aea84SAlexander Usyskin 	if (mei_cl_dma_map_find(dev, buffer_id)) {
2341369aea84SAlexander Usyskin 		cl_dbg(dev, cl, "client dma with id %d is already allocated\n",
2342369aea84SAlexander Usyskin 		       cl->dma.buffer_id);
2343369aea84SAlexander Usyskin 		return -EPROTO;
2344369aea84SAlexander Usyskin 	}
2345369aea84SAlexander Usyskin 
2346369aea84SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
2347369aea84SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
2348369aea84SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
2349369aea84SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
2350369aea84SAlexander Usyskin 		return rets;
2351369aea84SAlexander Usyskin 	}
2352369aea84SAlexander Usyskin 
2353369aea84SAlexander Usyskin 	rets = mei_cl_dma_alloc(cl, buffer_id, size);
2354369aea84SAlexander Usyskin 	if (rets) {
2355369aea84SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
2356369aea84SAlexander Usyskin 		return rets;
2357369aea84SAlexander Usyskin 	}
2358369aea84SAlexander Usyskin 
2359369aea84SAlexander Usyskin 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DMA_MAP, fp);
2360369aea84SAlexander Usyskin 	if (!cb) {
2361369aea84SAlexander Usyskin 		rets = -ENOMEM;
2362369aea84SAlexander Usyskin 		goto out;
2363369aea84SAlexander Usyskin 	}
2364369aea84SAlexander Usyskin 
2365369aea84SAlexander Usyskin 	if (mei_hbuf_acquire(dev)) {
2366369aea84SAlexander Usyskin 		if (mei_hbm_cl_dma_map_req(dev, cl)) {
2367369aea84SAlexander Usyskin 			rets = -ENODEV;
2368369aea84SAlexander Usyskin 			goto out;
2369369aea84SAlexander Usyskin 		}
2370369aea84SAlexander Usyskin 		list_move_tail(&cb->list, &dev->ctrl_rd_list);
2371369aea84SAlexander Usyskin 	}
2372369aea84SAlexander Usyskin 
237343aa323eSAlexander Usyskin 	cl->status = 0;
237443aa323eSAlexander Usyskin 
2375369aea84SAlexander Usyskin 	mutex_unlock(&dev->device_lock);
2376369aea84SAlexander Usyskin 	wait_event_timeout(cl->wait,
2377ce068bc7STomas Winkler 			   cl->dma_mapped || cl->status,
237895953618SAlexander Usyskin 			   dev->timeouts.cl_connect);
2379369aea84SAlexander Usyskin 	mutex_lock(&dev->device_lock);
2380369aea84SAlexander Usyskin 
2381369aea84SAlexander Usyskin 	if (!cl->dma_mapped && !cl->status)
2382369aea84SAlexander Usyskin 		cl->status = -EFAULT;
2383369aea84SAlexander Usyskin 
2384369aea84SAlexander Usyskin 	rets = cl->status;
2385369aea84SAlexander Usyskin 
2386369aea84SAlexander Usyskin out:
2387369aea84SAlexander Usyskin 	if (rets)
2388369aea84SAlexander Usyskin 		mei_cl_dma_free(cl);
2389369aea84SAlexander Usyskin 
2390369aea84SAlexander Usyskin 	cl_dbg(dev, cl, "rpm: autosuspend\n");
2391369aea84SAlexander Usyskin 	pm_runtime_mark_last_busy(dev->dev);
2392369aea84SAlexander Usyskin 	pm_runtime_put_autosuspend(dev->dev);
2393369aea84SAlexander Usyskin 
2394369aea84SAlexander Usyskin 	mei_io_cb_free(cb);
2395369aea84SAlexander Usyskin 	return rets;
2396369aea84SAlexander Usyskin }
2397369aea84SAlexander Usyskin 
2398369aea84SAlexander Usyskin /**
239909f8c33aSTamar Mashiah  * mei_cl_dma_unmap - send client dma unmap request
2400369aea84SAlexander Usyskin  *
2401369aea84SAlexander Usyskin  * @cl: host client
2402369aea84SAlexander Usyskin  * @fp: pointer to file structure
2403369aea84SAlexander Usyskin  *
2404369aea84SAlexander Usyskin  * Locking: called under "dev->device_lock" lock
2405369aea84SAlexander Usyskin  *
2406369aea84SAlexander Usyskin  * Return: 0 on such and error otherwise.
2407369aea84SAlexander Usyskin  */
mei_cl_dma_unmap(struct mei_cl * cl,const struct file * fp)2408369aea84SAlexander Usyskin int mei_cl_dma_unmap(struct mei_cl *cl, const struct file *fp)
2409369aea84SAlexander Usyskin {
2410369aea84SAlexander Usyskin 	struct mei_device *dev;
2411369aea84SAlexander Usyskin 	struct mei_cl_cb *cb;
2412369aea84SAlexander Usyskin 	int rets;
2413369aea84SAlexander Usyskin 
2414369aea84SAlexander Usyskin 	if (WARN_ON(!cl || !cl->dev))
2415369aea84SAlexander Usyskin 		return -ENODEV;
2416369aea84SAlexander Usyskin 
2417369aea84SAlexander Usyskin 	dev = cl->dev;
2418369aea84SAlexander Usyskin 
2419369aea84SAlexander Usyskin 	if (!dev->hbm_f_cd_supported) {
2420369aea84SAlexander Usyskin 		cl_dbg(dev, cl, "client dma is not supported\n");
2421369aea84SAlexander Usyskin 		return -EOPNOTSUPP;
2422369aea84SAlexander Usyskin 	}
2423369aea84SAlexander Usyskin 
2424ce068bc7STomas Winkler 	/* do not allow unmap for connected client */
2425ce068bc7STomas Winkler 	if (mei_cl_is_connected(cl))
2426ce068bc7STomas Winkler 		return -EPROTO;
2427369aea84SAlexander Usyskin 
2428369aea84SAlexander Usyskin 	if (!cl->dma_mapped)
2429369aea84SAlexander Usyskin 		return -EPROTO;
2430369aea84SAlexander Usyskin 
2431369aea84SAlexander Usyskin 	rets = pm_runtime_get(dev->dev);
2432369aea84SAlexander Usyskin 	if (rets < 0 && rets != -EINPROGRESS) {
2433369aea84SAlexander Usyskin 		pm_runtime_put_noidle(dev->dev);
2434369aea84SAlexander Usyskin 		cl_err(dev, cl, "rpm: get failed %d\n", rets);
2435369aea84SAlexander Usyskin 		return rets;
2436369aea84SAlexander Usyskin 	}
2437369aea84SAlexander Usyskin 
2438369aea84SAlexander Usyskin 	cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DMA_UNMAP, fp);
2439369aea84SAlexander Usyskin 	if (!cb) {
2440369aea84SAlexander Usyskin 		rets = -ENOMEM;
2441369aea84SAlexander Usyskin 		goto out;
2442369aea84SAlexander Usyskin 	}
2443369aea84SAlexander Usyskin 
2444369aea84SAlexander Usyskin 	if (mei_hbuf_acquire(dev)) {
2445369aea84SAlexander Usyskin 		if (mei_hbm_cl_dma_unmap_req(dev, cl)) {
2446369aea84SAlexander Usyskin 			rets = -ENODEV;
2447369aea84SAlexander Usyskin 			goto out;
2448369aea84SAlexander Usyskin 		}
2449369aea84SAlexander Usyskin 		list_move_tail(&cb->list, &dev->ctrl_rd_list);
2450369aea84SAlexander Usyskin 	}
2451369aea84SAlexander Usyskin 
245243aa323eSAlexander Usyskin 	cl->status = 0;
245343aa323eSAlexander Usyskin 
2454369aea84SAlexander Usyskin 	mutex_unlock(&dev->device_lock);
2455369aea84SAlexander Usyskin 	wait_event_timeout(cl->wait,
2456ce068bc7STomas Winkler 			   !cl->dma_mapped || cl->status,
245795953618SAlexander Usyskin 			   dev->timeouts.cl_connect);
2458369aea84SAlexander Usyskin 	mutex_lock(&dev->device_lock);
2459369aea84SAlexander Usyskin 
2460369aea84SAlexander Usyskin 	if (cl->dma_mapped && !cl->status)
2461369aea84SAlexander Usyskin 		cl->status = -EFAULT;
2462369aea84SAlexander Usyskin 
2463369aea84SAlexander Usyskin 	rets = cl->status;
2464369aea84SAlexander Usyskin 
2465369aea84SAlexander Usyskin 	if (!rets)
2466369aea84SAlexander Usyskin 		mei_cl_dma_free(cl);
2467369aea84SAlexander Usyskin out:
2468369aea84SAlexander Usyskin 	cl_dbg(dev, cl, "rpm: autosuspend\n");
2469369aea84SAlexander Usyskin 	pm_runtime_mark_last_busy(dev->dev);
2470369aea84SAlexander Usyskin 	pm_runtime_put_autosuspend(dev->dev);
2471369aea84SAlexander Usyskin 
2472369aea84SAlexander Usyskin 	mei_io_cb_free(cb);
2473369aea84SAlexander Usyskin 	return rets;
2474369aea84SAlexander Usyskin }
2475