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