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);
324*4882a352SAlexander Usyskin kvfree(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
500*4882a352SAlexander Usyskin cb->buf.data = kvmalloc(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) {
2035f78fff46SSu 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