xref: /linux/drivers/misc/issei/host_client.c (revision 93e4b3076b5f2d853462b9777d083c77fc0b7b23)
1*2207e8d4SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0
2*2207e8d4SAlexander Usyskin /* Copyright (C) 2023-2026 Intel Corporation */
3*2207e8d4SAlexander Usyskin #include <linux/cleanup.h>
4*2207e8d4SAlexander Usyskin #include <linux/dev_printk.h>
5*2207e8d4SAlexander Usyskin #include <linux/err.h>
6*2207e8d4SAlexander Usyskin #include <linux/fs.h>
7*2207e8d4SAlexander Usyskin #include <linux/kobject.h>
8*2207e8d4SAlexander Usyskin #include <linux/list.h>
9*2207e8d4SAlexander Usyskin #include <linux/overflow.h>
10*2207e8d4SAlexander Usyskin #include <linux/slab.h>
11*2207e8d4SAlexander Usyskin #include <linux/types.h>
12*2207e8d4SAlexander Usyskin #include <linux/wait.h>
13*2207e8d4SAlexander Usyskin #include <linux/uuid.h>
14*2207e8d4SAlexander Usyskin 
15*2207e8d4SAlexander Usyskin #include "fw_client.h"
16*2207e8d4SAlexander Usyskin #include "host_client.h"
17*2207e8d4SAlexander Usyskin #include "issei_dev.h"
18*2207e8d4SAlexander Usyskin 
19*2207e8d4SAlexander Usyskin static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl)
20*2207e8d4SAlexander Usyskin {
21*2207e8d4SAlexander Usyskin 	return cl->fw_cl ? cl->fw_cl->id : 0;
22*2207e8d4SAlexander Usyskin }
23*2207e8d4SAlexander Usyskin 
24*2207e8d4SAlexander Usyskin #define ISSEI_CL_FMT "cl:host=%02d fw=%02d "
25*2207e8d4SAlexander Usyskin 
26*2207e8d4SAlexander Usyskin #define cl_dbg(_dev_, _cl_, format, arg...) do { \
27*2207e8d4SAlexander Usyskin 	struct issei_host_client *_l_cl_ = _cl_; \
28*2207e8d4SAlexander Usyskin 	dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
29*2207e8d4SAlexander Usyskin 		__issei_cl_fw_id(_l_cl_), ##arg); \
30*2207e8d4SAlexander Usyskin } while (0)
31*2207e8d4SAlexander Usyskin 
32*2207e8d4SAlexander Usyskin #define cl_warn(_dev_, _cl_, format, arg...) do { \
33*2207e8d4SAlexander Usyskin 	struct issei_host_client *_l_cl_ = _cl_; \
34*2207e8d4SAlexander Usyskin 	dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
35*2207e8d4SAlexander Usyskin 		__issei_cl_fw_id(_l_cl_), ##arg); \
36*2207e8d4SAlexander Usyskin } while (0)
37*2207e8d4SAlexander Usyskin 
38*2207e8d4SAlexander Usyskin #define cl_err(_dev_, _cl_, format, arg...) do { \
39*2207e8d4SAlexander Usyskin 	struct issei_host_client *_l_cl_ = _cl_; \
40*2207e8d4SAlexander Usyskin 	dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \
41*2207e8d4SAlexander Usyskin 		__issei_cl_fw_id(_l_cl_), ##arg); \
42*2207e8d4SAlexander Usyskin } while (0)
43*2207e8d4SAlexander Usyskin 
44*2207e8d4SAlexander Usyskin static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf)
45*2207e8d4SAlexander Usyskin {
46*2207e8d4SAlexander Usyskin 	list_del(&wbuf->list);
47*2207e8d4SAlexander Usyskin 	kfree(wbuf->data);
48*2207e8d4SAlexander Usyskin 	kfree(wbuf);
49*2207e8d4SAlexander Usyskin }
50*2207e8d4SAlexander Usyskin 
51*2207e8d4SAlexander Usyskin static void __issei_cl_release_rbuf(struct issei_host_client *cl)
52*2207e8d4SAlexander Usyskin {
53*2207e8d4SAlexander Usyskin 	cl->read_data = NULL;
54*2207e8d4SAlexander Usyskin 	cl->read_data_size = 0;
55*2207e8d4SAlexander Usyskin }
56*2207e8d4SAlexander Usyskin 
57*2207e8d4SAlexander Usyskin static void __issei_cl_clean_rbuf(struct issei_host_client *cl)
58*2207e8d4SAlexander Usyskin {
59*2207e8d4SAlexander Usyskin 	kfree(cl->read_data);
60*2207e8d4SAlexander Usyskin 	__issei_cl_release_rbuf(cl);
61*2207e8d4SAlexander Usyskin }
62*2207e8d4SAlexander Usyskin 
63*2207e8d4SAlexander Usyskin static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct issei_host_client *cl)
64*2207e8d4SAlexander Usyskin {
65*2207e8d4SAlexander Usyskin 	struct issei_write_buf *wbuf, *next;
66*2207e8d4SAlexander Usyskin 
67*2207e8d4SAlexander Usyskin 	if (!cl->write_in_progress)
68*2207e8d4SAlexander Usyskin 		return;
69*2207e8d4SAlexander Usyskin 	list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) {
70*2207e8d4SAlexander Usyskin 		if (wbuf->cl == cl) {
71*2207e8d4SAlexander Usyskin 			__issei_cl_clean_wbuf(wbuf);
72*2207e8d4SAlexander Usyskin 			break;
73*2207e8d4SAlexander Usyskin 		}
74*2207e8d4SAlexander Usyskin 	}
75*2207e8d4SAlexander Usyskin 	cl->write_in_progress = false;
76*2207e8d4SAlexander Usyskin 	/* synchronized under host client mutex */
77*2207e8d4SAlexander Usyskin 	if (waitqueue_active(&cl->write_wait))
78*2207e8d4SAlexander Usyskin 		wake_up_interruptible(&cl->write_wait);
79*2207e8d4SAlexander Usyskin }
80*2207e8d4SAlexander Usyskin 
81*2207e8d4SAlexander Usyskin static struct issei_host_client *__issei_cl_by_id(struct issei_device *idev, u16 id)
82*2207e8d4SAlexander Usyskin {
83*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
84*2207e8d4SAlexander Usyskin 
85*2207e8d4SAlexander Usyskin 	list_for_each_entry(cl, &idev->host_client_list, list) {
86*2207e8d4SAlexander Usyskin 		if (cl->id == id)
87*2207e8d4SAlexander Usyskin 			return cl;
88*2207e8d4SAlexander Usyskin 	}
89*2207e8d4SAlexander Usyskin 	return NULL;
90*2207e8d4SAlexander Usyskin }
91*2207e8d4SAlexander Usyskin 
92*2207e8d4SAlexander Usyskin static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_client *cl)
93*2207e8d4SAlexander Usyskin {
94*2207e8d4SAlexander Usyskin 	if (cl->state == ISSEI_HOST_CL_STATE_DISCONNECTED)
95*2207e8d4SAlexander Usyskin 		return;
96*2207e8d4SAlexander Usyskin 
97*2207e8d4SAlexander Usyskin 	__issei_cl_clean_all_wbuf(idev, cl);
98*2207e8d4SAlexander Usyskin 
99*2207e8d4SAlexander Usyskin 	if (!WARN_ON(!cl->fw_cl)) {
100*2207e8d4SAlexander Usyskin 		issei_fw_cl_disconnect(cl->fw_cl);
101*2207e8d4SAlexander Usyskin 		cl->fw_cl = NULL;
102*2207e8d4SAlexander Usyskin 	}
103*2207e8d4SAlexander Usyskin 	cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
104*2207e8d4SAlexander Usyskin 
105*2207e8d4SAlexander Usyskin 	if (cl->read_data)
106*2207e8d4SAlexander Usyskin 		__issei_cl_clean_rbuf(cl);
107*2207e8d4SAlexander Usyskin 	/* synchronized under host client mutex */
108*2207e8d4SAlexander Usyskin 	if (waitqueue_active(&cl->read_wait))
109*2207e8d4SAlexander Usyskin 		wake_up_interruptible(&cl->read_wait);
110*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Disconnected\n");
111*2207e8d4SAlexander Usyskin }
112*2207e8d4SAlexander Usyskin 
113*2207e8d4SAlexander Usyskin static void __issei_cl_init(struct issei_host_client *cl, struct issei_device *idev,
114*2207e8d4SAlexander Usyskin 			    u16 id, struct file *fp)
115*2207e8d4SAlexander Usyskin {
116*2207e8d4SAlexander Usyskin 	INIT_LIST_HEAD(&cl->list);
117*2207e8d4SAlexander Usyskin 	cl->idev = idev;
118*2207e8d4SAlexander Usyskin 	cl->id = id;
119*2207e8d4SAlexander Usyskin 	cl->state = ISSEI_HOST_CL_STATE_DISCONNECTED;
120*2207e8d4SAlexander Usyskin 	cl->fp = fp;
121*2207e8d4SAlexander Usyskin 	init_waitqueue_head(&cl->write_wait);
122*2207e8d4SAlexander Usyskin 	init_waitqueue_head(&cl->read_wait);
123*2207e8d4SAlexander Usyskin 	__issei_cl_release_rbuf(cl);
124*2207e8d4SAlexander Usyskin }
125*2207e8d4SAlexander Usyskin 
126*2207e8d4SAlexander Usyskin /**
127*2207e8d4SAlexander Usyskin  * issei_cl_create - create the host client
128*2207e8d4SAlexander Usyskin  * @idev: issei device
129*2207e8d4SAlexander Usyskin  * @fp: file pointer to associate with host client
130*2207e8d4SAlexander Usyskin  *
131*2207e8d4SAlexander Usyskin  * Return: client pointer on success, ERR_PTR on error
132*2207e8d4SAlexander Usyskin  */
133*2207e8d4SAlexander Usyskin struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp)
134*2207e8d4SAlexander Usyskin {
135*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
136*2207e8d4SAlexander Usyskin 	u16 id;
137*2207e8d4SAlexander Usyskin 
138*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
139*2207e8d4SAlexander Usyskin 
140*2207e8d4SAlexander Usyskin 	if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) {
141*2207e8d4SAlexander Usyskin 		dev_err(&idev->dev, "Maximum open clients %d is reached.\n",
142*2207e8d4SAlexander Usyskin 			ISSEI_HOST_CLIENTS_MAX);
143*2207e8d4SAlexander Usyskin 		return ERR_PTR(-EMFILE);
144*2207e8d4SAlexander Usyskin 	}
145*2207e8d4SAlexander Usyskin 
146*2207e8d4SAlexander Usyskin 	do {
147*2207e8d4SAlexander Usyskin 		if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */
148*2207e8d4SAlexander Usyskin 			id = 1;
149*2207e8d4SAlexander Usyskin 		idev->host_client_last_id = id;
150*2207e8d4SAlexander Usyskin 		/* Not an endless loop as we have less clients then id's */
151*2207e8d4SAlexander Usyskin 	} while (__issei_cl_by_id(idev, id));
152*2207e8d4SAlexander Usyskin 
153*2207e8d4SAlexander Usyskin 	cl = kzalloc_obj(*cl);
154*2207e8d4SAlexander Usyskin 	if (!cl)
155*2207e8d4SAlexander Usyskin 		return ERR_PTR(-ENOMEM);
156*2207e8d4SAlexander Usyskin 
157*2207e8d4SAlexander Usyskin 	__issei_cl_init(cl, idev, id, fp);
158*2207e8d4SAlexander Usyskin 	list_add_tail(&cl->list, &idev->host_client_list);
159*2207e8d4SAlexander Usyskin 	idev->host_client_count++;
160*2207e8d4SAlexander Usyskin 
161*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Created\n");
162*2207e8d4SAlexander Usyskin 	return cl;
163*2207e8d4SAlexander Usyskin }
164*2207e8d4SAlexander Usyskin 
165*2207e8d4SAlexander Usyskin /**
166*2207e8d4SAlexander Usyskin  * issei_cl_remove - disconnect and free the host client
167*2207e8d4SAlexander Usyskin  * @cl: host client
168*2207e8d4SAlexander Usyskin  */
169*2207e8d4SAlexander Usyskin void issei_cl_remove(struct issei_host_client *cl)
170*2207e8d4SAlexander Usyskin {
171*2207e8d4SAlexander Usyskin 	struct issei_device *idev;
172*2207e8d4SAlexander Usyskin 
173*2207e8d4SAlexander Usyskin 	/* don't shout on error exit path */
174*2207e8d4SAlexander Usyskin 	if (!cl)
175*2207e8d4SAlexander Usyskin 		return;
176*2207e8d4SAlexander Usyskin 
177*2207e8d4SAlexander Usyskin 	idev = cl->idev;
178*2207e8d4SAlexander Usyskin 
179*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
180*2207e8d4SAlexander Usyskin 
181*2207e8d4SAlexander Usyskin 	idev->host_client_count--;
182*2207e8d4SAlexander Usyskin 	list_del(&cl->list);
183*2207e8d4SAlexander Usyskin 
184*2207e8d4SAlexander Usyskin 	__issei_cl_disconnect(idev, cl);
185*2207e8d4SAlexander Usyskin 
186*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Removed\n");
187*2207e8d4SAlexander Usyskin 	kfree(cl);
188*2207e8d4SAlexander Usyskin }
189*2207e8d4SAlexander Usyskin 
190*2207e8d4SAlexander Usyskin /**
191*2207e8d4SAlexander Usyskin  * issei_cl_connect - connect between FW and host client
192*2207e8d4SAlexander Usyskin  * @cl: host client
193*2207e8d4SAlexander Usyskin  * @uuid: FW client unique ID
194*2207e8d4SAlexander Usyskin  * @mtu: memory for FW client max message size
195*2207e8d4SAlexander Usyskin  * @ver: memory for FW client version
196*2207e8d4SAlexander Usyskin  * @flags: memory for FW client flags
197*2207e8d4SAlexander Usyskin  *
198*2207e8d4SAlexander Usyskin  * Search for firmware client by UUID and connect it to provided
199*2207e8d4SAlexander Usyskin  * host client, if not already connected to some client.
200*2207e8d4SAlexander Usyskin  *
201*2207e8d4SAlexander Usyskin  * Return: 0 on success, <0 on error
202*2207e8d4SAlexander Usyskin  */
203*2207e8d4SAlexander Usyskin int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32 *mtu, u8 *ver,
204*2207e8d4SAlexander Usyskin 		     u32 *flags)
205*2207e8d4SAlexander Usyskin {
206*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
207*2207e8d4SAlexander Usyskin 	struct issei_fw_client *fw_cl;
208*2207e8d4SAlexander Usyskin 	int ret;
209*2207e8d4SAlexander Usyskin 
210*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
211*2207e8d4SAlexander Usyskin 
212*2207e8d4SAlexander Usyskin 	if (cl->state == ISSEI_HOST_CL_STATE_CONNECTED) {
213*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "Already connected\n");
214*2207e8d4SAlexander Usyskin 		return -EISCONN;
215*2207e8d4SAlexander Usyskin 	}
216*2207e8d4SAlexander Usyskin 
217*2207e8d4SAlexander Usyskin 	fw_cl = issei_fw_cl_find_by_uuid(idev, uuid);
218*2207e8d4SAlexander Usyskin 	if (!fw_cl) {
219*2207e8d4SAlexander Usyskin 		cl_dbg(idev, cl, "FW client %pUb not found\n", uuid);
220*2207e8d4SAlexander Usyskin 		return -ENOTTY;
221*2207e8d4SAlexander Usyskin 	}
222*2207e8d4SAlexander Usyskin 
223*2207e8d4SAlexander Usyskin 	ret = issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on success */
224*2207e8d4SAlexander Usyskin 	kobject_put(&fw_cl->kobj);
225*2207e8d4SAlexander Usyskin 	if (ret) {
226*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "FW client is already connected ret = %d\n", ret);
227*2207e8d4SAlexander Usyskin 		return ret;
228*2207e8d4SAlexander Usyskin 	}
229*2207e8d4SAlexander Usyskin 
230*2207e8d4SAlexander Usyskin 	cl->fw_cl = fw_cl;
231*2207e8d4SAlexander Usyskin 	cl->state = ISSEI_HOST_CL_STATE_CONNECTED;
232*2207e8d4SAlexander Usyskin 
233*2207e8d4SAlexander Usyskin 	*mtu = fw_cl->mtu;
234*2207e8d4SAlexander Usyskin 	*ver = fw_cl->ver;
235*2207e8d4SAlexander Usyskin 	*flags = fw_cl->flags;
236*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Connected\n");
237*2207e8d4SAlexander Usyskin 	return 0;
238*2207e8d4SAlexander Usyskin }
239*2207e8d4SAlexander Usyskin 
240*2207e8d4SAlexander Usyskin /**
241*2207e8d4SAlexander Usyskin  * issei_cl_disconnect - disconnect between FW and host client
242*2207e8d4SAlexander Usyskin  * @cl: host client
243*2207e8d4SAlexander Usyskin  *
244*2207e8d4SAlexander Usyskin  * Return: 0 on success, -ENOTCONN if not connected
245*2207e8d4SAlexander Usyskin  */
246*2207e8d4SAlexander Usyskin int issei_cl_disconnect(struct issei_host_client *cl)
247*2207e8d4SAlexander Usyskin {
248*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
249*2207e8d4SAlexander Usyskin 
250*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
251*2207e8d4SAlexander Usyskin 
252*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
253*2207e8d4SAlexander Usyskin 		return -ENOTCONN;
254*2207e8d4SAlexander Usyskin 	__issei_cl_disconnect(idev, cl);
255*2207e8d4SAlexander Usyskin 	return 0;
256*2207e8d4SAlexander Usyskin }
257*2207e8d4SAlexander Usyskin 
258*2207e8d4SAlexander Usyskin /**
259*2207e8d4SAlexander Usyskin  * issei_cl_all_disconnect - disconnect all FW clients
260*2207e8d4SAlexander Usyskin  * @idev: issei device
261*2207e8d4SAlexander Usyskin  */
262*2207e8d4SAlexander Usyskin void issei_cl_all_disconnect(struct issei_device *idev)
263*2207e8d4SAlexander Usyskin {
264*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
265*2207e8d4SAlexander Usyskin 
266*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
267*2207e8d4SAlexander Usyskin 
268*2207e8d4SAlexander Usyskin 	list_for_each_entry(cl, &idev->host_client_list, list)
269*2207e8d4SAlexander Usyskin 		__issei_cl_disconnect(idev, cl);
270*2207e8d4SAlexander Usyskin }
271*2207e8d4SAlexander Usyskin 
272*2207e8d4SAlexander Usyskin /**
273*2207e8d4SAlexander Usyskin  * issei_cl_write - enqueue write request
274*2207e8d4SAlexander Usyskin  * @cl: host client
275*2207e8d4SAlexander Usyskin  * @buf: buffer to write
276*2207e8d4SAlexander Usyskin  * @buf_size: buffer size
277*2207e8d4SAlexander Usyskin  *
278*2207e8d4SAlexander Usyskin  * Add write request to the write queue and wakes working thread.
279*2207e8d4SAlexander Usyskin  * This call takes ownership of buf memory, if succeeded.
280*2207e8d4SAlexander Usyskin  *
281*2207e8d4SAlexander Usyskin  * Return: size of data on success, <0 on error
282*2207e8d4SAlexander Usyskin  */
283*2207e8d4SAlexander Usyskin ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t buf_size)
284*2207e8d4SAlexander Usyskin {
285*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
286*2207e8d4SAlexander Usyskin 	struct issei_write_buf *wbuf;
287*2207e8d4SAlexander Usyskin 
288*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
289*2207e8d4SAlexander Usyskin 
290*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
291*2207e8d4SAlexander Usyskin 		return -ENOTCONN;
292*2207e8d4SAlexander Usyskin 
293*2207e8d4SAlexander Usyskin 	if (cl->write_in_progress) {
294*2207e8d4SAlexander Usyskin 		cl_dbg(idev, cl, "Another write is in progress\n");
295*2207e8d4SAlexander Usyskin 		return -EAGAIN;
296*2207e8d4SAlexander Usyskin 	}
297*2207e8d4SAlexander Usyskin 
298*2207e8d4SAlexander Usyskin 	if (buf_size > cl->fw_cl->mtu) {
299*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
300*2207e8d4SAlexander Usyskin 		return -EFBIG;
301*2207e8d4SAlexander Usyskin 	}
302*2207e8d4SAlexander Usyskin 
303*2207e8d4SAlexander Usyskin 	wbuf = kmalloc_obj(*wbuf);
304*2207e8d4SAlexander Usyskin 	if (!wbuf)
305*2207e8d4SAlexander Usyskin 		return -ENOMEM;
306*2207e8d4SAlexander Usyskin 	wbuf->cl = cl;
307*2207e8d4SAlexander Usyskin 	wbuf->data = buf;
308*2207e8d4SAlexander Usyskin 	wbuf->data_size = buf_size;
309*2207e8d4SAlexander Usyskin 	list_add_tail(&wbuf->list, &idev->write_queue);
310*2207e8d4SAlexander Usyskin 	cl->write_in_progress = true;
311*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size);
312*2207e8d4SAlexander Usyskin 
313*2207e8d4SAlexander Usyskin 	issei_poke_process_thread(idev);
314*2207e8d4SAlexander Usyskin 
315*2207e8d4SAlexander Usyskin 	return buf_size;
316*2207e8d4SAlexander Usyskin }
317*2207e8d4SAlexander Usyskin 
318*2207e8d4SAlexander Usyskin /**
319*2207e8d4SAlexander Usyskin  * issei_cl_write_from_queue - writes first request from queue to firmware
320*2207e8d4SAlexander Usyskin  * @idev: issei device
321*2207e8d4SAlexander Usyskin  *
322*2207e8d4SAlexander Usyskin  * Tries to write first request from the write queue to firmware.
323*2207e8d4SAlexander Usyskin  * Releases buf memory, if succeeded.
324*2207e8d4SAlexander Usyskin  *
325*2207e8d4SAlexander Usyskin  * Return: 0 on success, <0 on error
326*2207e8d4SAlexander Usyskin  */
327*2207e8d4SAlexander Usyskin int issei_cl_write_from_queue(struct issei_device *idev)
328*2207e8d4SAlexander Usyskin {
329*2207e8d4SAlexander Usyskin 	struct issei_write_buf *wbuf;
330*2207e8d4SAlexander Usyskin 	struct issei_dma_data data;
331*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
332*2207e8d4SAlexander Usyskin 	int ret;
333*2207e8d4SAlexander Usyskin 
334*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
335*2207e8d4SAlexander Usyskin 
336*2207e8d4SAlexander Usyskin 	wbuf = list_first_entry_or_null(&idev->write_queue, struct issei_write_buf, list);
337*2207e8d4SAlexander Usyskin 	if (!wbuf)
338*2207e8d4SAlexander Usyskin 		return 0;
339*2207e8d4SAlexander Usyskin 
340*2207e8d4SAlexander Usyskin 	cl = wbuf->cl;
341*2207e8d4SAlexander Usyskin 
342*2207e8d4SAlexander Usyskin 	data.fw_id = cl->fw_cl->id;
343*2207e8d4SAlexander Usyskin 	data.host_id = cl->id;
344*2207e8d4SAlexander Usyskin 	data.flags = 0;
345*2207e8d4SAlexander Usyskin 	data.status = 0;
346*2207e8d4SAlexander Usyskin 	data.length = wbuf->data_size;
347*2207e8d4SAlexander Usyskin 	data.buf = (void *)wbuf->data;
348*2207e8d4SAlexander Usyskin 	ret = issei_dma_write(idev, &data);
349*2207e8d4SAlexander Usyskin 	if (ret == -EBUSY)
350*2207e8d4SAlexander Usyskin 		return 0;
351*2207e8d4SAlexander Usyskin 	if (ret == -EIO)
352*2207e8d4SAlexander Usyskin 		return ret;
353*2207e8d4SAlexander Usyskin 	if (ret >= 0)
354*2207e8d4SAlexander Usyskin 		idev->ops->irq_write_generate(idev);
355*2207e8d4SAlexander Usyskin 	cl->write_in_progress = false;
356*2207e8d4SAlexander Usyskin 	/* synchronized under host client mutex */
357*2207e8d4SAlexander Usyskin 	if (waitqueue_active(&cl->write_wait))
358*2207e8d4SAlexander Usyskin 		wake_up_interruptible(&cl->write_wait);
359*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size);
360*2207e8d4SAlexander Usyskin 	__issei_cl_clean_wbuf(wbuf);
361*2207e8d4SAlexander Usyskin 	return 0;
362*2207e8d4SAlexander Usyskin }
363*2207e8d4SAlexander Usyskin 
364*2207e8d4SAlexander Usyskin static struct issei_host_client *__issei_cl_read_buf_check(struct issei_device *idev, u16 fw_id,
365*2207e8d4SAlexander Usyskin 							   u16 host_id, size_t buf_size)
366*2207e8d4SAlexander Usyskin {
367*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
368*2207e8d4SAlexander Usyskin 
369*2207e8d4SAlexander Usyskin 	cl = __issei_cl_by_id(idev, host_id);
370*2207e8d4SAlexander Usyskin 	if (!cl) {
371*2207e8d4SAlexander Usyskin 		dev_dbg(&idev->dev, "No client %u\n", host_id);
372*2207e8d4SAlexander Usyskin 		return ERR_PTR(-ENOTTY);
373*2207e8d4SAlexander Usyskin 	}
374*2207e8d4SAlexander Usyskin 
375*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED) {
376*2207e8d4SAlexander Usyskin 		cl_dbg(idev, cl, "Not connected\n");
377*2207e8d4SAlexander Usyskin 		return ERR_PTR(-ENODEV);
378*2207e8d4SAlexander Usyskin 	}
379*2207e8d4SAlexander Usyskin 	if (cl->fw_cl->id != fw_id) {
380*2207e8d4SAlexander Usyskin 		cl_dbg(idev, cl, "Wrong firmware client %u ?= %u\n", cl->fw_cl->id, fw_id);
381*2207e8d4SAlexander Usyskin 		return ERR_PTR(-ENODEV);
382*2207e8d4SAlexander Usyskin 	}
383*2207e8d4SAlexander Usyskin 
384*2207e8d4SAlexander Usyskin 	if (buf_size > cl->fw_cl->mtu) {
385*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu);
386*2207e8d4SAlexander Usyskin 		__issei_cl_disconnect(idev, cl);
387*2207e8d4SAlexander Usyskin 		return NULL;
388*2207e8d4SAlexander Usyskin 	}
389*2207e8d4SAlexander Usyskin 
390*2207e8d4SAlexander Usyskin 	if (cl->read_data) {
391*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "Previous data was not read by user-space, disconnecting\n");
392*2207e8d4SAlexander Usyskin 		__issei_cl_disconnect(idev, cl);
393*2207e8d4SAlexander Usyskin 		return NULL;
394*2207e8d4SAlexander Usyskin 	}
395*2207e8d4SAlexander Usyskin 
396*2207e8d4SAlexander Usyskin 	return cl;
397*2207e8d4SAlexander Usyskin }
398*2207e8d4SAlexander Usyskin 
399*2207e8d4SAlexander Usyskin /**
400*2207e8d4SAlexander Usyskin  * issei_cl_read_buf - process data from firmware
401*2207e8d4SAlexander Usyskin  * @idev: issei device
402*2207e8d4SAlexander Usyskin  * @fw_id: firmware client id
403*2207e8d4SAlexander Usyskin  * @host_id: host client id
404*2207e8d4SAlexander Usyskin  * @buf: buffer with data
405*2207e8d4SAlexander Usyskin  * @buf_size: buffer size
406*2207e8d4SAlexander Usyskin  *
407*2207e8d4SAlexander Usyskin  * Puts data from firmware into provided host client storage.
408*2207e8d4SAlexander Usyskin  * Free buffer or consume it.
409*2207e8d4SAlexander Usyskin  *
410*2207e8d4SAlexander Usyskin  * Return: 0 on success or recoverable error, <0 on unrecoverable error
411*2207e8d4SAlexander Usyskin  */
412*2207e8d4SAlexander Usyskin int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u8 *buf, size_t buf_size)
413*2207e8d4SAlexander Usyskin {
414*2207e8d4SAlexander Usyskin 	struct issei_host_client *cl;
415*2207e8d4SAlexander Usyskin 
416*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
417*2207e8d4SAlexander Usyskin 
418*2207e8d4SAlexander Usyskin 	cl = __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size);
419*2207e8d4SAlexander Usyskin 	if (IS_ERR_OR_NULL(cl)) {
420*2207e8d4SAlexander Usyskin 		kfree(buf);
421*2207e8d4SAlexander Usyskin 		return PTR_ERR(cl);
422*2207e8d4SAlexander Usyskin 	}
423*2207e8d4SAlexander Usyskin 
424*2207e8d4SAlexander Usyskin 	cl->read_data = buf;
425*2207e8d4SAlexander Usyskin 	cl->read_data_size = buf_size;
426*2207e8d4SAlexander Usyskin 
427*2207e8d4SAlexander Usyskin 	/* synchronized under host client mutex */
428*2207e8d4SAlexander Usyskin 	if (waitqueue_active(&cl->read_wait))
429*2207e8d4SAlexander Usyskin 		wake_up_interruptible(&cl->read_wait);
430*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Read %zu bytes\n", buf_size);
431*2207e8d4SAlexander Usyskin 
432*2207e8d4SAlexander Usyskin 	return 0;
433*2207e8d4SAlexander Usyskin }
434*2207e8d4SAlexander Usyskin 
435*2207e8d4SAlexander Usyskin /**
436*2207e8d4SAlexander Usyskin  * issei_cl_read - read data from queue to provided buffer
437*2207e8d4SAlexander Usyskin  * @cl: host client
438*2207e8d4SAlexander Usyskin  * @buf: buffer to store data
439*2207e8d4SAlexander Usyskin  * @buf_size: buffer size
440*2207e8d4SAlexander Usyskin  *
441*2207e8d4SAlexander Usyskin  * Tries to take data buffer from client and return it to caller.
442*2207e8d4SAlexander Usyskin  * The caller receives ownership of the data buffer.
443*2207e8d4SAlexander Usyskin  *
444*2207e8d4SAlexander Usyskin  * Return: read data size on success, <0 on error
445*2207e8d4SAlexander Usyskin  */
446*2207e8d4SAlexander Usyskin ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_size)
447*2207e8d4SAlexander Usyskin {
448*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
449*2207e8d4SAlexander Usyskin 	size_t read_data_size;
450*2207e8d4SAlexander Usyskin 
451*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
452*2207e8d4SAlexander Usyskin 
453*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
454*2207e8d4SAlexander Usyskin 		return -ENOTCONN;
455*2207e8d4SAlexander Usyskin 
456*2207e8d4SAlexander Usyskin 	if (!cl->read_data)
457*2207e8d4SAlexander Usyskin 		return -ENOENT;
458*2207e8d4SAlexander Usyskin 
459*2207e8d4SAlexander Usyskin 	if (cl->read_data_size > buf_size) {
460*2207e8d4SAlexander Usyskin 		cl_err(idev, cl, "Buffer is too small %zu > %zu\n",
461*2207e8d4SAlexander Usyskin 		       cl->read_data_size, buf_size);
462*2207e8d4SAlexander Usyskin 		return -EFBIG;
463*2207e8d4SAlexander Usyskin 	}
464*2207e8d4SAlexander Usyskin 
465*2207e8d4SAlexander Usyskin 	*buf = cl->read_data;
466*2207e8d4SAlexander Usyskin 	read_data_size = cl->read_data_size;
467*2207e8d4SAlexander Usyskin 	cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size);
468*2207e8d4SAlexander Usyskin 	__issei_cl_release_rbuf(cl);
469*2207e8d4SAlexander Usyskin 	return read_data_size;
470*2207e8d4SAlexander Usyskin }
471*2207e8d4SAlexander Usyskin 
472*2207e8d4SAlexander Usyskin /**
473*2207e8d4SAlexander Usyskin  * issei_cl_check_read - check if client has data to read
474*2207e8d4SAlexander Usyskin  *
475*2207e8d4SAlexander Usyskin  * @cl: host client
476*2207e8d4SAlexander Usyskin  *
477*2207e8d4SAlexander Usyskin  * Return: 1 - data available, 0 - no data, < 0 on error
478*2207e8d4SAlexander Usyskin  */
479*2207e8d4SAlexander Usyskin int issei_cl_check_read(struct issei_host_client *cl)
480*2207e8d4SAlexander Usyskin {
481*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
482*2207e8d4SAlexander Usyskin 
483*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
484*2207e8d4SAlexander Usyskin 
485*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
486*2207e8d4SAlexander Usyskin 		return -ENOTCONN;
487*2207e8d4SAlexander Usyskin 	if (!cl->read_data)
488*2207e8d4SAlexander Usyskin 		return 0;
489*2207e8d4SAlexander Usyskin 	return 1;
490*2207e8d4SAlexander Usyskin }
491*2207e8d4SAlexander Usyskin 
492*2207e8d4SAlexander Usyskin /**
493*2207e8d4SAlexander Usyskin  * issei_cl_check_write - check if client is ready to write
494*2207e8d4SAlexander Usyskin  *
495*2207e8d4SAlexander Usyskin  * @cl: host client
496*2207e8d4SAlexander Usyskin  *
497*2207e8d4SAlexander Usyskin  * Return: 1 - can not write, 0 - can write, < 0 on error
498*2207e8d4SAlexander Usyskin  */
499*2207e8d4SAlexander Usyskin int issei_cl_check_write(struct issei_host_client *cl)
500*2207e8d4SAlexander Usyskin {
501*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
502*2207e8d4SAlexander Usyskin 
503*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
504*2207e8d4SAlexander Usyskin 
505*2207e8d4SAlexander Usyskin 	if (cl->state != ISSEI_HOST_CL_STATE_CONNECTED)
506*2207e8d4SAlexander Usyskin 		return -ENOTCONN;
507*2207e8d4SAlexander Usyskin 	if (cl->write_in_progress)
508*2207e8d4SAlexander Usyskin 		return 1;
509*2207e8d4SAlexander Usyskin 	return 0;
510*2207e8d4SAlexander Usyskin }
511*2207e8d4SAlexander Usyskin 
512*2207e8d4SAlexander Usyskin void issei_cl_clean_all_wbuf(struct issei_host_client *cl)
513*2207e8d4SAlexander Usyskin {
514*2207e8d4SAlexander Usyskin 	struct issei_device *idev = cl->idev;
515*2207e8d4SAlexander Usyskin 
516*2207e8d4SAlexander Usyskin 	guard(mutex)(&idev->client_lock);
517*2207e8d4SAlexander Usyskin 
518*2207e8d4SAlexander Usyskin 	__issei_cl_clean_all_wbuf(idev, cl);
519*2207e8d4SAlexander Usyskin }
520