18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 88c2ecf20Sopenharmony_ci#include <linux/wait.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/mei.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "mei_dev.h" 168c2ecf20Sopenharmony_ci#include "hbm.h" 178c2ecf20Sopenharmony_ci#include "client.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * mei_me_cl_init - initialize me client 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * @me_cl: me client 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_civoid mei_me_cl_init(struct mei_me_client *me_cl) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&me_cl->list); 278c2ecf20Sopenharmony_ci kref_init(&me_cl->refcnt); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * mei_me_cl_get - increases me client refcount 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * @me_cl: me client 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Return: me client or NULL 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistruct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci if (me_cl && kref_get_unless_zero(&me_cl->refcnt)) 428c2ecf20Sopenharmony_ci return me_cl; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return NULL; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * mei_me_cl_release - free me client 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * @ref: me_client refcount 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic void mei_me_cl_release(struct kref *ref) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct mei_me_client *me_cl = 578c2ecf20Sopenharmony_ci container_of(ref, struct mei_me_client, refcnt); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci kfree(me_cl); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * mei_me_cl_put - decrease me client refcount and free client if necessary 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * @me_cl: me client 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_civoid mei_me_cl_put(struct mei_me_client *me_cl) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (me_cl) 728c2ecf20Sopenharmony_ci kref_put(&me_cl->refcnt, mei_me_cl_release); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/** 768c2ecf20Sopenharmony_ci * __mei_me_cl_del - delete me client from the list and decrease 778c2ecf20Sopenharmony_ci * reference counter 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * @dev: mei device 808c2ecf20Sopenharmony_ci * @me_cl: me client 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Locking: dev->me_clients_rwsem 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci if (!me_cl) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci list_del_init(&me_cl->list); 908c2ecf20Sopenharmony_ci mei_me_cl_put(me_cl); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * mei_me_cl_del - delete me client from the list and decrease 958c2ecf20Sopenharmony_ci * reference counter 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * @dev: mei device 988c2ecf20Sopenharmony_ci * @me_cl: me client 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_civoid mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci down_write(&dev->me_clients_rwsem); 1038c2ecf20Sopenharmony_ci __mei_me_cl_del(dev, me_cl); 1048c2ecf20Sopenharmony_ci up_write(&dev->me_clients_rwsem); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/** 1088c2ecf20Sopenharmony_ci * mei_me_cl_add - add me client to the list 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * @dev: mei device 1118c2ecf20Sopenharmony_ci * @me_cl: me client 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_civoid mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci down_write(&dev->me_clients_rwsem); 1168c2ecf20Sopenharmony_ci list_add(&me_cl->list, &dev->me_clients); 1178c2ecf20Sopenharmony_ci up_write(&dev->me_clients_rwsem); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * __mei_me_cl_by_uuid - locate me client by uuid 1228c2ecf20Sopenharmony_ci * increases ref count 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * @dev: mei device 1258c2ecf20Sopenharmony_ci * @uuid: me client uuid 1268c2ecf20Sopenharmony_ci * 1278c2ecf20Sopenharmony_ci * Return: me client or NULL if not found 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Locking: dev->me_clients_rwsem 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_cistatic struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev, 1328c2ecf20Sopenharmony_ci const uuid_le *uuid) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 1358c2ecf20Sopenharmony_ci const uuid_le *pn; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci list_for_each_entry(me_cl, &dev->me_clients, list) { 1408c2ecf20Sopenharmony_ci pn = &me_cl->props.protocol_name; 1418c2ecf20Sopenharmony_ci if (uuid_le_cmp(*uuid, *pn) == 0) 1428c2ecf20Sopenharmony_ci return mei_me_cl_get(me_cl); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return NULL; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/** 1498c2ecf20Sopenharmony_ci * mei_me_cl_by_uuid - locate me client by uuid 1508c2ecf20Sopenharmony_ci * increases ref count 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * @dev: mei device 1538c2ecf20Sopenharmony_ci * @uuid: me client uuid 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Return: me client or NULL if not found 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Locking: dev->me_clients_rwsem 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistruct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, 1608c2ecf20Sopenharmony_ci const uuid_le *uuid) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci down_read(&dev->me_clients_rwsem); 1658c2ecf20Sopenharmony_ci me_cl = __mei_me_cl_by_uuid(dev, uuid); 1668c2ecf20Sopenharmony_ci up_read(&dev->me_clients_rwsem); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return me_cl; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/** 1728c2ecf20Sopenharmony_ci * mei_me_cl_by_id - locate me client by client id 1738c2ecf20Sopenharmony_ci * increases ref count 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * @dev: the device structure 1768c2ecf20Sopenharmony_ci * @client_id: me client id 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Return: me client or NULL if not found 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Locking: dev->me_clients_rwsem 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistruct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci struct mei_me_client *__me_cl, *me_cl = NULL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci down_read(&dev->me_clients_rwsem); 1888c2ecf20Sopenharmony_ci list_for_each_entry(__me_cl, &dev->me_clients, list) { 1898c2ecf20Sopenharmony_ci if (__me_cl->client_id == client_id) { 1908c2ecf20Sopenharmony_ci me_cl = mei_me_cl_get(__me_cl); 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci up_read(&dev->me_clients_rwsem); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return me_cl; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/** 2008c2ecf20Sopenharmony_ci * __mei_me_cl_by_uuid_id - locate me client by client id and uuid 2018c2ecf20Sopenharmony_ci * increases ref count 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * @dev: the device structure 2048c2ecf20Sopenharmony_ci * @uuid: me client uuid 2058c2ecf20Sopenharmony_ci * @client_id: me client id 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * Return: me client or null if not found 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Locking: dev->me_clients_rwsem 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev, 2128c2ecf20Sopenharmony_ci const uuid_le *uuid, u8 client_id) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 2158c2ecf20Sopenharmony_ci const uuid_le *pn; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci list_for_each_entry(me_cl, &dev->me_clients, list) { 2208c2ecf20Sopenharmony_ci pn = &me_cl->props.protocol_name; 2218c2ecf20Sopenharmony_ci if (uuid_le_cmp(*uuid, *pn) == 0 && 2228c2ecf20Sopenharmony_ci me_cl->client_id == client_id) 2238c2ecf20Sopenharmony_ci return mei_me_cl_get(me_cl); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return NULL; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/** 2318c2ecf20Sopenharmony_ci * mei_me_cl_by_uuid_id - locate me client by client id and uuid 2328c2ecf20Sopenharmony_ci * increases ref count 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * @dev: the device structure 2358c2ecf20Sopenharmony_ci * @uuid: me client uuid 2368c2ecf20Sopenharmony_ci * @client_id: me client id 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * Return: me client or null if not found 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistruct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, 2418c2ecf20Sopenharmony_ci const uuid_le *uuid, u8 client_id) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci down_read(&dev->me_clients_rwsem); 2468c2ecf20Sopenharmony_ci me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id); 2478c2ecf20Sopenharmony_ci up_read(&dev->me_clients_rwsem); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return me_cl; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/** 2538c2ecf20Sopenharmony_ci * mei_me_cl_rm_by_uuid - remove all me clients matching uuid 2548c2ecf20Sopenharmony_ci * 2558c2ecf20Sopenharmony_ci * @dev: the device structure 2568c2ecf20Sopenharmony_ci * @uuid: me client uuid 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_civoid mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "remove %pUl\n", uuid); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci down_write(&dev->me_clients_rwsem); 2678c2ecf20Sopenharmony_ci me_cl = __mei_me_cl_by_uuid(dev, uuid); 2688c2ecf20Sopenharmony_ci __mei_me_cl_del(dev, me_cl); 2698c2ecf20Sopenharmony_ci mei_me_cl_put(me_cl); 2708c2ecf20Sopenharmony_ci up_write(&dev->me_clients_rwsem); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/** 2748c2ecf20Sopenharmony_ci * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * @dev: the device structure 2778c2ecf20Sopenharmony_ci * @uuid: me client uuid 2788c2ecf20Sopenharmony_ci * @id: me client id 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_civoid mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct mei_me_client *me_cl; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci down_write(&dev->me_clients_rwsem); 2898c2ecf20Sopenharmony_ci me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id); 2908c2ecf20Sopenharmony_ci __mei_me_cl_del(dev, me_cl); 2918c2ecf20Sopenharmony_ci mei_me_cl_put(me_cl); 2928c2ecf20Sopenharmony_ci up_write(&dev->me_clients_rwsem); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/** 2968c2ecf20Sopenharmony_ci * mei_me_cl_rm_all - remove all me clients 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * @dev: the device structure 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid mei_me_cl_rm_all(struct mei_device *dev) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mei_me_client *me_cl, *next; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci down_write(&dev->me_clients_rwsem); 3078c2ecf20Sopenharmony_ci list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) 3088c2ecf20Sopenharmony_ci __mei_me_cl_del(dev, me_cl); 3098c2ecf20Sopenharmony_ci up_write(&dev->me_clients_rwsem); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * mei_io_cb_free - free mei_cb_private related memory 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * @cb: mei callback struct 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_civoid mei_io_cb_free(struct mei_cl_cb *cb) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci if (cb == NULL) 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci list_del(&cb->list); 3238c2ecf20Sopenharmony_ci kfree(cb->buf.data); 3248c2ecf20Sopenharmony_ci kfree(cb); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/** 3288c2ecf20Sopenharmony_ci * mei_tx_cb_queue - queue tx callback 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * @cb: mei callback struct 3338c2ecf20Sopenharmony_ci * @head: an instance of list to queue on 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb, 3368c2ecf20Sopenharmony_ci struct list_head *head) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci list_add_tail(&cb->list, head); 3398c2ecf20Sopenharmony_ci cb->cl->tx_cb_queued++; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/** 3438c2ecf20Sopenharmony_ci * mei_tx_cb_dequeue - dequeue tx callback 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * @cb: mei callback struct to dequeue and free 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci if (!WARN_ON(cb->cl->tx_cb_queued == 0)) 3528c2ecf20Sopenharmony_ci cb->cl->tx_cb_queued--; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/** 3588c2ecf20Sopenharmony_ci * mei_cl_set_read_by_fp - set pending_read flag to vtag struct for given fp 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * @cl: mei client 3638c2ecf20Sopenharmony_ci * @fp: pointer to file structure 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic void mei_cl_set_read_by_fp(const struct mei_cl *cl, 3668c2ecf20Sopenharmony_ci const struct file *fp) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct mei_cl_vtag *cl_vtag; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci list_for_each_entry(cl_vtag, &cl->vtag_map, list) { 3718c2ecf20Sopenharmony_ci if (cl_vtag->fp == fp) { 3728c2ecf20Sopenharmony_ci cl_vtag->pending_read = true; 3738c2ecf20Sopenharmony_ci return; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/** 3798c2ecf20Sopenharmony_ci * mei_io_cb_init - allocate and initialize io callback 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * @cl: mei client 3828c2ecf20Sopenharmony_ci * @type: operation type 3838c2ecf20Sopenharmony_ci * @fp: pointer to file structure 3848c2ecf20Sopenharmony_ci * 3858c2ecf20Sopenharmony_ci * Return: mei_cl_cb pointer or NULL; 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_cistatic struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, 3888c2ecf20Sopenharmony_ci enum mei_cb_file_ops type, 3898c2ecf20Sopenharmony_ci const struct file *fp) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci cb = kzalloc(sizeof(*cb), GFP_KERNEL); 3948c2ecf20Sopenharmony_ci if (!cb) 3958c2ecf20Sopenharmony_ci return NULL; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cb->list); 3988c2ecf20Sopenharmony_ci cb->fp = fp; 3998c2ecf20Sopenharmony_ci cb->cl = cl; 4008c2ecf20Sopenharmony_ci cb->buf_idx = 0; 4018c2ecf20Sopenharmony_ci cb->fop_type = type; 4028c2ecf20Sopenharmony_ci cb->vtag = 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return cb; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * mei_io_list_flush_cl - removes cbs belonging to the cl. 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * @head: an instance of our list structure 4118c2ecf20Sopenharmony_ci * @cl: host client 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_cistatic void mei_io_list_flush_cl(struct list_head *head, 4148c2ecf20Sopenharmony_ci const struct mei_cl *cl) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct mei_cl_cb *cb, *next; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, head, list) { 4198c2ecf20Sopenharmony_ci if (cl == cb->cl) { 4208c2ecf20Sopenharmony_ci list_del_init(&cb->list); 4218c2ecf20Sopenharmony_ci if (cb->fop_type == MEI_FOP_READ) 4228c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/** 4288c2ecf20Sopenharmony_ci * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * @head: An instance of our list structure 4318c2ecf20Sopenharmony_ci * @cl: host client 4328c2ecf20Sopenharmony_ci * @fp: file pointer (matching cb file object), may be NULL 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_cistatic void mei_io_tx_list_free_cl(struct list_head *head, 4358c2ecf20Sopenharmony_ci const struct mei_cl *cl, 4368c2ecf20Sopenharmony_ci const struct file *fp) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct mei_cl_cb *cb, *next; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, head, list) { 4418c2ecf20Sopenharmony_ci if (cl == cb->cl && (!fp || fp == cb->fp)) 4428c2ecf20Sopenharmony_ci mei_tx_cb_dequeue(cb); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/** 4478c2ecf20Sopenharmony_ci * mei_io_list_free_fp - free cb from a list that matches file pointer 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * @head: io list 4508c2ecf20Sopenharmony_ci * @fp: file pointer (matching cb file object), may be NULL 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_cistatic void mei_io_list_free_fp(struct list_head *head, const struct file *fp) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct mei_cl_cb *cb, *next; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci list_for_each_entry_safe(cb, next, head, list) 4578c2ecf20Sopenharmony_ci if (!fp || fp == cb->fp) 4588c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/** 4628c2ecf20Sopenharmony_ci * mei_cl_free_pending - free pending cb 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * @cl: host client 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_cistatic void mei_cl_free_pending(struct mei_cl *cl) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); 4718c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/** 4758c2ecf20Sopenharmony_ci * mei_cl_alloc_cb - a convenient wrapper for allocating read cb 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * @cl: host client 4788c2ecf20Sopenharmony_ci * @length: size of the buffer 4798c2ecf20Sopenharmony_ci * @fop_type: operation type 4808c2ecf20Sopenharmony_ci * @fp: associated file pointer (might be NULL) 4818c2ecf20Sopenharmony_ci * 4828c2ecf20Sopenharmony_ci * Return: cb on success and NULL on failure 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistruct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, 4858c2ecf20Sopenharmony_ci enum mei_cb_file_ops fop_type, 4868c2ecf20Sopenharmony_ci const struct file *fp) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci cb = mei_io_cb_init(cl, fop_type, fp); 4918c2ecf20Sopenharmony_ci if (!cb) 4928c2ecf20Sopenharmony_ci return NULL; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (length == 0) 4958c2ecf20Sopenharmony_ci return cb; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci cb->buf.data = kmalloc(roundup(length, MEI_SLOT_SIZE), GFP_KERNEL); 4988c2ecf20Sopenharmony_ci if (!cb->buf.data) { 4998c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 5008c2ecf20Sopenharmony_ci return NULL; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci cb->buf.size = length; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return cb; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci/** 5088c2ecf20Sopenharmony_ci * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating 5098c2ecf20Sopenharmony_ci * and enqueuing of the control commands cb 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * @cl: host client 5128c2ecf20Sopenharmony_ci * @length: size of the buffer 5138c2ecf20Sopenharmony_ci * @fop_type: operation type 5148c2ecf20Sopenharmony_ci * @fp: associated file pointer (might be NULL) 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * Return: cb on success and NULL on failure 5178c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_cistruct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, 5208c2ecf20Sopenharmony_ci enum mei_cb_file_ops fop_type, 5218c2ecf20Sopenharmony_ci const struct file *fp) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* for RX always allocate at least client's mtu */ 5268c2ecf20Sopenharmony_ci if (length) 5278c2ecf20Sopenharmony_ci length = max_t(size_t, length, mei_cl_mtu(cl)); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci cb = mei_cl_alloc_cb(cl, length, fop_type, fp); 5308c2ecf20Sopenharmony_ci if (!cb) 5318c2ecf20Sopenharmony_ci return NULL; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci list_add_tail(&cb->list, &cl->dev->ctrl_wr_list); 5348c2ecf20Sopenharmony_ci return cb; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/** 5388c2ecf20Sopenharmony_ci * mei_cl_read_cb - find this cl's callback in the read list 5398c2ecf20Sopenharmony_ci * for a specific file 5408c2ecf20Sopenharmony_ci * 5418c2ecf20Sopenharmony_ci * @cl: host client 5428c2ecf20Sopenharmony_ci * @fp: file pointer (matching cb file object), may be NULL 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * Return: cb on success, NULL if cb is not found 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_cistruct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 5498c2ecf20Sopenharmony_ci struct mei_cl_cb *ret_cb = NULL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci spin_lock(&cl->rd_completed_lock); 5528c2ecf20Sopenharmony_ci list_for_each_entry(cb, &cl->rd_completed, list) 5538c2ecf20Sopenharmony_ci if (!fp || fp == cb->fp) { 5548c2ecf20Sopenharmony_ci ret_cb = cb; 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci spin_unlock(&cl->rd_completed_lock); 5588c2ecf20Sopenharmony_ci return ret_cb; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * mei_cl_flush_queues - flushes queue lists belonging to cl. 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * @cl: host client 5658c2ecf20Sopenharmony_ci * @fp: file pointer (matching cb file object), may be NULL 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ciint mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct mei_device *dev; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 5748c2ecf20Sopenharmony_ci return -EINVAL; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci dev = cl->dev; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 5798c2ecf20Sopenharmony_ci mei_io_tx_list_free_cl(&cl->dev->write_list, cl, fp); 5808c2ecf20Sopenharmony_ci mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl, fp); 5818c2ecf20Sopenharmony_ci /* free pending and control cb only in final flush */ 5828c2ecf20Sopenharmony_ci if (!fp) { 5838c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); 5848c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); 5858c2ecf20Sopenharmony_ci mei_cl_free_pending(cl); 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci spin_lock(&cl->rd_completed_lock); 5888c2ecf20Sopenharmony_ci mei_io_list_free_fp(&cl->rd_completed, fp); 5898c2ecf20Sopenharmony_ci spin_unlock(&cl->rd_completed_lock); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci/** 5958c2ecf20Sopenharmony_ci * mei_cl_init - initializes cl. 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * @cl: host client to be initialized 5988c2ecf20Sopenharmony_ci * @dev: mei device 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_cistatic void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci memset(cl, 0, sizeof(*cl)); 6038c2ecf20Sopenharmony_ci init_waitqueue_head(&cl->wait); 6048c2ecf20Sopenharmony_ci init_waitqueue_head(&cl->rx_wait); 6058c2ecf20Sopenharmony_ci init_waitqueue_head(&cl->tx_wait); 6068c2ecf20Sopenharmony_ci init_waitqueue_head(&cl->ev_wait); 6078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cl->vtag_map); 6088c2ecf20Sopenharmony_ci spin_lock_init(&cl->rd_completed_lock); 6098c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cl->rd_completed); 6108c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cl->rd_pending); 6118c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cl->link); 6128c2ecf20Sopenharmony_ci cl->writing_state = MEI_IDLE; 6138c2ecf20Sopenharmony_ci cl->state = MEI_FILE_UNINITIALIZED; 6148c2ecf20Sopenharmony_ci cl->dev = dev; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/** 6188c2ecf20Sopenharmony_ci * mei_cl_allocate - allocates cl structure and sets it up. 6198c2ecf20Sopenharmony_ci * 6208c2ecf20Sopenharmony_ci * @dev: mei device 6218c2ecf20Sopenharmony_ci * Return: The allocated file or NULL on failure 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistruct mei_cl *mei_cl_allocate(struct mei_device *dev) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct mei_cl *cl; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci cl = kmalloc(sizeof(*cl), GFP_KERNEL); 6288c2ecf20Sopenharmony_ci if (!cl) 6298c2ecf20Sopenharmony_ci return NULL; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci mei_cl_init(cl, dev); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return cl; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci/** 6378c2ecf20Sopenharmony_ci * mei_cl_link - allocate host id in the host map 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * @cl: host client 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * Return: 0 on success 6428c2ecf20Sopenharmony_ci * -EINVAL on incorrect values 6438c2ecf20Sopenharmony_ci * -EMFILE if open count exceeded. 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ciint mei_cl_link(struct mei_cl *cl) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct mei_device *dev; 6488c2ecf20Sopenharmony_ci int id; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 6518c2ecf20Sopenharmony_ci return -EINVAL; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci dev = cl->dev; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); 6568c2ecf20Sopenharmony_ci if (id >= MEI_CLIENTS_MAX) { 6578c2ecf20Sopenharmony_ci dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 6588c2ecf20Sopenharmony_ci return -EMFILE; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6628c2ecf20Sopenharmony_ci dev_err(dev->dev, "open_handle_count exceeded %d", 6638c2ecf20Sopenharmony_ci MEI_MAX_OPEN_HANDLE_COUNT); 6648c2ecf20Sopenharmony_ci return -EMFILE; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci dev->open_handle_count++; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci cl->host_client_id = id; 6708c2ecf20Sopenharmony_ci list_add_tail(&cl->link, &dev->file_list); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci set_bit(id, dev->host_clients_map); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci cl->state = MEI_FILE_INITIALIZING; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "link cl\n"); 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci/** 6818c2ecf20Sopenharmony_ci * mei_cl_unlink - remove host client from the list 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * @cl: host client 6848c2ecf20Sopenharmony_ci * 6858c2ecf20Sopenharmony_ci * Return: always 0 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ciint mei_cl_unlink(struct mei_cl *cl) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct mei_device *dev; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* don't shout on error exit path */ 6928c2ecf20Sopenharmony_ci if (!cl) 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (WARN_ON(!cl->dev)) 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci dev = cl->dev; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "unlink client"); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (dev->open_handle_count > 0) 7038c2ecf20Sopenharmony_ci dev->open_handle_count--; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* never clear the 0 bit */ 7068c2ecf20Sopenharmony_ci if (cl->host_client_id) 7078c2ecf20Sopenharmony_ci clear_bit(cl->host_client_id, dev->host_clients_map); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci list_del_init(&cl->link); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci cl->state = MEI_FILE_UNINITIALIZED; 7128c2ecf20Sopenharmony_ci cl->writing_state = MEI_IDLE; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci WARN_ON(!list_empty(&cl->rd_completed) || 7158c2ecf20Sopenharmony_ci !list_empty(&cl->rd_pending) || 7168c2ecf20Sopenharmony_ci !list_empty(&cl->link)); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_civoid mei_host_client_init(struct mei_device *dev) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci mei_set_devstate(dev, MEI_DEV_ENABLED); 7248c2ecf20Sopenharmony_ci dev->reset_count = 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci schedule_work(&dev->bus_rescan_work); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 7298c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "rpm: autosuspend\n"); 7308c2ecf20Sopenharmony_ci pm_request_autosuspend(dev->dev); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/** 7348c2ecf20Sopenharmony_ci * mei_hbuf_acquire - try to acquire host buffer 7358c2ecf20Sopenharmony_ci * 7368c2ecf20Sopenharmony_ci * @dev: the device structure 7378c2ecf20Sopenharmony_ci * Return: true if host buffer was acquired 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_cibool mei_hbuf_acquire(struct mei_device *dev) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci if (mei_pg_state(dev) == MEI_PG_ON || 7428c2ecf20Sopenharmony_ci mei_pg_in_transition(dev)) { 7438c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "device is in pg\n"); 7448c2ecf20Sopenharmony_ci return false; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!dev->hbuf_is_ready) { 7488c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "hbuf is not ready\n"); 7498c2ecf20Sopenharmony_ci return false; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci dev->hbuf_is_ready = false; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return true; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci/** 7588c2ecf20Sopenharmony_ci * mei_cl_wake_all - wake up readers, writers and event waiters so 7598c2ecf20Sopenharmony_ci * they can be interrupted 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * @cl: host client 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_cistatic void mei_cl_wake_all(struct mei_cl *cl) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* synchronized under device mutex */ 7688c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->rx_wait)) { 7698c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Waking up reading client!\n"); 7708c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->rx_wait); 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci /* synchronized under device mutex */ 7738c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->tx_wait)) { 7748c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Waking up writing client!\n"); 7758c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->tx_wait); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci /* synchronized under device mutex */ 7788c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->ev_wait)) { 7798c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); 7808c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->ev_wait); 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci /* synchronized under device mutex */ 7838c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->wait)) { 7848c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Waking up ctrl write clients!\n"); 7858c2ecf20Sopenharmony_ci wake_up(&cl->wait); 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * mei_cl_set_disconnected - set disconnected state and clear 7918c2ecf20Sopenharmony_ci * associated states and resources 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * @cl: host client 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_cistatic void mei_cl_set_disconnected(struct mei_cl *cl) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (cl->state == MEI_FILE_DISCONNECTED || 8008c2ecf20Sopenharmony_ci cl->state <= MEI_FILE_INITIALIZING) 8018c2ecf20Sopenharmony_ci return; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci cl->state = MEI_FILE_DISCONNECTED; 8048c2ecf20Sopenharmony_ci mei_io_tx_list_free_cl(&dev->write_list, cl, NULL); 8058c2ecf20Sopenharmony_ci mei_io_tx_list_free_cl(&dev->write_waiting_list, cl, NULL); 8068c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); 8078c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); 8088c2ecf20Sopenharmony_ci mei_cl_wake_all(cl); 8098c2ecf20Sopenharmony_ci cl->rx_flow_ctrl_creds = 0; 8108c2ecf20Sopenharmony_ci cl->tx_flow_ctrl_creds = 0; 8118c2ecf20Sopenharmony_ci cl->timer_count = 0; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (!cl->me_cl) 8148c2ecf20Sopenharmony_ci return; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!WARN_ON(cl->me_cl->connect_count == 0)) 8178c2ecf20Sopenharmony_ci cl->me_cl->connect_count--; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (cl->me_cl->connect_count == 0) 8208c2ecf20Sopenharmony_ci cl->me_cl->tx_flow_ctrl_creds = 0; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci mei_me_cl_put(cl->me_cl); 8238c2ecf20Sopenharmony_ci cl->me_cl = NULL; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci if (!mei_me_cl_get(me_cl)) 8298c2ecf20Sopenharmony_ci return -ENOENT; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* only one connection is allowed for fixed address clients */ 8328c2ecf20Sopenharmony_ci if (me_cl->props.fixed_address) { 8338c2ecf20Sopenharmony_ci if (me_cl->connect_count) { 8348c2ecf20Sopenharmony_ci mei_me_cl_put(me_cl); 8358c2ecf20Sopenharmony_ci return -EBUSY; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci cl->me_cl = me_cl; 8408c2ecf20Sopenharmony_ci cl->state = MEI_FILE_CONNECTING; 8418c2ecf20Sopenharmony_ci cl->me_cl->connect_count++; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci return 0; 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci/* 8478c2ecf20Sopenharmony_ci * mei_cl_send_disconnect - send disconnect request 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * @cl: host client 8508c2ecf20Sopenharmony_ci * @cb: callback block 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct mei_device *dev; 8578c2ecf20Sopenharmony_ci int ret; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci dev = cl->dev; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ret = mei_hbm_cl_disconnect_req(dev, cl); 8628c2ecf20Sopenharmony_ci cl->status = ret; 8638c2ecf20Sopenharmony_ci if (ret) { 8648c2ecf20Sopenharmony_ci cl->state = MEI_FILE_DISCONNECT_REPLY; 8658c2ecf20Sopenharmony_ci return ret; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &dev->ctrl_rd_list); 8698c2ecf20Sopenharmony_ci cl->timer_count = MEI_CONNECT_TIMEOUT; 8708c2ecf20Sopenharmony_ci mei_schedule_stall_timer(dev); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/** 8768c2ecf20Sopenharmony_ci * mei_cl_irq_disconnect - processes close related operation from 8778c2ecf20Sopenharmony_ci * interrupt thread context - send disconnect request 8788c2ecf20Sopenharmony_ci * 8798c2ecf20Sopenharmony_ci * @cl: client 8808c2ecf20Sopenharmony_ci * @cb: callback block. 8818c2ecf20Sopenharmony_ci * @cmpl_list: complete list. 8828c2ecf20Sopenharmony_ci * 8838c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ciint mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 8868c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 8898c2ecf20Sopenharmony_ci u32 msg_slots; 8908c2ecf20Sopenharmony_ci int slots; 8918c2ecf20Sopenharmony_ci int ret; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request)); 8948c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 8958c2ecf20Sopenharmony_ci if (slots < 0) 8968c2ecf20Sopenharmony_ci return -EOVERFLOW; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if ((u32)slots < msg_slots) 8998c2ecf20Sopenharmony_ci return -EMSGSIZE; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci ret = mei_cl_send_disconnect(cl, cb); 9028c2ecf20Sopenharmony_ci if (ret) 9038c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/** 9098c2ecf20Sopenharmony_ci * __mei_cl_disconnect - disconnect host client from the me one 9108c2ecf20Sopenharmony_ci * internal function runtime pm has to be already acquired 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * @cl: host client 9138c2ecf20Sopenharmony_ci * 9148c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_cistatic int __mei_cl_disconnect(struct mei_cl *cl) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci struct mei_device *dev; 9198c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 9208c2ecf20Sopenharmony_ci int rets; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci dev = cl->dev; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci cl->state = MEI_FILE_DISCONNECTING; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL); 9278c2ecf20Sopenharmony_ci if (!cb) { 9288c2ecf20Sopenharmony_ci rets = -ENOMEM; 9298c2ecf20Sopenharmony_ci goto out; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (mei_hbuf_acquire(dev)) { 9338c2ecf20Sopenharmony_ci rets = mei_cl_send_disconnect(cl, cb); 9348c2ecf20Sopenharmony_ci if (rets) { 9358c2ecf20Sopenharmony_ci cl_err(dev, cl, "failed to disconnect.\n"); 9368c2ecf20Sopenharmony_ci goto out; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 9418c2ecf20Sopenharmony_ci wait_event_timeout(cl->wait, 9428c2ecf20Sopenharmony_ci cl->state == MEI_FILE_DISCONNECT_REPLY || 9438c2ecf20Sopenharmony_ci cl->state == MEI_FILE_DISCONNECTED, 9448c2ecf20Sopenharmony_ci mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 9458c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci rets = cl->status; 9488c2ecf20Sopenharmony_ci if (cl->state != MEI_FILE_DISCONNECT_REPLY && 9498c2ecf20Sopenharmony_ci cl->state != MEI_FILE_DISCONNECTED) { 9508c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 9518c2ecf20Sopenharmony_ci rets = -ETIME; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ciout: 9558c2ecf20Sopenharmony_ci /* we disconnect also on error */ 9568c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 9578c2ecf20Sopenharmony_ci if (!rets) 9588c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 9618c2ecf20Sopenharmony_ci return rets; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/** 9658c2ecf20Sopenharmony_ci * mei_cl_disconnect - disconnect host client from the me one 9668c2ecf20Sopenharmony_ci * 9678c2ecf20Sopenharmony_ci * @cl: host client 9688c2ecf20Sopenharmony_ci * 9698c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_ciint mei_cl_disconnect(struct mei_cl *cl) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct mei_device *dev; 9768c2ecf20Sopenharmony_ci int rets; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 9798c2ecf20Sopenharmony_ci return -ENODEV; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci dev = cl->dev; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "disconnecting"); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (mei_cl_is_fixed_address(cl)) { 9898c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (dev->dev_state == MEI_DEV_POWER_DOWN) { 9948c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Device is powering down, don't bother with disconnection\n"); 9958c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 9968c2ecf20Sopenharmony_ci return 0; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci rets = pm_runtime_get(dev->dev); 10008c2ecf20Sopenharmony_ci if (rets < 0 && rets != -EINPROGRESS) { 10018c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 10028c2ecf20Sopenharmony_ci cl_err(dev, cl, "rpm: get failed %d\n", rets); 10038c2ecf20Sopenharmony_ci return rets; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci rets = __mei_cl_disconnect(cl); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "rpm: autosuspend\n"); 10098c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 10108c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci return rets; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/** 10178c2ecf20Sopenharmony_ci * mei_cl_is_other_connecting - checks if other 10188c2ecf20Sopenharmony_ci * client with the same me client id is connecting 10198c2ecf20Sopenharmony_ci * 10208c2ecf20Sopenharmony_ci * @cl: private data of the file object 10218c2ecf20Sopenharmony_ci * 10228c2ecf20Sopenharmony_ci * Return: true if other client is connected, false - otherwise. 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_cistatic bool mei_cl_is_other_connecting(struct mei_cl *cl) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct mei_device *dev; 10278c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci dev = cl->dev; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci list_for_each_entry(cb, &dev->ctrl_rd_list, list) { 10328c2ecf20Sopenharmony_ci if (cb->fop_type == MEI_FOP_CONNECT && 10338c2ecf20Sopenharmony_ci mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) 10348c2ecf20Sopenharmony_ci return true; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci return false; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci/** 10418c2ecf20Sopenharmony_ci * mei_cl_send_connect - send connect request 10428c2ecf20Sopenharmony_ci * 10438c2ecf20Sopenharmony_ci * @cl: host client 10448c2ecf20Sopenharmony_ci * @cb: callback block 10458c2ecf20Sopenharmony_ci * 10468c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 10478c2ecf20Sopenharmony_ci */ 10488c2ecf20Sopenharmony_cistatic int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci struct mei_device *dev; 10518c2ecf20Sopenharmony_ci int ret; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci dev = cl->dev; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = mei_hbm_cl_connect_req(dev, cl); 10568c2ecf20Sopenharmony_ci cl->status = ret; 10578c2ecf20Sopenharmony_ci if (ret) { 10588c2ecf20Sopenharmony_ci cl->state = MEI_FILE_DISCONNECT_REPLY; 10598c2ecf20Sopenharmony_ci return ret; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &dev->ctrl_rd_list); 10638c2ecf20Sopenharmony_ci cl->timer_count = MEI_CONNECT_TIMEOUT; 10648c2ecf20Sopenharmony_ci mei_schedule_stall_timer(dev); 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/** 10698c2ecf20Sopenharmony_ci * mei_cl_irq_connect - send connect request in irq_thread context 10708c2ecf20Sopenharmony_ci * 10718c2ecf20Sopenharmony_ci * @cl: host client 10728c2ecf20Sopenharmony_ci * @cb: callback block 10738c2ecf20Sopenharmony_ci * @cmpl_list: complete list 10748c2ecf20Sopenharmony_ci * 10758c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise, error. 10768c2ecf20Sopenharmony_ci */ 10778c2ecf20Sopenharmony_ciint mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, 10788c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 10818c2ecf20Sopenharmony_ci u32 msg_slots; 10828c2ecf20Sopenharmony_ci int slots; 10838c2ecf20Sopenharmony_ci int rets; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (mei_cl_is_other_connecting(cl)) 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request)); 10898c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 10908c2ecf20Sopenharmony_ci if (slots < 0) 10918c2ecf20Sopenharmony_ci return -EOVERFLOW; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if ((u32)slots < msg_slots) 10948c2ecf20Sopenharmony_ci return -EMSGSIZE; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci rets = mei_cl_send_connect(cl, cb); 10978c2ecf20Sopenharmony_ci if (rets) 10988c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return rets; 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci/** 11048c2ecf20Sopenharmony_ci * mei_cl_connect - connect host client to the me one 11058c2ecf20Sopenharmony_ci * 11068c2ecf20Sopenharmony_ci * @cl: host client 11078c2ecf20Sopenharmony_ci * @me_cl: me client 11088c2ecf20Sopenharmony_ci * @fp: pointer to file structure 11098c2ecf20Sopenharmony_ci * 11108c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 11118c2ecf20Sopenharmony_ci * 11128c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ciint mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, 11158c2ecf20Sopenharmony_ci const struct file *fp) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct mei_device *dev; 11188c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 11198c2ecf20Sopenharmony_ci int rets; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev || !me_cl)) 11228c2ecf20Sopenharmony_ci return -ENODEV; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci dev = cl->dev; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci rets = mei_cl_set_connecting(cl, me_cl); 11278c2ecf20Sopenharmony_ci if (rets) 11288c2ecf20Sopenharmony_ci goto nortpm; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (mei_cl_is_fixed_address(cl)) { 11318c2ecf20Sopenharmony_ci cl->state = MEI_FILE_CONNECTED; 11328c2ecf20Sopenharmony_ci rets = 0; 11338c2ecf20Sopenharmony_ci goto nortpm; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci rets = pm_runtime_get(dev->dev); 11378c2ecf20Sopenharmony_ci if (rets < 0 && rets != -EINPROGRESS) { 11388c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 11398c2ecf20Sopenharmony_ci cl_err(dev, cl, "rpm: get failed %d\n", rets); 11408c2ecf20Sopenharmony_ci goto nortpm; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp); 11448c2ecf20Sopenharmony_ci if (!cb) { 11458c2ecf20Sopenharmony_ci rets = -ENOMEM; 11468c2ecf20Sopenharmony_ci goto out; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* run hbuf acquire last so we don't have to undo */ 11508c2ecf20Sopenharmony_ci if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 11518c2ecf20Sopenharmony_ci rets = mei_cl_send_connect(cl, cb); 11528c2ecf20Sopenharmony_ci if (rets) 11538c2ecf20Sopenharmony_ci goto out; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 11578c2ecf20Sopenharmony_ci wait_event_timeout(cl->wait, 11588c2ecf20Sopenharmony_ci (cl->state == MEI_FILE_CONNECTED || 11598c2ecf20Sopenharmony_ci cl->state == MEI_FILE_DISCONNECTED || 11608c2ecf20Sopenharmony_ci cl->state == MEI_FILE_DISCONNECT_REQUIRED || 11618c2ecf20Sopenharmony_ci cl->state == MEI_FILE_DISCONNECT_REPLY), 11628c2ecf20Sopenharmony_ci mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 11638c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) { 11668c2ecf20Sopenharmony_ci if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { 11678c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); 11688c2ecf20Sopenharmony_ci mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); 11698c2ecf20Sopenharmony_ci /* ignore disconnect return valuue; 11708c2ecf20Sopenharmony_ci * in case of failure reset will be invoked 11718c2ecf20Sopenharmony_ci */ 11728c2ecf20Sopenharmony_ci __mei_cl_disconnect(cl); 11738c2ecf20Sopenharmony_ci rets = -EFAULT; 11748c2ecf20Sopenharmony_ci goto out; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* timeout or something went really wrong */ 11788c2ecf20Sopenharmony_ci if (!cl->status) 11798c2ecf20Sopenharmony_ci cl->status = -EFAULT; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci rets = cl->status; 11838c2ecf20Sopenharmony_ciout: 11848c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "rpm: autosuspend\n"); 11858c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 11868c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cinortpm: 11918c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) 11928c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return rets; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci/** 11988c2ecf20Sopenharmony_ci * mei_cl_alloc_linked - allocate and link host client 11998c2ecf20Sopenharmony_ci * 12008c2ecf20Sopenharmony_ci * @dev: the device structure 12018c2ecf20Sopenharmony_ci * 12028c2ecf20Sopenharmony_ci * Return: cl on success ERR_PTR on failure 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_cistruct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct mei_cl *cl; 12078c2ecf20Sopenharmony_ci int ret; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci cl = mei_cl_allocate(dev); 12108c2ecf20Sopenharmony_ci if (!cl) { 12118c2ecf20Sopenharmony_ci ret = -ENOMEM; 12128c2ecf20Sopenharmony_ci goto err; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci ret = mei_cl_link(cl); 12168c2ecf20Sopenharmony_ci if (ret) 12178c2ecf20Sopenharmony_ci goto err; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return cl; 12208c2ecf20Sopenharmony_cierr: 12218c2ecf20Sopenharmony_ci kfree(cl); 12228c2ecf20Sopenharmony_ci return ERR_PTR(ret); 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci/** 12268c2ecf20Sopenharmony_ci * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl. 12278c2ecf20Sopenharmony_ci * 12288c2ecf20Sopenharmony_ci * @cl: host client 12298c2ecf20Sopenharmony_ci * 12308c2ecf20Sopenharmony_ci * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_cistatic int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->me_cl)) 12358c2ecf20Sopenharmony_ci return -EINVAL; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (cl->tx_flow_ctrl_creds > 0) 12388c2ecf20Sopenharmony_ci return 1; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (mei_cl_is_fixed_address(cl)) 12418c2ecf20Sopenharmony_ci return 1; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci if (mei_cl_is_single_recv_buf(cl)) { 12448c2ecf20Sopenharmony_ci if (cl->me_cl->tx_flow_ctrl_creds > 0) 12458c2ecf20Sopenharmony_ci return 1; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci return 0; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci/** 12518c2ecf20Sopenharmony_ci * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits 12528c2ecf20Sopenharmony_ci * for a client 12538c2ecf20Sopenharmony_ci * 12548c2ecf20Sopenharmony_ci * @cl: host client 12558c2ecf20Sopenharmony_ci * 12568c2ecf20Sopenharmony_ci * Return: 12578c2ecf20Sopenharmony_ci * 0 on success 12588c2ecf20Sopenharmony_ci * -EINVAL when ctrl credits are <= 0 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_cistatic int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->me_cl)) 12638c2ecf20Sopenharmony_ci return -EINVAL; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (mei_cl_is_fixed_address(cl)) 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (mei_cl_is_single_recv_buf(cl)) { 12698c2ecf20Sopenharmony_ci if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0)) 12708c2ecf20Sopenharmony_ci return -EINVAL; 12718c2ecf20Sopenharmony_ci cl->me_cl->tx_flow_ctrl_creds--; 12728c2ecf20Sopenharmony_ci } else { 12738c2ecf20Sopenharmony_ci if (WARN_ON(cl->tx_flow_ctrl_creds <= 0)) 12748c2ecf20Sopenharmony_ci return -EINVAL; 12758c2ecf20Sopenharmony_ci cl->tx_flow_ctrl_creds--; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci return 0; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci/** 12818c2ecf20Sopenharmony_ci * mei_cl_vtag_alloc - allocate and fill the vtag structure 12828c2ecf20Sopenharmony_ci * 12838c2ecf20Sopenharmony_ci * @fp: pointer to file structure 12848c2ecf20Sopenharmony_ci * @vtag: vm tag 12858c2ecf20Sopenharmony_ci * 12868c2ecf20Sopenharmony_ci * Return: 12878c2ecf20Sopenharmony_ci * * Pointer to allocated struct - on success 12888c2ecf20Sopenharmony_ci * * ERR_PTR(-ENOMEM) on memory allocation failure 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_cistruct mei_cl_vtag *mei_cl_vtag_alloc(struct file *fp, u8 vtag) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct mei_cl_vtag *cl_vtag; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci cl_vtag = kzalloc(sizeof(*cl_vtag), GFP_KERNEL); 12958c2ecf20Sopenharmony_ci if (!cl_vtag) 12968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cl_vtag->list); 12998c2ecf20Sopenharmony_ci cl_vtag->vtag = vtag; 13008c2ecf20Sopenharmony_ci cl_vtag->fp = fp; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return cl_vtag; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/** 13068c2ecf20Sopenharmony_ci * mei_cl_fp_by_vtag - obtain the file pointer by vtag 13078c2ecf20Sopenharmony_ci * 13088c2ecf20Sopenharmony_ci * @cl: host client 13098c2ecf20Sopenharmony_ci * @vtag: vm tag 13108c2ecf20Sopenharmony_ci * 13118c2ecf20Sopenharmony_ci * Return: 13128c2ecf20Sopenharmony_ci * * A file pointer - on success 13138c2ecf20Sopenharmony_ci * * ERR_PTR(-ENOENT) if vtag is not found in the client vtag list 13148c2ecf20Sopenharmony_ci */ 13158c2ecf20Sopenharmony_ciconst struct file *mei_cl_fp_by_vtag(const struct mei_cl *cl, u8 vtag) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct mei_cl_vtag *vtag_l; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci list_for_each_entry(vtag_l, &cl->vtag_map, list) 13208c2ecf20Sopenharmony_ci if (vtag_l->vtag == vtag) 13218c2ecf20Sopenharmony_ci return vtag_l->fp; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci/** 13278c2ecf20Sopenharmony_ci * mei_cl_reset_read_by_vtag - reset pending_read flag by given vtag 13288c2ecf20Sopenharmony_ci * 13298c2ecf20Sopenharmony_ci * @cl: host client 13308c2ecf20Sopenharmony_ci * @vtag: vm tag 13318c2ecf20Sopenharmony_ci */ 13328c2ecf20Sopenharmony_cistatic void mei_cl_reset_read_by_vtag(const struct mei_cl *cl, u8 vtag) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci struct mei_cl_vtag *vtag_l; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci list_for_each_entry(vtag_l, &cl->vtag_map, list) { 13378c2ecf20Sopenharmony_ci if (vtag_l->vtag == vtag) { 13388c2ecf20Sopenharmony_ci vtag_l->pending_read = false; 13398c2ecf20Sopenharmony_ci break; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci/** 13458c2ecf20Sopenharmony_ci * mei_cl_read_vtag_add_fc - add flow control for next pending reader 13468c2ecf20Sopenharmony_ci * in the vtag list 13478c2ecf20Sopenharmony_ci * 13488c2ecf20Sopenharmony_ci * @cl: host client 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_cistatic void mei_cl_read_vtag_add_fc(struct mei_cl *cl) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct mei_cl_vtag *cl_vtag; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci list_for_each_entry(cl_vtag, &cl->vtag_map, list) { 13558c2ecf20Sopenharmony_ci if (cl_vtag->pending_read) { 13568c2ecf20Sopenharmony_ci if (mei_cl_enqueue_ctrl_wr_cb(cl, 13578c2ecf20Sopenharmony_ci mei_cl_mtu(cl), 13588c2ecf20Sopenharmony_ci MEI_FOP_READ, 13598c2ecf20Sopenharmony_ci cl_vtag->fp)) 13608c2ecf20Sopenharmony_ci cl->rx_flow_ctrl_creds++; 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci/** 13678c2ecf20Sopenharmony_ci * mei_cl_vt_support_check - check if client support vtags 13688c2ecf20Sopenharmony_ci * 13698c2ecf20Sopenharmony_ci * @cl: host client 13708c2ecf20Sopenharmony_ci * 13718c2ecf20Sopenharmony_ci * Return: 13728c2ecf20Sopenharmony_ci * * 0 - supported, or not connected at all 13738c2ecf20Sopenharmony_ci * * -EOPNOTSUPP - vtags are not supported by client 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_ciint mei_cl_vt_support_check(const struct mei_cl *cl) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (!dev->hbm_f_vt_supported) 13808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (!cl->me_cl) 13838c2ecf20Sopenharmony_ci return 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci return cl->me_cl->props.vt_supported ? 0 : -EOPNOTSUPP; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci/** 13898c2ecf20Sopenharmony_ci * mei_cl_add_rd_completed - add read completed callback to list with lock 13908c2ecf20Sopenharmony_ci * and vtag check 13918c2ecf20Sopenharmony_ci * 13928c2ecf20Sopenharmony_ci * @cl: host client 13938c2ecf20Sopenharmony_ci * @cb: callback block 13948c2ecf20Sopenharmony_ci * 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_civoid mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci const struct file *fp; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (!mei_cl_vt_support_check(cl)) { 14018c2ecf20Sopenharmony_ci fp = mei_cl_fp_by_vtag(cl, cb->vtag); 14028c2ecf20Sopenharmony_ci if (IS_ERR(fp)) { 14038c2ecf20Sopenharmony_ci /* client already disconnected, discarding */ 14048c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 14058c2ecf20Sopenharmony_ci return; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci cb->fp = fp; 14088c2ecf20Sopenharmony_ci mei_cl_reset_read_by_vtag(cl, cb->vtag); 14098c2ecf20Sopenharmony_ci mei_cl_read_vtag_add_fc(cl); 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci spin_lock(&cl->rd_completed_lock); 14138c2ecf20Sopenharmony_ci list_add_tail(&cb->list, &cl->rd_completed); 14148c2ecf20Sopenharmony_ci spin_unlock(&cl->rd_completed_lock); 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci/** 14188c2ecf20Sopenharmony_ci * mei_cl_del_rd_completed - free read completed callback with lock 14198c2ecf20Sopenharmony_ci * 14208c2ecf20Sopenharmony_ci * @cl: host client 14218c2ecf20Sopenharmony_ci * @cb: callback block 14228c2ecf20Sopenharmony_ci * 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_civoid mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) 14258c2ecf20Sopenharmony_ci{ 14268c2ecf20Sopenharmony_ci spin_lock(&cl->rd_completed_lock); 14278c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 14288c2ecf20Sopenharmony_ci spin_unlock(&cl->rd_completed_lock); 14298c2ecf20Sopenharmony_ci} 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci/** 14328c2ecf20Sopenharmony_ci * mei_cl_notify_fop2req - convert fop to proper request 14338c2ecf20Sopenharmony_ci * 14348c2ecf20Sopenharmony_ci * @fop: client notification start response command 14358c2ecf20Sopenharmony_ci * 14368c2ecf20Sopenharmony_ci * Return: MEI_HBM_NOTIFICATION_START/STOP 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_ciu8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci if (fop == MEI_FOP_NOTIFY_START) 14418c2ecf20Sopenharmony_ci return MEI_HBM_NOTIFICATION_START; 14428c2ecf20Sopenharmony_ci else 14438c2ecf20Sopenharmony_ci return MEI_HBM_NOTIFICATION_STOP; 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci/** 14478c2ecf20Sopenharmony_ci * mei_cl_notify_req2fop - convert notification request top file operation type 14488c2ecf20Sopenharmony_ci * 14498c2ecf20Sopenharmony_ci * @req: hbm notification request type 14508c2ecf20Sopenharmony_ci * 14518c2ecf20Sopenharmony_ci * Return: MEI_FOP_NOTIFY_START/STOP 14528c2ecf20Sopenharmony_ci */ 14538c2ecf20Sopenharmony_cienum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci if (req == MEI_HBM_NOTIFICATION_START) 14568c2ecf20Sopenharmony_ci return MEI_FOP_NOTIFY_START; 14578c2ecf20Sopenharmony_ci else 14588c2ecf20Sopenharmony_ci return MEI_FOP_NOTIFY_STOP; 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci/** 14628c2ecf20Sopenharmony_ci * mei_cl_irq_notify - send notification request in irq_thread context 14638c2ecf20Sopenharmony_ci * 14648c2ecf20Sopenharmony_ci * @cl: client 14658c2ecf20Sopenharmony_ci * @cb: callback block. 14668c2ecf20Sopenharmony_ci * @cmpl_list: complete list. 14678c2ecf20Sopenharmony_ci * 14688c2ecf20Sopenharmony_ci * Return: 0 on such and error otherwise. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_ciint mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, 14718c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 14748c2ecf20Sopenharmony_ci u32 msg_slots; 14758c2ecf20Sopenharmony_ci int slots; 14768c2ecf20Sopenharmony_ci int ret; 14778c2ecf20Sopenharmony_ci bool request; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci msg_slots = mei_hbm2slots(sizeof(struct hbm_client_connect_request)); 14808c2ecf20Sopenharmony_ci slots = mei_hbuf_empty_slots(dev); 14818c2ecf20Sopenharmony_ci if (slots < 0) 14828c2ecf20Sopenharmony_ci return -EOVERFLOW; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if ((u32)slots < msg_slots) 14858c2ecf20Sopenharmony_ci return -EMSGSIZE; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci request = mei_cl_notify_fop2req(cb->fop_type); 14888c2ecf20Sopenharmony_ci ret = mei_hbm_cl_notify_req(dev, cl, request); 14898c2ecf20Sopenharmony_ci if (ret) { 14908c2ecf20Sopenharmony_ci cl->status = ret; 14918c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 14928c2ecf20Sopenharmony_ci return ret; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &dev->ctrl_rd_list); 14968c2ecf20Sopenharmony_ci return 0; 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/** 15008c2ecf20Sopenharmony_ci * mei_cl_notify_request - send notification stop/start request 15018c2ecf20Sopenharmony_ci * 15028c2ecf20Sopenharmony_ci * @cl: host client 15038c2ecf20Sopenharmony_ci * @fp: associate request with file 15048c2ecf20Sopenharmony_ci * @request: 1 for start or 0 for stop 15058c2ecf20Sopenharmony_ci * 15068c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 15078c2ecf20Sopenharmony_ci * 15088c2ecf20Sopenharmony_ci * Return: 0 on such and error otherwise. 15098c2ecf20Sopenharmony_ci */ 15108c2ecf20Sopenharmony_ciint mei_cl_notify_request(struct mei_cl *cl, 15118c2ecf20Sopenharmony_ci const struct file *fp, u8 request) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct mei_device *dev; 15148c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 15158c2ecf20Sopenharmony_ci enum mei_cb_file_ops fop_type; 15168c2ecf20Sopenharmony_ci int rets; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 15198c2ecf20Sopenharmony_ci return -ENODEV; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci dev = cl->dev; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (!dev->hbm_f_ev_supported) { 15248c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "notifications not supported\n"); 15258c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) 15298c2ecf20Sopenharmony_ci return -ENODEV; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci rets = pm_runtime_get(dev->dev); 15328c2ecf20Sopenharmony_ci if (rets < 0 && rets != -EINPROGRESS) { 15338c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 15348c2ecf20Sopenharmony_ci cl_err(dev, cl, "rpm: get failed %d\n", rets); 15358c2ecf20Sopenharmony_ci return rets; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci fop_type = mei_cl_notify_req2fop(request); 15398c2ecf20Sopenharmony_ci cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp); 15408c2ecf20Sopenharmony_ci if (!cb) { 15418c2ecf20Sopenharmony_ci rets = -ENOMEM; 15428c2ecf20Sopenharmony_ci goto out; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (mei_hbuf_acquire(dev)) { 15468c2ecf20Sopenharmony_ci if (mei_hbm_cl_notify_req(dev, cl, request)) { 15478c2ecf20Sopenharmony_ci rets = -ENODEV; 15488c2ecf20Sopenharmony_ci goto out; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &dev->ctrl_rd_list); 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 15548c2ecf20Sopenharmony_ci wait_event_timeout(cl->wait, 15558c2ecf20Sopenharmony_ci cl->notify_en == request || 15568c2ecf20Sopenharmony_ci cl->status || 15578c2ecf20Sopenharmony_ci !mei_cl_is_connected(cl), 15588c2ecf20Sopenharmony_ci mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 15598c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci if (cl->notify_en != request && !cl->status) 15628c2ecf20Sopenharmony_ci cl->status = -EFAULT; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci rets = cl->status; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ciout: 15678c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "rpm: autosuspend\n"); 15688c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 15698c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 15728c2ecf20Sopenharmony_ci return rets; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci/** 15768c2ecf20Sopenharmony_ci * mei_cl_notify - raise notification 15778c2ecf20Sopenharmony_ci * 15788c2ecf20Sopenharmony_ci * @cl: host client 15798c2ecf20Sopenharmony_ci * 15808c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 15818c2ecf20Sopenharmony_ci */ 15828c2ecf20Sopenharmony_civoid mei_cl_notify(struct mei_cl *cl) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci struct mei_device *dev; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if (!cl || !cl->dev) 15878c2ecf20Sopenharmony_ci return; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci dev = cl->dev; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (!cl->notify_en) 15928c2ecf20Sopenharmony_ci return; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "notify event"); 15958c2ecf20Sopenharmony_ci cl->notify_ev = true; 15968c2ecf20Sopenharmony_ci if (!mei_cl_bus_notify_event(cl)) 15978c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->ev_wait); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (cl->ev_async) 16008c2ecf20Sopenharmony_ci kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci/** 16058c2ecf20Sopenharmony_ci * mei_cl_notify_get - get or wait for notification event 16068c2ecf20Sopenharmony_ci * 16078c2ecf20Sopenharmony_ci * @cl: host client 16088c2ecf20Sopenharmony_ci * @block: this request is blocking 16098c2ecf20Sopenharmony_ci * @notify_ev: true if notification event was received 16108c2ecf20Sopenharmony_ci * 16118c2ecf20Sopenharmony_ci * Locking: called under "dev->device_lock" lock 16128c2ecf20Sopenharmony_ci * 16138c2ecf20Sopenharmony_ci * Return: 0 on such and error otherwise. 16148c2ecf20Sopenharmony_ci */ 16158c2ecf20Sopenharmony_ciint mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) 16168c2ecf20Sopenharmony_ci{ 16178c2ecf20Sopenharmony_ci struct mei_device *dev; 16188c2ecf20Sopenharmony_ci int rets; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci *notify_ev = false; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 16238c2ecf20Sopenharmony_ci return -ENODEV; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci dev = cl->dev; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci if (!dev->hbm_f_ev_supported) { 16288c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "notifications not supported\n"); 16298c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) 16338c2ecf20Sopenharmony_ci return -ENODEV; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (cl->notify_ev) 16368c2ecf20Sopenharmony_ci goto out; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (!block) 16398c2ecf20Sopenharmony_ci return -EAGAIN; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 16428c2ecf20Sopenharmony_ci rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); 16438c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if (rets < 0) 16468c2ecf20Sopenharmony_ci return rets; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ciout: 16498c2ecf20Sopenharmony_ci *notify_ev = cl->notify_ev; 16508c2ecf20Sopenharmony_ci cl->notify_ev = false; 16518c2ecf20Sopenharmony_ci return 0; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/** 16558c2ecf20Sopenharmony_ci * mei_cl_read_start - the start read client message function. 16568c2ecf20Sopenharmony_ci * 16578c2ecf20Sopenharmony_ci * @cl: host client 16588c2ecf20Sopenharmony_ci * @length: number of bytes to read 16598c2ecf20Sopenharmony_ci * @fp: pointer to file structure 16608c2ecf20Sopenharmony_ci * 16618c2ecf20Sopenharmony_ci * Return: 0 on success, <0 on failure. 16628c2ecf20Sopenharmony_ci */ 16638c2ecf20Sopenharmony_ciint mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) 16648c2ecf20Sopenharmony_ci{ 16658c2ecf20Sopenharmony_ci struct mei_device *dev; 16668c2ecf20Sopenharmony_ci struct mei_cl_cb *cb; 16678c2ecf20Sopenharmony_ci int rets; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 16708c2ecf20Sopenharmony_ci return -ENODEV; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci dev = cl->dev; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (!mei_cl_is_connected(cl)) 16758c2ecf20Sopenharmony_ci return -ENODEV; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (!mei_me_cl_is_active(cl->me_cl)) { 16788c2ecf20Sopenharmony_ci cl_err(dev, cl, "no such me client\n"); 16798c2ecf20Sopenharmony_ci return -ENOTTY; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (mei_cl_is_fixed_address(cl)) 16838c2ecf20Sopenharmony_ci return 0; 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci /* HW currently supports only one pending read */ 16868c2ecf20Sopenharmony_ci if (cl->rx_flow_ctrl_creds) { 16878c2ecf20Sopenharmony_ci mei_cl_set_read_by_fp(cl, fp); 16888c2ecf20Sopenharmony_ci return -EBUSY; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp); 16928c2ecf20Sopenharmony_ci if (!cb) 16938c2ecf20Sopenharmony_ci return -ENOMEM; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci mei_cl_set_read_by_fp(cl, fp); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci rets = pm_runtime_get(dev->dev); 16988c2ecf20Sopenharmony_ci if (rets < 0 && rets != -EINPROGRESS) { 16998c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 17008c2ecf20Sopenharmony_ci cl_err(dev, cl, "rpm: get failed %d\n", rets); 17018c2ecf20Sopenharmony_ci goto nortpm; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci rets = 0; 17058c2ecf20Sopenharmony_ci if (mei_hbuf_acquire(dev)) { 17068c2ecf20Sopenharmony_ci rets = mei_hbm_cl_flow_control_req(dev, cl); 17078c2ecf20Sopenharmony_ci if (rets < 0) 17088c2ecf20Sopenharmony_ci goto out; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &cl->rd_pending); 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci cl->rx_flow_ctrl_creds++; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ciout: 17158c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "rpm: autosuspend\n"); 17168c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 17178c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 17188c2ecf20Sopenharmony_cinortpm: 17198c2ecf20Sopenharmony_ci if (rets) 17208c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci return rets; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic inline u8 mei_ext_hdr_set_vtag(struct mei_ext_hdr *ext, u8 vtag) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci ext->type = MEI_EXT_HDR_VTAG; 17288c2ecf20Sopenharmony_ci ext->ext_payload[0] = vtag; 17298c2ecf20Sopenharmony_ci ext->length = mei_data2slots(sizeof(*ext)); 17308c2ecf20Sopenharmony_ci return ext->length; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci/** 17348c2ecf20Sopenharmony_ci * mei_msg_hdr_init - allocate and initialize mei message header 17358c2ecf20Sopenharmony_ci * 17368c2ecf20Sopenharmony_ci * @cb: message callback structure 17378c2ecf20Sopenharmony_ci * 17388c2ecf20Sopenharmony_ci * Return: a pointer to initialized header 17398c2ecf20Sopenharmony_ci */ 17408c2ecf20Sopenharmony_cistatic struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb) 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci size_t hdr_len; 17438c2ecf20Sopenharmony_ci struct mei_ext_meta_hdr *meta; 17448c2ecf20Sopenharmony_ci struct mei_ext_hdr *ext; 17458c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr; 17468c2ecf20Sopenharmony_ci bool is_ext, is_vtag; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (!cb) 17498c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* Extended header for vtag is attached only on the first fragment */ 17528c2ecf20Sopenharmony_ci is_vtag = (cb->vtag && cb->buf_idx == 0); 17538c2ecf20Sopenharmony_ci is_ext = is_vtag; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci /* Compute extended header size */ 17568c2ecf20Sopenharmony_ci hdr_len = sizeof(*mei_hdr); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci if (!is_ext) 17598c2ecf20Sopenharmony_ci goto setup_hdr; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci hdr_len += sizeof(*meta); 17628c2ecf20Sopenharmony_ci if (is_vtag) 17638c2ecf20Sopenharmony_ci hdr_len += sizeof(*ext); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cisetup_hdr: 17668c2ecf20Sopenharmony_ci mei_hdr = kzalloc(hdr_len, GFP_KERNEL); 17678c2ecf20Sopenharmony_ci if (!mei_hdr) 17688c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci mei_hdr->host_addr = mei_cl_host_addr(cb->cl); 17718c2ecf20Sopenharmony_ci mei_hdr->me_addr = mei_cl_me_id(cb->cl); 17728c2ecf20Sopenharmony_ci mei_hdr->internal = cb->internal; 17738c2ecf20Sopenharmony_ci mei_hdr->extended = is_ext; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci if (!is_ext) 17768c2ecf20Sopenharmony_ci goto out; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci meta = (struct mei_ext_meta_hdr *)mei_hdr->extension; 17798c2ecf20Sopenharmony_ci if (is_vtag) { 17808c2ecf20Sopenharmony_ci meta->count++; 17818c2ecf20Sopenharmony_ci meta->size += mei_ext_hdr_set_vtag(meta->hdrs, cb->vtag); 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ciout: 17848c2ecf20Sopenharmony_ci mei_hdr->length = hdr_len - sizeof(*mei_hdr); 17858c2ecf20Sopenharmony_ci return mei_hdr; 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci/** 17898c2ecf20Sopenharmony_ci * mei_cl_irq_write - write a message to device 17908c2ecf20Sopenharmony_ci * from the interrupt thread context 17918c2ecf20Sopenharmony_ci * 17928c2ecf20Sopenharmony_ci * @cl: client 17938c2ecf20Sopenharmony_ci * @cb: callback block. 17948c2ecf20Sopenharmony_ci * @cmpl_list: complete list. 17958c2ecf20Sopenharmony_ci * 17968c2ecf20Sopenharmony_ci * Return: 0, OK; otherwise error. 17978c2ecf20Sopenharmony_ci */ 17988c2ecf20Sopenharmony_ciint mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 17998c2ecf20Sopenharmony_ci struct list_head *cmpl_list) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci struct mei_device *dev; 18028c2ecf20Sopenharmony_ci struct mei_msg_data *buf; 18038c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr = NULL; 18048c2ecf20Sopenharmony_ci size_t hdr_len; 18058c2ecf20Sopenharmony_ci size_t hbuf_len, dr_len; 18068c2ecf20Sopenharmony_ci size_t buf_len; 18078c2ecf20Sopenharmony_ci size_t data_len; 18088c2ecf20Sopenharmony_ci int hbuf_slots; 18098c2ecf20Sopenharmony_ci u32 dr_slots; 18108c2ecf20Sopenharmony_ci u32 dma_len; 18118c2ecf20Sopenharmony_ci int rets; 18128c2ecf20Sopenharmony_ci bool first_chunk; 18138c2ecf20Sopenharmony_ci const void *data; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 18168c2ecf20Sopenharmony_ci return -ENODEV; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci dev = cl->dev; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci buf = &cb->buf; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci first_chunk = cb->buf_idx == 0; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1; 18258c2ecf20Sopenharmony_ci if (rets < 0) 18268c2ecf20Sopenharmony_ci goto err; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci if (rets == 0) { 18298c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 18308c2ecf20Sopenharmony_ci return 0; 18318c2ecf20Sopenharmony_ci } 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci buf_len = buf->size - cb->buf_idx; 18348c2ecf20Sopenharmony_ci data = buf->data + cb->buf_idx; 18358c2ecf20Sopenharmony_ci hbuf_slots = mei_hbuf_empty_slots(dev); 18368c2ecf20Sopenharmony_ci if (hbuf_slots < 0) { 18378c2ecf20Sopenharmony_ci rets = -EOVERFLOW; 18388c2ecf20Sopenharmony_ci goto err; 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci hbuf_len = mei_slots2data(hbuf_slots) & MEI_MSG_MAX_LEN_MASK; 18428c2ecf20Sopenharmony_ci dr_slots = mei_dma_ring_empty_slots(dev); 18438c2ecf20Sopenharmony_ci dr_len = mei_slots2data(dr_slots); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci mei_hdr = mei_msg_hdr_init(cb); 18468c2ecf20Sopenharmony_ci if (IS_ERR(mei_hdr)) { 18478c2ecf20Sopenharmony_ci rets = PTR_ERR(mei_hdr); 18488c2ecf20Sopenharmony_ci mei_hdr = NULL; 18498c2ecf20Sopenharmony_ci goto err; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Extended Header %d vtag = %d\n", 18538c2ecf20Sopenharmony_ci mei_hdr->extended, cb->vtag); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci hdr_len = sizeof(*mei_hdr) + mei_hdr->length; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci /** 18588c2ecf20Sopenharmony_ci * Split the message only if we can write the whole host buffer 18598c2ecf20Sopenharmony_ci * otherwise wait for next time the host buffer is empty. 18608c2ecf20Sopenharmony_ci */ 18618c2ecf20Sopenharmony_ci if (hdr_len + buf_len <= hbuf_len) { 18628c2ecf20Sopenharmony_ci data_len = buf_len; 18638c2ecf20Sopenharmony_ci mei_hdr->msg_complete = 1; 18648c2ecf20Sopenharmony_ci } else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) { 18658c2ecf20Sopenharmony_ci mei_hdr->dma_ring = 1; 18668c2ecf20Sopenharmony_ci if (buf_len > dr_len) 18678c2ecf20Sopenharmony_ci buf_len = dr_len; 18688c2ecf20Sopenharmony_ci else 18698c2ecf20Sopenharmony_ci mei_hdr->msg_complete = 1; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci data_len = sizeof(dma_len); 18728c2ecf20Sopenharmony_ci dma_len = buf_len; 18738c2ecf20Sopenharmony_ci data = &dma_len; 18748c2ecf20Sopenharmony_ci } else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) { 18758c2ecf20Sopenharmony_ci buf_len = hbuf_len - hdr_len; 18768c2ecf20Sopenharmony_ci data_len = buf_len; 18778c2ecf20Sopenharmony_ci } else { 18788c2ecf20Sopenharmony_ci kfree(mei_hdr); 18798c2ecf20Sopenharmony_ci return 0; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci mei_hdr->length += data_len; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) 18848c2ecf20Sopenharmony_ci mei_dma_ring_write(dev, buf->data + cb->buf_idx, buf_len); 18858c2ecf20Sopenharmony_ci rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (rets) 18888c2ecf20Sopenharmony_ci goto err; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci cl->status = 0; 18918c2ecf20Sopenharmony_ci cl->writing_state = MEI_WRITING; 18928c2ecf20Sopenharmony_ci cb->buf_idx += buf_len; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (first_chunk) { 18958c2ecf20Sopenharmony_ci if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) { 18968c2ecf20Sopenharmony_ci rets = -EIO; 18978c2ecf20Sopenharmony_ci goto err; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (mei_hdr->msg_complete) 19028c2ecf20Sopenharmony_ci list_move_tail(&cb->list, &dev->write_waiting_list); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci kfree(mei_hdr); 19058c2ecf20Sopenharmony_ci return 0; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cierr: 19088c2ecf20Sopenharmony_ci kfree(mei_hdr); 19098c2ecf20Sopenharmony_ci cl->status = rets; 19108c2ecf20Sopenharmony_ci list_move_tail(&cb->list, cmpl_list); 19118c2ecf20Sopenharmony_ci return rets; 19128c2ecf20Sopenharmony_ci} 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci/** 19158c2ecf20Sopenharmony_ci * mei_cl_write - submit a write cb to mei device 19168c2ecf20Sopenharmony_ci * assumes device_lock is locked 19178c2ecf20Sopenharmony_ci * 19188c2ecf20Sopenharmony_ci * @cl: host client 19198c2ecf20Sopenharmony_ci * @cb: write callback with filled data 19208c2ecf20Sopenharmony_ci * 19218c2ecf20Sopenharmony_ci * Return: number of bytes sent on success, <0 on failure. 19228c2ecf20Sopenharmony_ci */ 19238c2ecf20Sopenharmony_cissize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci struct mei_device *dev; 19268c2ecf20Sopenharmony_ci struct mei_msg_data *buf; 19278c2ecf20Sopenharmony_ci struct mei_msg_hdr *mei_hdr = NULL; 19288c2ecf20Sopenharmony_ci size_t hdr_len; 19298c2ecf20Sopenharmony_ci size_t hbuf_len, dr_len; 19308c2ecf20Sopenharmony_ci size_t buf_len; 19318c2ecf20Sopenharmony_ci size_t data_len; 19328c2ecf20Sopenharmony_ci int hbuf_slots; 19338c2ecf20Sopenharmony_ci u32 dr_slots; 19348c2ecf20Sopenharmony_ci u32 dma_len; 19358c2ecf20Sopenharmony_ci ssize_t rets; 19368c2ecf20Sopenharmony_ci bool blocking; 19378c2ecf20Sopenharmony_ci const void *data; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (WARN_ON(!cl || !cl->dev)) 19408c2ecf20Sopenharmony_ci return -ENODEV; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci if (WARN_ON(!cb)) 19438c2ecf20Sopenharmony_ci return -EINVAL; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci dev = cl->dev; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci buf = &cb->buf; 19488c2ecf20Sopenharmony_ci buf_len = buf->size; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "buf_len=%zd\n", buf_len); 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci blocking = cb->blocking; 19538c2ecf20Sopenharmony_ci data = buf->data; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci rets = pm_runtime_get(dev->dev); 19568c2ecf20Sopenharmony_ci if (rets < 0 && rets != -EINPROGRESS) { 19578c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 19588c2ecf20Sopenharmony_ci cl_err(dev, cl, "rpm: get failed %zd\n", rets); 19598c2ecf20Sopenharmony_ci goto free; 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci cb->buf_idx = 0; 19638c2ecf20Sopenharmony_ci cl->writing_state = MEI_IDLE; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci rets = mei_cl_tx_flow_ctrl_creds(cl); 19678c2ecf20Sopenharmony_ci if (rets < 0) 19688c2ecf20Sopenharmony_ci goto err; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci mei_hdr = mei_msg_hdr_init(cb); 19718c2ecf20Sopenharmony_ci if (IS_ERR(mei_hdr)) { 19728c2ecf20Sopenharmony_ci rets = PTR_ERR(mei_hdr); 19738c2ecf20Sopenharmony_ci mei_hdr = NULL; 19748c2ecf20Sopenharmony_ci goto err; 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Extended Header %d vtag = %d\n", 19788c2ecf20Sopenharmony_ci mei_hdr->extended, cb->vtag); 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci hdr_len = sizeof(*mei_hdr) + mei_hdr->length; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci if (rets == 0) { 19838c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 19848c2ecf20Sopenharmony_ci rets = buf_len; 19858c2ecf20Sopenharmony_ci goto out; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci if (!mei_hbuf_acquire(dev)) { 19898c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 19908c2ecf20Sopenharmony_ci rets = buf_len; 19918c2ecf20Sopenharmony_ci goto out; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci hbuf_slots = mei_hbuf_empty_slots(dev); 19958c2ecf20Sopenharmony_ci if (hbuf_slots < 0) { 19968c2ecf20Sopenharmony_ci buf_len = -EOVERFLOW; 19978c2ecf20Sopenharmony_ci goto out; 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci hbuf_len = mei_slots2data(hbuf_slots) & MEI_MSG_MAX_LEN_MASK; 20018c2ecf20Sopenharmony_ci dr_slots = mei_dma_ring_empty_slots(dev); 20028c2ecf20Sopenharmony_ci dr_len = mei_slots2data(dr_slots); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci if (hdr_len + buf_len <= hbuf_len) { 20058c2ecf20Sopenharmony_ci data_len = buf_len; 20068c2ecf20Sopenharmony_ci mei_hdr->msg_complete = 1; 20078c2ecf20Sopenharmony_ci } else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) { 20088c2ecf20Sopenharmony_ci mei_hdr->dma_ring = 1; 20098c2ecf20Sopenharmony_ci if (buf_len > dr_len) 20108c2ecf20Sopenharmony_ci buf_len = dr_len; 20118c2ecf20Sopenharmony_ci else 20128c2ecf20Sopenharmony_ci mei_hdr->msg_complete = 1; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci data_len = sizeof(dma_len); 20158c2ecf20Sopenharmony_ci dma_len = buf_len; 20168c2ecf20Sopenharmony_ci data = &dma_len; 20178c2ecf20Sopenharmony_ci } else { 20188c2ecf20Sopenharmony_ci buf_len = hbuf_len - hdr_len; 20198c2ecf20Sopenharmony_ci data_len = buf_len; 20208c2ecf20Sopenharmony_ci } 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci mei_hdr->length += data_len; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci if (mei_hdr->dma_ring) 20258c2ecf20Sopenharmony_ci mei_dma_ring_write(dev, buf->data, buf_len); 20268c2ecf20Sopenharmony_ci rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (rets) 20298c2ecf20Sopenharmony_ci goto err; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci rets = mei_cl_tx_flow_ctrl_creds_reduce(cl); 20328c2ecf20Sopenharmony_ci if (rets) 20338c2ecf20Sopenharmony_ci goto err; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci cl->writing_state = MEI_WRITING; 20368c2ecf20Sopenharmony_ci cb->buf_idx = buf_len; 20378c2ecf20Sopenharmony_ci /* restore return value */ 20388c2ecf20Sopenharmony_ci buf_len = buf->size; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ciout: 20418c2ecf20Sopenharmony_ci if (mei_hdr->msg_complete) 20428c2ecf20Sopenharmony_ci mei_tx_cb_enqueue(cb, &dev->write_waiting_list); 20438c2ecf20Sopenharmony_ci else 20448c2ecf20Sopenharmony_ci mei_tx_cb_enqueue(cb, &dev->write_list); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci cb = NULL; 20478c2ecf20Sopenharmony_ci if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci mutex_unlock(&dev->device_lock); 20508c2ecf20Sopenharmony_ci rets = wait_event_interruptible(cl->tx_wait, 20518c2ecf20Sopenharmony_ci cl->writing_state == MEI_WRITE_COMPLETE || 20528c2ecf20Sopenharmony_ci (!mei_cl_is_connected(cl))); 20538c2ecf20Sopenharmony_ci mutex_lock(&dev->device_lock); 20548c2ecf20Sopenharmony_ci /* wait_event_interruptible returns -ERESTARTSYS */ 20558c2ecf20Sopenharmony_ci if (rets) { 20568c2ecf20Sopenharmony_ci if (signal_pending(current)) 20578c2ecf20Sopenharmony_ci rets = -EINTR; 20588c2ecf20Sopenharmony_ci goto err; 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci if (cl->writing_state != MEI_WRITE_COMPLETE) { 20618c2ecf20Sopenharmony_ci rets = -EFAULT; 20628c2ecf20Sopenharmony_ci goto err; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci rets = buf_len; 20678c2ecf20Sopenharmony_cierr: 20688c2ecf20Sopenharmony_ci cl_dbg(dev, cl, "rpm: autosuspend\n"); 20698c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 20708c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 20718c2ecf20Sopenharmony_cifree: 20728c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci kfree(mei_hdr); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci return rets; 20778c2ecf20Sopenharmony_ci} 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci/** 20808c2ecf20Sopenharmony_ci * mei_cl_complete - processes completed operation for a client 20818c2ecf20Sopenharmony_ci * 20828c2ecf20Sopenharmony_ci * @cl: private data of the file object. 20838c2ecf20Sopenharmony_ci * @cb: callback block. 20848c2ecf20Sopenharmony_ci */ 20858c2ecf20Sopenharmony_civoid mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 20868c2ecf20Sopenharmony_ci{ 20878c2ecf20Sopenharmony_ci struct mei_device *dev = cl->dev; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci switch (cb->fop_type) { 20908c2ecf20Sopenharmony_ci case MEI_FOP_WRITE: 20918c2ecf20Sopenharmony_ci mei_tx_cb_dequeue(cb); 20928c2ecf20Sopenharmony_ci cl->writing_state = MEI_WRITE_COMPLETE; 20938c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->tx_wait)) { 20948c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->tx_wait); 20958c2ecf20Sopenharmony_ci } else { 20968c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 20978c2ecf20Sopenharmony_ci pm_request_autosuspend(dev->dev); 20988c2ecf20Sopenharmony_ci } 20998c2ecf20Sopenharmony_ci break; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci case MEI_FOP_READ: 21028c2ecf20Sopenharmony_ci mei_cl_add_rd_completed(cl, cb); 21038c2ecf20Sopenharmony_ci if (!mei_cl_is_fixed_address(cl) && 21048c2ecf20Sopenharmony_ci !WARN_ON(!cl->rx_flow_ctrl_creds)) 21058c2ecf20Sopenharmony_ci cl->rx_flow_ctrl_creds--; 21068c2ecf20Sopenharmony_ci if (!mei_cl_bus_rx_event(cl)) 21078c2ecf20Sopenharmony_ci wake_up_interruptible(&cl->rx_wait); 21088c2ecf20Sopenharmony_ci break; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci case MEI_FOP_CONNECT: 21118c2ecf20Sopenharmony_ci case MEI_FOP_DISCONNECT: 21128c2ecf20Sopenharmony_ci case MEI_FOP_NOTIFY_STOP: 21138c2ecf20Sopenharmony_ci case MEI_FOP_NOTIFY_START: 21148c2ecf20Sopenharmony_ci if (waitqueue_active(&cl->wait)) 21158c2ecf20Sopenharmony_ci wake_up(&cl->wait); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci break; 21188c2ecf20Sopenharmony_ci case MEI_FOP_DISCONNECT_RSP: 21198c2ecf20Sopenharmony_ci mei_io_cb_free(cb); 21208c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 21218c2ecf20Sopenharmony_ci break; 21228c2ecf20Sopenharmony_ci default: 21238c2ecf20Sopenharmony_ci BUG_ON(0); 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci} 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci/** 21298c2ecf20Sopenharmony_ci * mei_cl_all_disconnect - disconnect forcefully all connected clients 21308c2ecf20Sopenharmony_ci * 21318c2ecf20Sopenharmony_ci * @dev: mei device 21328c2ecf20Sopenharmony_ci */ 21338c2ecf20Sopenharmony_civoid mei_cl_all_disconnect(struct mei_device *dev) 21348c2ecf20Sopenharmony_ci{ 21358c2ecf20Sopenharmony_ci struct mei_cl *cl; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci list_for_each_entry(cl, &dev->file_list, link) 21388c2ecf20Sopenharmony_ci mei_cl_set_disconnected(cl); 21398c2ecf20Sopenharmony_ci} 2140