19ca9050bSTomas Winkler /* 29ca9050bSTomas Winkler * 39ca9050bSTomas Winkler * Intel Management Engine Interface (Intel MEI) Linux driver 49ca9050bSTomas Winkler * Copyright (c) 2003-2012, Intel Corporation. 59ca9050bSTomas Winkler * 69ca9050bSTomas Winkler * This program is free software; you can redistribute it and/or modify it 79ca9050bSTomas Winkler * under the terms and conditions of the GNU General Public License, 89ca9050bSTomas Winkler * version 2, as published by the Free Software Foundation. 99ca9050bSTomas Winkler * 109ca9050bSTomas Winkler * This program is distributed in the hope it will be useful, but WITHOUT 119ca9050bSTomas Winkler * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 129ca9050bSTomas Winkler * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 139ca9050bSTomas Winkler * more details. 149ca9050bSTomas Winkler * 159ca9050bSTomas Winkler */ 169ca9050bSTomas Winkler 179ca9050bSTomas Winkler #include <linux/sched.h> 189ca9050bSTomas Winkler #include <linux/wait.h> 199ca9050bSTomas Winkler #include <linux/delay.h> 201f180359STomas Winkler #include <linux/slab.h> 2104bb139aSTomas Winkler #include <linux/pm_runtime.h> 229ca9050bSTomas Winkler 239ca9050bSTomas Winkler #include <linux/mei.h> 249ca9050bSTomas Winkler 259ca9050bSTomas Winkler #include "mei_dev.h" 269ca9050bSTomas Winkler #include "hbm.h" 2790e0b5f1STomas Winkler #include "client.h" 2890e0b5f1STomas Winkler 2990e0b5f1STomas Winkler /** 3079563db9STomas Winkler * mei_me_cl_init - initialize me client 3179563db9STomas Winkler * 3279563db9STomas Winkler * @me_cl: me client 3379563db9STomas Winkler */ 3479563db9STomas Winkler void mei_me_cl_init(struct mei_me_client *me_cl) 3579563db9STomas Winkler { 3679563db9STomas Winkler INIT_LIST_HEAD(&me_cl->list); 3779563db9STomas Winkler kref_init(&me_cl->refcnt); 3879563db9STomas Winkler } 3979563db9STomas Winkler 4079563db9STomas Winkler /** 4179563db9STomas Winkler * mei_me_cl_get - increases me client refcount 4279563db9STomas Winkler * 4379563db9STomas Winkler * @me_cl: me client 4479563db9STomas Winkler * 4579563db9STomas Winkler * Locking: called under "dev->device_lock" lock 4679563db9STomas Winkler * 4779563db9STomas Winkler * Return: me client or NULL 4879563db9STomas Winkler */ 4979563db9STomas Winkler struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl) 5079563db9STomas Winkler { 51b7d88514STomas Winkler if (me_cl && kref_get_unless_zero(&me_cl->refcnt)) 5279563db9STomas Winkler return me_cl; 53b7d88514STomas Winkler 54b7d88514STomas Winkler return NULL; 5579563db9STomas Winkler } 5679563db9STomas Winkler 5779563db9STomas Winkler /** 58b7d88514STomas Winkler * mei_me_cl_release - free me client 5979563db9STomas Winkler * 6079563db9STomas Winkler * Locking: called under "dev->device_lock" lock 6179563db9STomas Winkler * 6279563db9STomas Winkler * @ref: me_client refcount 6379563db9STomas Winkler */ 6479563db9STomas Winkler static void mei_me_cl_release(struct kref *ref) 6579563db9STomas Winkler { 6679563db9STomas Winkler struct mei_me_client *me_cl = 6779563db9STomas Winkler container_of(ref, struct mei_me_client, refcnt); 68b7d88514STomas Winkler 6979563db9STomas Winkler kfree(me_cl); 7079563db9STomas Winkler } 71b7d88514STomas Winkler 7279563db9STomas Winkler /** 7379563db9STomas Winkler * mei_me_cl_put - decrease me client refcount and free client if necessary 7479563db9STomas Winkler * 7579563db9STomas Winkler * Locking: called under "dev->device_lock" lock 7679563db9STomas Winkler * 7779563db9STomas Winkler * @me_cl: me client 7879563db9STomas Winkler */ 7979563db9STomas Winkler void mei_me_cl_put(struct mei_me_client *me_cl) 8079563db9STomas Winkler { 8179563db9STomas Winkler if (me_cl) 8279563db9STomas Winkler kref_put(&me_cl->refcnt, mei_me_cl_release); 8379563db9STomas Winkler } 8479563db9STomas Winkler 8579563db9STomas Winkler /** 86d49ed64aSAlexander Usyskin * __mei_me_cl_del - delete me client from the list and decrease 87b7d88514STomas Winkler * reference counter 88b7d88514STomas Winkler * 89b7d88514STomas Winkler * @dev: mei device 90b7d88514STomas Winkler * @me_cl: me client 91b7d88514STomas Winkler * 92b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 93b7d88514STomas Winkler */ 94b7d88514STomas Winkler static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) 95b7d88514STomas Winkler { 96b7d88514STomas Winkler if (!me_cl) 97b7d88514STomas Winkler return; 98b7d88514STomas Winkler 99d49ed64aSAlexander Usyskin list_del_init(&me_cl->list); 100b7d88514STomas Winkler mei_me_cl_put(me_cl); 101b7d88514STomas Winkler } 102b7d88514STomas Winkler 103b7d88514STomas Winkler /** 104d49ed64aSAlexander Usyskin * mei_me_cl_del - delete me client from the list and decrease 105d49ed64aSAlexander Usyskin * reference counter 106d49ed64aSAlexander Usyskin * 107d49ed64aSAlexander Usyskin * @dev: mei device 108d49ed64aSAlexander Usyskin * @me_cl: me client 109d49ed64aSAlexander Usyskin */ 110d49ed64aSAlexander Usyskin void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) 111d49ed64aSAlexander Usyskin { 112d49ed64aSAlexander Usyskin down_write(&dev->me_clients_rwsem); 113d49ed64aSAlexander Usyskin __mei_me_cl_del(dev, me_cl); 114d49ed64aSAlexander Usyskin up_write(&dev->me_clients_rwsem); 115d49ed64aSAlexander Usyskin } 116d49ed64aSAlexander Usyskin 117d49ed64aSAlexander Usyskin /** 118b7d88514STomas Winkler * mei_me_cl_add - add me client to the list 119b7d88514STomas Winkler * 120b7d88514STomas Winkler * @dev: mei device 121b7d88514STomas Winkler * @me_cl: me client 122b7d88514STomas Winkler */ 123b7d88514STomas Winkler void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl) 124b7d88514STomas Winkler { 125b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 126b7d88514STomas Winkler list_add(&me_cl->list, &dev->me_clients); 127b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 128b7d88514STomas Winkler } 129b7d88514STomas Winkler 130b7d88514STomas Winkler /** 131b7d88514STomas Winkler * __mei_me_cl_by_uuid - locate me client by uuid 132b7d88514STomas Winkler * increases ref count 133b7d88514STomas Winkler * 134b7d88514STomas Winkler * @dev: mei device 135b7d88514STomas Winkler * @uuid: me client uuid 136b7d88514STomas Winkler * 137b7d88514STomas Winkler * Return: me client or NULL if not found 138b7d88514STomas Winkler * 139b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 140b7d88514STomas Winkler */ 141b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev, 142b7d88514STomas Winkler const uuid_le *uuid) 143b7d88514STomas Winkler { 144b7d88514STomas Winkler struct mei_me_client *me_cl; 145b7d88514STomas Winkler const uuid_le *pn; 146b7d88514STomas Winkler 147b7d88514STomas Winkler WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 148b7d88514STomas Winkler 149b7d88514STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) { 150b7d88514STomas Winkler pn = &me_cl->props.protocol_name; 151b7d88514STomas Winkler if (uuid_le_cmp(*uuid, *pn) == 0) 152b7d88514STomas Winkler return mei_me_cl_get(me_cl); 153b7d88514STomas Winkler } 154b7d88514STomas Winkler 155b7d88514STomas Winkler return NULL; 156b7d88514STomas Winkler } 157b7d88514STomas Winkler 158b7d88514STomas Winkler /** 159a8605ea2SAlexander Usyskin * mei_me_cl_by_uuid - locate me client by uuid 16079563db9STomas Winkler * increases ref count 16190e0b5f1STomas Winkler * 16290e0b5f1STomas Winkler * @dev: mei device 163a8605ea2SAlexander Usyskin * @uuid: me client uuid 164a27a76d3SAlexander Usyskin * 165a8605ea2SAlexander Usyskin * Return: me client or NULL if not found 166b7d88514STomas Winkler * 167b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 16890e0b5f1STomas Winkler */ 169b7d88514STomas Winkler struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, 170d320832fSTomas Winkler const uuid_le *uuid) 17190e0b5f1STomas Winkler { 1725ca2d388STomas Winkler struct mei_me_client *me_cl; 17390e0b5f1STomas Winkler 174b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 175b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid(dev, uuid); 176b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 17790e0b5f1STomas Winkler 178b7d88514STomas Winkler return me_cl; 17990e0b5f1STomas Winkler } 18090e0b5f1STomas Winkler 18190e0b5f1STomas Winkler /** 182a8605ea2SAlexander Usyskin * mei_me_cl_by_id - locate me client by client id 18379563db9STomas Winkler * increases ref count 18490e0b5f1STomas Winkler * 18590e0b5f1STomas Winkler * @dev: the device structure 18690e0b5f1STomas Winkler * @client_id: me client id 18790e0b5f1STomas Winkler * 188a8605ea2SAlexander Usyskin * Return: me client or NULL if not found 189b7d88514STomas Winkler * 190b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 19190e0b5f1STomas Winkler */ 192d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 19390e0b5f1STomas Winkler { 194a27a76d3SAlexander Usyskin 195b7d88514STomas Winkler struct mei_me_client *__me_cl, *me_cl = NULL; 196a27a76d3SAlexander Usyskin 197b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 198b7d88514STomas Winkler list_for_each_entry(__me_cl, &dev->me_clients, list) { 199b7d88514STomas Winkler if (__me_cl->client_id == client_id) { 200b7d88514STomas Winkler me_cl = mei_me_cl_get(__me_cl); 201b7d88514STomas Winkler break; 202b7d88514STomas Winkler } 203b7d88514STomas Winkler } 204b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 205b7d88514STomas Winkler 206b7d88514STomas Winkler return me_cl; 207b7d88514STomas Winkler } 208b7d88514STomas Winkler 209b7d88514STomas Winkler /** 210b7d88514STomas Winkler * __mei_me_cl_by_uuid_id - locate me client by client id and uuid 211b7d88514STomas Winkler * increases ref count 212b7d88514STomas Winkler * 213b7d88514STomas Winkler * @dev: the device structure 214b7d88514STomas Winkler * @uuid: me client uuid 215b7d88514STomas Winkler * @client_id: me client id 216b7d88514STomas Winkler * 217b7d88514STomas Winkler * Return: me client or null if not found 218b7d88514STomas Winkler * 219b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 220b7d88514STomas Winkler */ 221b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev, 222b7d88514STomas Winkler const uuid_le *uuid, u8 client_id) 223b7d88514STomas Winkler { 224b7d88514STomas Winkler struct mei_me_client *me_cl; 225b7d88514STomas Winkler const uuid_le *pn; 226b7d88514STomas Winkler 227b7d88514STomas Winkler WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 228b7d88514STomas Winkler 229b7d88514STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) { 230b7d88514STomas Winkler pn = &me_cl->props.protocol_name; 231b7d88514STomas Winkler if (uuid_le_cmp(*uuid, *pn) == 0 && 232b7d88514STomas Winkler me_cl->client_id == client_id) 23379563db9STomas Winkler return mei_me_cl_get(me_cl); 234b7d88514STomas Winkler } 23579563db9STomas Winkler 236d320832fSTomas Winkler return NULL; 23790e0b5f1STomas Winkler } 2389ca9050bSTomas Winkler 239b7d88514STomas Winkler 240a8605ea2SAlexander Usyskin /** 241a8605ea2SAlexander Usyskin * mei_me_cl_by_uuid_id - locate me client by client id and uuid 24279563db9STomas Winkler * increases ref count 243a8605ea2SAlexander Usyskin * 244a8605ea2SAlexander Usyskin * @dev: the device structure 245a8605ea2SAlexander Usyskin * @uuid: me client uuid 246a8605ea2SAlexander Usyskin * @client_id: me client id 247a8605ea2SAlexander Usyskin * 248b7d88514STomas Winkler * Return: me client or null if not found 249a8605ea2SAlexander Usyskin */ 250d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, 251d880f329STomas Winkler const uuid_le *uuid, u8 client_id) 252d880f329STomas Winkler { 253d880f329STomas Winkler struct mei_me_client *me_cl; 254d880f329STomas Winkler 255b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 256b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id); 257b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 25879563db9STomas Winkler 259b7d88514STomas Winkler return me_cl; 260d880f329STomas Winkler } 261d880f329STomas Winkler 26225ca6472STomas Winkler /** 26379563db9STomas Winkler * mei_me_cl_rm_by_uuid - remove all me clients matching uuid 26425ca6472STomas Winkler * 26525ca6472STomas Winkler * @dev: the device structure 26625ca6472STomas Winkler * @uuid: me client uuid 26779563db9STomas Winkler * 26879563db9STomas Winkler * Locking: called under "dev->device_lock" lock 26925ca6472STomas Winkler */ 27079563db9STomas Winkler void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) 27125ca6472STomas Winkler { 272b7d88514STomas Winkler struct mei_me_client *me_cl; 27325ca6472STomas Winkler 27479563db9STomas Winkler dev_dbg(dev->dev, "remove %pUl\n", uuid); 275b7d88514STomas Winkler 276b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 277b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid(dev, uuid); 278b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 279b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 28079563db9STomas Winkler } 28179563db9STomas Winkler 28279563db9STomas Winkler /** 28379563db9STomas Winkler * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id 28479563db9STomas Winkler * 28579563db9STomas Winkler * @dev: the device structure 28679563db9STomas Winkler * @uuid: me client uuid 28779563db9STomas Winkler * @id: me client id 28879563db9STomas Winkler * 28979563db9STomas Winkler * Locking: called under "dev->device_lock" lock 29079563db9STomas Winkler */ 29179563db9STomas Winkler void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id) 29279563db9STomas Winkler { 293b7d88514STomas Winkler struct mei_me_client *me_cl; 29479563db9STomas Winkler 29579563db9STomas Winkler dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id); 296b7d88514STomas Winkler 297b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 298b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id); 299b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 300b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 30125ca6472STomas Winkler } 30279563db9STomas Winkler 30379563db9STomas Winkler /** 30479563db9STomas Winkler * mei_me_cl_rm_all - remove all me clients 30579563db9STomas Winkler * 30679563db9STomas Winkler * @dev: the device structure 30779563db9STomas Winkler * 30879563db9STomas Winkler * Locking: called under "dev->device_lock" lock 30979563db9STomas Winkler */ 31079563db9STomas Winkler void mei_me_cl_rm_all(struct mei_device *dev) 31179563db9STomas Winkler { 31279563db9STomas Winkler struct mei_me_client *me_cl, *next; 31379563db9STomas Winkler 314b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 31579563db9STomas Winkler list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) 316b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 317b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 31825ca6472STomas Winkler } 31925ca6472STomas Winkler 3209ca9050bSTomas Winkler /** 321cc99ecfdSTomas Winkler * mei_cl_cmp_id - tells if the clients are the same 322cc99ecfdSTomas Winkler * 323cc99ecfdSTomas Winkler * @cl1: host client 1 324cc99ecfdSTomas Winkler * @cl2: host client 2 325cc99ecfdSTomas Winkler * 326a8605ea2SAlexander Usyskin * Return: true - if the clients has same host and me ids 327cc99ecfdSTomas Winkler * false - otherwise 328cc99ecfdSTomas Winkler */ 329cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, 330cc99ecfdSTomas Winkler const struct mei_cl *cl2) 331cc99ecfdSTomas Winkler { 332cc99ecfdSTomas Winkler return cl1 && cl2 && 333cc99ecfdSTomas Winkler (cl1->host_client_id == cl2->host_client_id) && 334d49ed64aSAlexander Usyskin (mei_cl_me_id(cl1) == mei_cl_me_id(cl2)); 335cc99ecfdSTomas Winkler } 336cc99ecfdSTomas Winkler 337cc99ecfdSTomas Winkler /** 3389ca9050bSTomas Winkler * mei_io_cb_free - free mei_cb_private related memory 3399ca9050bSTomas Winkler * 3409ca9050bSTomas Winkler * @cb: mei callback struct 3419ca9050bSTomas Winkler */ 3429ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb) 3439ca9050bSTomas Winkler { 3449ca9050bSTomas Winkler if (cb == NULL) 3459ca9050bSTomas Winkler return; 3469ca9050bSTomas Winkler 347928fa666STomas Winkler list_del(&cb->list); 3485db7514dSTomas Winkler kfree(cb->buf.data); 3499ca9050bSTomas Winkler kfree(cb); 3509ca9050bSTomas Winkler } 3519ca9050bSTomas Winkler 3529ca9050bSTomas Winkler /** 3539ca9050bSTomas Winkler * mei_io_cb_init - allocate and initialize io callback 3549ca9050bSTomas Winkler * 355a8605ea2SAlexander Usyskin * @cl: mei client 356bca67d68STomas Winkler * @type: operation type 357393b148fSMasanari Iida * @fp: pointer to file structure 3589ca9050bSTomas Winkler * 359a8605ea2SAlexander Usyskin * Return: mei_cl_cb pointer or NULL; 3609ca9050bSTomas Winkler */ 3613030dc05STomas Winkler static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, 3623030dc05STomas Winkler enum mei_cb_file_ops type, 363f23e2cc4STomas Winkler const struct file *fp) 3649ca9050bSTomas Winkler { 3659ca9050bSTomas Winkler struct mei_cl_cb *cb; 3669ca9050bSTomas Winkler 3679ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 3689ca9050bSTomas Winkler if (!cb) 3699ca9050bSTomas Winkler return NULL; 3709ca9050bSTomas Winkler 371928fa666STomas Winkler INIT_LIST_HEAD(&cb->list); 37262e8e6adSTomas Winkler cb->fp = fp; 3739ca9050bSTomas Winkler cb->cl = cl; 3749ca9050bSTomas Winkler cb->buf_idx = 0; 375bca67d68STomas Winkler cb->fop_type = type; 3769ca9050bSTomas Winkler return cb; 3779ca9050bSTomas Winkler } 3789ca9050bSTomas Winkler 3799ca9050bSTomas Winkler /** 380928fa666STomas Winkler * __mei_io_list_flush - removes and frees cbs belonging to cl. 381928fa666STomas Winkler * 382962ff7bcSAlexander Usyskin * @head: an instance of our list structure 383928fa666STomas Winkler * @cl: host client, can be NULL for flushing the whole list 384928fa666STomas Winkler * @free: whether to free the cbs 385928fa666STomas Winkler */ 386962ff7bcSAlexander Usyskin static void __mei_io_list_flush(struct list_head *head, 387928fa666STomas Winkler struct mei_cl *cl, bool free) 388928fa666STomas Winkler { 389928fa666STomas Winkler struct mei_cl_cb *cb, *next; 390928fa666STomas Winkler 391928fa666STomas Winkler /* enable removing everything if no cl is specified */ 392962ff7bcSAlexander Usyskin list_for_each_entry_safe(cb, next, head, list) { 393928fa666STomas Winkler if (!cl || mei_cl_cmp_id(cl, cb->cl)) { 394928fa666STomas Winkler list_del_init(&cb->list); 395928fa666STomas Winkler if (free) 396928fa666STomas Winkler mei_io_cb_free(cb); 397928fa666STomas Winkler } 398928fa666STomas Winkler } 399928fa666STomas Winkler } 400928fa666STomas Winkler 401928fa666STomas Winkler /** 402928fa666STomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 403928fa666STomas Winkler * 404962ff7bcSAlexander Usyskin * @head: An instance of our list structure 405928fa666STomas Winkler * @cl: host client 406928fa666STomas Winkler */ 407962ff7bcSAlexander Usyskin static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl) 408928fa666STomas Winkler { 409962ff7bcSAlexander Usyskin __mei_io_list_flush(head, cl, false); 410928fa666STomas Winkler } 411928fa666STomas Winkler 412928fa666STomas Winkler /** 413928fa666STomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 414928fa666STomas Winkler * 415962ff7bcSAlexander Usyskin * @head: An instance of our list structure 416928fa666STomas Winkler * @cl: host client 417928fa666STomas Winkler */ 418962ff7bcSAlexander Usyskin static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl) 419928fa666STomas Winkler { 420962ff7bcSAlexander Usyskin __mei_io_list_flush(head, cl, true); 421928fa666STomas Winkler } 422928fa666STomas Winkler 423928fa666STomas Winkler /** 424bca67d68STomas Winkler * mei_cl_alloc_cb - a convenient wrapper for allocating read cb 425bca67d68STomas Winkler * 426bca67d68STomas Winkler * @cl: host client 427bca67d68STomas Winkler * @length: size of the buffer 428967b274eSAlexander Usyskin * @fop_type: operation type 429bca67d68STomas Winkler * @fp: associated file pointer (might be NULL) 430bca67d68STomas Winkler * 431bca67d68STomas Winkler * Return: cb on success and NULL on failure 432bca67d68STomas Winkler */ 433bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, 4343030dc05STomas Winkler enum mei_cb_file_ops fop_type, 435f23e2cc4STomas Winkler const struct file *fp) 436bca67d68STomas Winkler { 437bca67d68STomas Winkler struct mei_cl_cb *cb; 438bca67d68STomas Winkler 4393030dc05STomas Winkler cb = mei_io_cb_init(cl, fop_type, fp); 440bca67d68STomas Winkler if (!cb) 441bca67d68STomas Winkler return NULL; 442bca67d68STomas Winkler 443aab3b1a3SAlexander Usyskin if (length == 0) 444aab3b1a3SAlexander Usyskin return cb; 445aab3b1a3SAlexander Usyskin 446aab3b1a3SAlexander Usyskin cb->buf.data = kmalloc(length, GFP_KERNEL); 447aab3b1a3SAlexander Usyskin if (!cb->buf.data) { 448bca67d68STomas Winkler mei_io_cb_free(cb); 449bca67d68STomas Winkler return NULL; 450bca67d68STomas Winkler } 451aab3b1a3SAlexander Usyskin cb->buf.size = length; 452bca67d68STomas Winkler 453bca67d68STomas Winkler return cb; 454bca67d68STomas Winkler } 455bca67d68STomas Winkler 456bca67d68STomas Winkler /** 4573030dc05STomas Winkler * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating 4583030dc05STomas Winkler * and enqueuing of the control commands cb 4593030dc05STomas Winkler * 4603030dc05STomas Winkler * @cl: host client 4613030dc05STomas Winkler * @length: size of the buffer 462967b274eSAlexander Usyskin * @fop_type: operation type 4633030dc05STomas Winkler * @fp: associated file pointer (might be NULL) 4643030dc05STomas Winkler * 4653030dc05STomas Winkler * Return: cb on success and NULL on failure 4663030dc05STomas Winkler * Locking: called under "dev->device_lock" lock 4673030dc05STomas Winkler */ 4683030dc05STomas Winkler struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, 4693030dc05STomas Winkler enum mei_cb_file_ops fop_type, 4703030dc05STomas Winkler const struct file *fp) 4713030dc05STomas Winkler { 4723030dc05STomas Winkler struct mei_cl_cb *cb; 4733030dc05STomas Winkler 4743030dc05STomas Winkler /* for RX always allocate at least client's mtu */ 4753030dc05STomas Winkler if (length) 4763030dc05STomas Winkler length = max_t(size_t, length, mei_cl_mtu(cl)); 4773030dc05STomas Winkler 4783030dc05STomas Winkler cb = mei_cl_alloc_cb(cl, length, fop_type, fp); 4793030dc05STomas Winkler if (!cb) 4803030dc05STomas Winkler return NULL; 4813030dc05STomas Winkler 482962ff7bcSAlexander Usyskin list_add_tail(&cb->list, &cl->dev->ctrl_wr_list); 4833030dc05STomas Winkler return cb; 4843030dc05STomas Winkler } 4853030dc05STomas Winkler 4863030dc05STomas Winkler /** 487a9bed610STomas Winkler * mei_cl_read_cb - find this cl's callback in the read list 488a9bed610STomas Winkler * for a specific file 489a9bed610STomas Winkler * 490a9bed610STomas Winkler * @cl: host client 491a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 492a9bed610STomas Winkler * 493a9bed610STomas Winkler * Return: cb on success, NULL if cb is not found 494a9bed610STomas Winkler */ 495a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) 496a9bed610STomas Winkler { 497a9bed610STomas Winkler struct mei_cl_cb *cb; 498a9bed610STomas Winkler 499a9bed610STomas Winkler list_for_each_entry(cb, &cl->rd_completed, list) 50062e8e6adSTomas Winkler if (!fp || fp == cb->fp) 501a9bed610STomas Winkler return cb; 502a9bed610STomas Winkler 503a9bed610STomas Winkler return NULL; 504a9bed610STomas Winkler } 505a9bed610STomas Winkler 506a9bed610STomas Winkler /** 507a9bed610STomas Winkler * mei_cl_read_cb_flush - free client's read pending and completed cbs 508a9bed610STomas Winkler * for a specific file 509a9bed610STomas Winkler * 510a9bed610STomas Winkler * @cl: host client 511a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 512a9bed610STomas Winkler */ 513a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) 514a9bed610STomas Winkler { 515a9bed610STomas Winkler struct mei_cl_cb *cb, *next; 516a9bed610STomas Winkler 517a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_completed, list) 51862e8e6adSTomas Winkler if (!fp || fp == cb->fp) 519a9bed610STomas Winkler mei_io_cb_free(cb); 520a9bed610STomas Winkler 521a9bed610STomas Winkler 522a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_pending, list) 52362e8e6adSTomas Winkler if (!fp || fp == cb->fp) 524a9bed610STomas Winkler mei_io_cb_free(cb); 525a9bed610STomas Winkler } 526a9bed610STomas Winkler 527a9bed610STomas Winkler /** 5289ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 5299ca9050bSTomas Winkler * 5309ca9050bSTomas Winkler * @cl: host client 531a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 532ce23139cSAlexander Usyskin * 533ce23139cSAlexander Usyskin * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. 5349ca9050bSTomas Winkler */ 535a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) 5369ca9050bSTomas Winkler { 537c0abffbdSAlexander Usyskin struct mei_device *dev; 538c0abffbdSAlexander Usyskin 53990e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5409ca9050bSTomas Winkler return -EINVAL; 5419ca9050bSTomas Winkler 542c0abffbdSAlexander Usyskin dev = cl->dev; 543c0abffbdSAlexander Usyskin 544c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 545cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 546cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 5479ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 5489ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 549a9bed610STomas Winkler 550a9bed610STomas Winkler mei_cl_read_cb_flush(cl, fp); 551a9bed610STomas Winkler 5529ca9050bSTomas Winkler return 0; 5539ca9050bSTomas Winkler } 5549ca9050bSTomas Winkler 5559ca9050bSTomas Winkler 5569ca9050bSTomas Winkler /** 55783ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 5589ca9050bSTomas Winkler * 5599ca9050bSTomas Winkler * @cl: host client to be initialized 5609ca9050bSTomas Winkler * @dev: mei device 5619ca9050bSTomas Winkler */ 5629ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 5639ca9050bSTomas Winkler { 5649ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 5659ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 5669ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 5679ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 568b38a362fSTomas Winkler init_waitqueue_head(&cl->ev_wait); 569a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_completed); 570a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_pending); 5719ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 5729ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 573bd47b526SAlexander Usyskin cl->state = MEI_FILE_UNINITIALIZED; 5749ca9050bSTomas Winkler cl->dev = dev; 5759ca9050bSTomas Winkler } 5769ca9050bSTomas Winkler 5779ca9050bSTomas Winkler /** 5789ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 5799ca9050bSTomas Winkler * 5809ca9050bSTomas Winkler * @dev: mei device 581a8605ea2SAlexander Usyskin * Return: The allocated file or NULL on failure 5829ca9050bSTomas Winkler */ 5839ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 5849ca9050bSTomas Winkler { 5859ca9050bSTomas Winkler struct mei_cl *cl; 5869ca9050bSTomas Winkler 5879ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 5889ca9050bSTomas Winkler if (!cl) 5899ca9050bSTomas Winkler return NULL; 5909ca9050bSTomas Winkler 5919ca9050bSTomas Winkler mei_cl_init(cl, dev); 5929ca9050bSTomas Winkler 5939ca9050bSTomas Winkler return cl; 5949ca9050bSTomas Winkler } 5959ca9050bSTomas Winkler 59690e0b5f1STomas Winkler /** 5973908be6fSAlexander Usyskin * mei_cl_link - allocate host id in the host map 5989ca9050bSTomas Winkler * 5993908be6fSAlexander Usyskin * @cl: host client 600393b148fSMasanari Iida * 601a8605ea2SAlexander Usyskin * Return: 0 on success 6029ca9050bSTomas Winkler * -EINVAL on incorrect values 60303b8d341STomas Winkler * -EMFILE if open count exceeded. 6049ca9050bSTomas Winkler */ 6057851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl) 6069ca9050bSTomas Winkler { 60790e0b5f1STomas Winkler struct mei_device *dev; 60822f96a0eSTomas Winkler long open_handle_count; 6097851e008SAlexander Usyskin int id; 6109ca9050bSTomas Winkler 611781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 6129ca9050bSTomas Winkler return -EINVAL; 6139ca9050bSTomas Winkler 61490e0b5f1STomas Winkler dev = cl->dev; 61590e0b5f1STomas Winkler 6167851e008SAlexander Usyskin id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); 617781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 6182bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 619e036cc57STomas Winkler return -EMFILE; 620e036cc57STomas Winkler } 621e036cc57STomas Winkler 62222f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 62322f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6242bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 625e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 626e036cc57STomas Winkler return -EMFILE; 6279ca9050bSTomas Winkler } 628781d0d89STomas Winkler 629781d0d89STomas Winkler dev->open_handle_count++; 630781d0d89STomas Winkler 631781d0d89STomas Winkler cl->host_client_id = id; 632781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 633781d0d89STomas Winkler 634781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 635781d0d89STomas Winkler 636781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 637781d0d89STomas Winkler 638c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 639781d0d89STomas Winkler return 0; 640781d0d89STomas Winkler } 641781d0d89STomas Winkler 6429ca9050bSTomas Winkler /** 643d49ed64aSAlexander Usyskin * mei_cl_unlink - remove host client from the list 6449ca9050bSTomas Winkler * 645393b148fSMasanari Iida * @cl: host client 646ce23139cSAlexander Usyskin * 647ce23139cSAlexander Usyskin * Return: always 0 6489ca9050bSTomas Winkler */ 64990e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 6509ca9050bSTomas Winkler { 65190e0b5f1STomas Winkler struct mei_device *dev; 65290e0b5f1STomas Winkler 653781d0d89STomas Winkler /* don't shout on error exit path */ 654781d0d89STomas Winkler if (!cl) 655781d0d89STomas Winkler return 0; 656781d0d89STomas Winkler 657fdd9b865SAlexander Usyskin /* amthif might not be initialized */ 6588e9a4a9aSTomas Winkler if (!cl->dev) 6598e9a4a9aSTomas Winkler return 0; 66090e0b5f1STomas Winkler 66190e0b5f1STomas Winkler dev = cl->dev; 66290e0b5f1STomas Winkler 663a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 664a14c44d8STomas Winkler 66522f96a0eSTomas Winkler if (dev->open_handle_count > 0) 66622f96a0eSTomas Winkler dev->open_handle_count--; 66722f96a0eSTomas Winkler 66822f96a0eSTomas Winkler /* never clear the 0 bit */ 66922f96a0eSTomas Winkler if (cl->host_client_id) 67022f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 67122f96a0eSTomas Winkler 67222f96a0eSTomas Winkler list_del_init(&cl->link); 67322f96a0eSTomas Winkler 674bd47b526SAlexander Usyskin cl->state = MEI_FILE_UNINITIALIZED; 6757c7a6077SAlexander Usyskin cl->writing_state = MEI_IDLE; 6767c7a6077SAlexander Usyskin 6777c7a6077SAlexander Usyskin WARN_ON(!list_empty(&cl->rd_completed) || 6787c7a6077SAlexander Usyskin !list_empty(&cl->rd_pending) || 6797c7a6077SAlexander Usyskin !list_empty(&cl->link)); 68022f96a0eSTomas Winkler 68190e0b5f1STomas Winkler return 0; 6829ca9050bSTomas Winkler } 6839ca9050bSTomas Winkler 684025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev) 6859ca9050bSTomas Winkler { 6869ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 6876adb8efbSTomas Winkler dev->reset_count = 0; 68804bb139aSTomas Winkler 689025fb792SAlexander Usyskin schedule_work(&dev->bus_rescan_work); 6906009595aSTomas Winkler 6912bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 6922bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 693d5f8e166SAlexander Usyskin pm_request_autosuspend(dev->dev); 6949ca9050bSTomas Winkler } 6959ca9050bSTomas Winkler 6966aae48ffSTomas Winkler /** 697a8605ea2SAlexander Usyskin * mei_hbuf_acquire - try to acquire host buffer 6986aae48ffSTomas Winkler * 6996aae48ffSTomas Winkler * @dev: the device structure 700a8605ea2SAlexander Usyskin * Return: true if host buffer was acquired 7016aae48ffSTomas Winkler */ 7026aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 7036aae48ffSTomas Winkler { 70404bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 7053dc196eaSAlexander Usyskin mei_pg_in_transition(dev)) { 7062bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 70704bb139aSTomas Winkler return false; 70804bb139aSTomas Winkler } 70904bb139aSTomas Winkler 7106aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 7112bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 7126aae48ffSTomas Winkler return false; 7136aae48ffSTomas Winkler } 7146aae48ffSTomas Winkler 7156aae48ffSTomas Winkler dev->hbuf_is_ready = false; 7166aae48ffSTomas Winkler 7176aae48ffSTomas Winkler return true; 7186aae48ffSTomas Winkler } 7199ca9050bSTomas Winkler 7209ca9050bSTomas Winkler /** 721a4307fe4SAlexander Usyskin * mei_cl_wake_all - wake up readers, writers and event waiters so 722a4307fe4SAlexander Usyskin * they can be interrupted 723a4307fe4SAlexander Usyskin * 724a4307fe4SAlexander Usyskin * @cl: host client 725a4307fe4SAlexander Usyskin */ 726a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl) 727a4307fe4SAlexander Usyskin { 728a4307fe4SAlexander Usyskin struct mei_device *dev = cl->dev; 729a4307fe4SAlexander Usyskin 730a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 731a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->rx_wait)) { 732a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 733a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->rx_wait); 734a4307fe4SAlexander Usyskin } 735a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 736a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 737a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 738a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->tx_wait); 739a4307fe4SAlexander Usyskin } 740a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 741a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->ev_wait)) { 742a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); 743a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->ev_wait); 744a4307fe4SAlexander Usyskin } 7457ff4bdd4SAlexander Usyskin /* synchronized under device mutex */ 7467ff4bdd4SAlexander Usyskin if (waitqueue_active(&cl->wait)) { 7477ff4bdd4SAlexander Usyskin cl_dbg(dev, cl, "Waking up ctrl write clients!\n"); 74869f1804aSAlexander Usyskin wake_up(&cl->wait); 7497ff4bdd4SAlexander Usyskin } 750a4307fe4SAlexander Usyskin } 751a4307fe4SAlexander Usyskin 752a4307fe4SAlexander Usyskin /** 7533c666182STomas Winkler * mei_cl_set_disconnected - set disconnected state and clear 7543c666182STomas Winkler * associated states and resources 7553c666182STomas Winkler * 7563c666182STomas Winkler * @cl: host client 7573c666182STomas Winkler */ 758669c256cSAlexander Usyskin static void mei_cl_set_disconnected(struct mei_cl *cl) 7593c666182STomas Winkler { 7603c666182STomas Winkler struct mei_device *dev = cl->dev; 7613c666182STomas Winkler 7623c666182STomas Winkler if (cl->state == MEI_FILE_DISCONNECTED || 763bd47b526SAlexander Usyskin cl->state <= MEI_FILE_INITIALIZING) 7643c666182STomas Winkler return; 7653c666182STomas Winkler 7663c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 767a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_list, cl); 768a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_waiting_list, cl); 7693c666182STomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 7703c666182STomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 7716537ae2fSTomas Winkler mei_io_list_free(&dev->amthif_cmd_list, cl); 772a4307fe4SAlexander Usyskin mei_cl_wake_all(cl); 77346978adaSAlexander Usyskin cl->rx_flow_ctrl_creds = 0; 7744034b81bSTomas Winkler cl->tx_flow_ctrl_creds = 0; 7753c666182STomas Winkler cl->timer_count = 0; 776d49ed64aSAlexander Usyskin 7775d882460SAlexander Usyskin mei_cl_bus_module_put(cl); 7785d882460SAlexander Usyskin 779a03d77f6SAlexander Usyskin if (!cl->me_cl) 780a03d77f6SAlexander Usyskin return; 781a03d77f6SAlexander Usyskin 782a03d77f6SAlexander Usyskin if (!WARN_ON(cl->me_cl->connect_count == 0)) 783a03d77f6SAlexander Usyskin cl->me_cl->connect_count--; 784a03d77f6SAlexander Usyskin 785c241e9b1SAlexander Usyskin if (cl->me_cl->connect_count == 0) 7864034b81bSTomas Winkler cl->me_cl->tx_flow_ctrl_creds = 0; 787c241e9b1SAlexander Usyskin 788d49ed64aSAlexander Usyskin mei_me_cl_put(cl->me_cl); 789d49ed64aSAlexander Usyskin cl->me_cl = NULL; 7903c666182STomas Winkler } 7913c666182STomas Winkler 792a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) 793a03d77f6SAlexander Usyskin { 7941df629efSAlexander Usyskin if (!mei_me_cl_get(me_cl)) 795a03d77f6SAlexander Usyskin return -ENOENT; 796a03d77f6SAlexander Usyskin 7971df629efSAlexander Usyskin /* only one connection is allowed for fixed address clients */ 7981df629efSAlexander Usyskin if (me_cl->props.fixed_address) { 7991df629efSAlexander Usyskin if (me_cl->connect_count) { 8001df629efSAlexander Usyskin mei_me_cl_put(me_cl); 8011df629efSAlexander Usyskin return -EBUSY; 8021df629efSAlexander Usyskin } 8031df629efSAlexander Usyskin } 8041df629efSAlexander Usyskin 8051df629efSAlexander Usyskin cl->me_cl = me_cl; 806a03d77f6SAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 807a03d77f6SAlexander Usyskin cl->me_cl->connect_count++; 808a03d77f6SAlexander Usyskin 809a03d77f6SAlexander Usyskin return 0; 810a03d77f6SAlexander Usyskin } 811a03d77f6SAlexander Usyskin 8123c666182STomas Winkler /* 8133c666182STomas Winkler * mei_cl_send_disconnect - send disconnect request 8143c666182STomas Winkler * 8153c666182STomas Winkler * @cl: host client 8163c666182STomas Winkler * @cb: callback block 8173c666182STomas Winkler * 8183c666182STomas Winkler * Return: 0, OK; otherwise, error. 8193c666182STomas Winkler */ 8203c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 8213c666182STomas Winkler { 8223c666182STomas Winkler struct mei_device *dev; 8233c666182STomas Winkler int ret; 8243c666182STomas Winkler 8253c666182STomas Winkler dev = cl->dev; 8263c666182STomas Winkler 8273c666182STomas Winkler ret = mei_hbm_cl_disconnect_req(dev, cl); 8283c666182STomas Winkler cl->status = ret; 8293c666182STomas Winkler if (ret) { 8303c666182STomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 8313c666182STomas Winkler return ret; 8323c666182STomas Winkler } 8333c666182STomas Winkler 834962ff7bcSAlexander Usyskin list_move_tail(&cb->list, &dev->ctrl_rd_list); 8353c666182STomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 8361892fc2eSAlexander Usyskin mei_schedule_stall_timer(dev); 8373c666182STomas Winkler 8383c666182STomas Winkler return 0; 8393c666182STomas Winkler } 8403c666182STomas Winkler 8413c666182STomas Winkler /** 8423c666182STomas Winkler * mei_cl_irq_disconnect - processes close related operation from 8433c666182STomas Winkler * interrupt thread context - send disconnect request 8443c666182STomas Winkler * 8453c666182STomas Winkler * @cl: client 8463c666182STomas Winkler * @cb: callback block. 8473c666182STomas Winkler * @cmpl_list: complete list. 8483c666182STomas Winkler * 8493c666182STomas Winkler * Return: 0, OK; otherwise, error. 8503c666182STomas Winkler */ 8513c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 852962ff7bcSAlexander Usyskin struct list_head *cmpl_list) 8533c666182STomas Winkler { 8543c666182STomas Winkler struct mei_device *dev = cl->dev; 8553c666182STomas Winkler u32 msg_slots; 8563c666182STomas Winkler int slots; 8573c666182STomas Winkler int ret; 8583c666182STomas Winkler 8593c666182STomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 8603c666182STomas Winkler slots = mei_hbuf_empty_slots(dev); 8613c666182STomas Winkler 8623c666182STomas Winkler if (slots < msg_slots) 8633c666182STomas Winkler return -EMSGSIZE; 8643c666182STomas Winkler 8653c666182STomas Winkler ret = mei_cl_send_disconnect(cl, cb); 8663c666182STomas Winkler if (ret) 867962ff7bcSAlexander Usyskin list_move_tail(&cb->list, cmpl_list); 8683c666182STomas Winkler 8693c666182STomas Winkler return ret; 8703c666182STomas Winkler } 8713c666182STomas Winkler 8723c666182STomas Winkler /** 87318901357SAlexander Usyskin * __mei_cl_disconnect - disconnect host client from the me one 87418901357SAlexander Usyskin * internal function runtime pm has to be already acquired 8759ca9050bSTomas Winkler * 87690e0b5f1STomas Winkler * @cl: host client 8779ca9050bSTomas Winkler * 878a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 8799ca9050bSTomas Winkler */ 88018901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl) 8819ca9050bSTomas Winkler { 88290e0b5f1STomas Winkler struct mei_device *dev; 8839ca9050bSTomas Winkler struct mei_cl_cb *cb; 884fe2f17ebSAlexander Usyskin int rets; 8859ca9050bSTomas Winkler 88690e0b5f1STomas Winkler dev = cl->dev; 88790e0b5f1STomas Winkler 8883c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTING; 8893c666182STomas Winkler 8903030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL); 8913030dc05STomas Winkler if (!cb) { 8923030dc05STomas Winkler rets = -ENOMEM; 8933c666182STomas Winkler goto out; 8943030dc05STomas Winkler } 8959ca9050bSTomas Winkler 8963c666182STomas Winkler if (mei_hbuf_acquire(dev)) { 8973c666182STomas Winkler rets = mei_cl_send_disconnect(cl, cb); 8983c666182STomas Winkler if (rets) { 8993c666182STomas Winkler cl_err(dev, cl, "failed to disconnect.\n"); 9003c666182STomas Winkler goto out; 9019ca9050bSTomas Winkler } 9023c666182STomas Winkler } 9033c666182STomas Winkler 9049ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 9057ff4bdd4SAlexander Usyskin wait_event_timeout(cl->wait, 9067ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REPLY || 9077ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECTED, 9089ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 9099ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 910fe2f17ebSAlexander Usyskin 9113c666182STomas Winkler rets = cl->status; 9127ff4bdd4SAlexander Usyskin if (cl->state != MEI_FILE_DISCONNECT_REPLY && 9137ff4bdd4SAlexander Usyskin cl->state != MEI_FILE_DISCONNECTED) { 914fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 915fe2f17ebSAlexander Usyskin rets = -ETIME; 9169ca9050bSTomas Winkler } 9179ca9050bSTomas Winkler 9183c666182STomas Winkler out: 9193c666182STomas Winkler /* we disconnect also on error */ 9203c666182STomas Winkler mei_cl_set_disconnected(cl); 9213c666182STomas Winkler if (!rets) 9223c666182STomas Winkler cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 9233c666182STomas Winkler 92418901357SAlexander Usyskin mei_io_cb_free(cb); 92518901357SAlexander Usyskin return rets; 92618901357SAlexander Usyskin } 92718901357SAlexander Usyskin 92818901357SAlexander Usyskin /** 92918901357SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 93018901357SAlexander Usyskin * 93118901357SAlexander Usyskin * @cl: host client 93218901357SAlexander Usyskin * 93318901357SAlexander Usyskin * Locking: called under "dev->device_lock" lock 93418901357SAlexander Usyskin * 93518901357SAlexander Usyskin * Return: 0 on success, <0 on failure. 93618901357SAlexander Usyskin */ 93718901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl) 93818901357SAlexander Usyskin { 93918901357SAlexander Usyskin struct mei_device *dev; 94018901357SAlexander Usyskin int rets; 94118901357SAlexander Usyskin 94218901357SAlexander Usyskin if (WARN_ON(!cl || !cl->dev)) 94318901357SAlexander Usyskin return -ENODEV; 94418901357SAlexander Usyskin 94518901357SAlexander Usyskin dev = cl->dev; 94618901357SAlexander Usyskin 94718901357SAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 94818901357SAlexander Usyskin 94918901357SAlexander Usyskin if (!mei_cl_is_connected(cl)) 95018901357SAlexander Usyskin return 0; 95118901357SAlexander Usyskin 95218901357SAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 95318901357SAlexander Usyskin mei_cl_set_disconnected(cl); 95418901357SAlexander Usyskin return 0; 95518901357SAlexander Usyskin } 95618901357SAlexander Usyskin 95718901357SAlexander Usyskin rets = pm_runtime_get(dev->dev); 95818901357SAlexander Usyskin if (rets < 0 && rets != -EINPROGRESS) { 95918901357SAlexander Usyskin pm_runtime_put_noidle(dev->dev); 96018901357SAlexander Usyskin cl_err(dev, cl, "rpm: get failed %d\n", rets); 96118901357SAlexander Usyskin return rets; 96218901357SAlexander Usyskin } 96318901357SAlexander Usyskin 96418901357SAlexander Usyskin rets = __mei_cl_disconnect(cl); 96518901357SAlexander Usyskin 96604bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 9672bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 9682bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 96904bb139aSTomas Winkler 9709ca9050bSTomas Winkler return rets; 9719ca9050bSTomas Winkler } 9729ca9050bSTomas Winkler 9739ca9050bSTomas Winkler 9749ca9050bSTomas Winkler /** 97590e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 97690e0b5f1STomas Winkler * client with the same me client id is connecting 9779ca9050bSTomas Winkler * 9789ca9050bSTomas Winkler * @cl: private data of the file object 9799ca9050bSTomas Winkler * 980a8605ea2SAlexander Usyskin * Return: true if other client is connected, false - otherwise. 9819ca9050bSTomas Winkler */ 9820c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl) 9839ca9050bSTomas Winkler { 98490e0b5f1STomas Winkler struct mei_device *dev; 9850c53357cSTomas Winkler struct mei_cl_cb *cb; 98690e0b5f1STomas Winkler 98790e0b5f1STomas Winkler dev = cl->dev; 98890e0b5f1STomas Winkler 989962ff7bcSAlexander Usyskin list_for_each_entry(cb, &dev->ctrl_rd_list, list) { 9900c53357cSTomas Winkler if (cb->fop_type == MEI_FOP_CONNECT && 991d49ed64aSAlexander Usyskin mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) 99290e0b5f1STomas Winkler return true; 9939ca9050bSTomas Winkler } 99490e0b5f1STomas Winkler 99590e0b5f1STomas Winkler return false; 9969ca9050bSTomas Winkler } 9979ca9050bSTomas Winkler 9989ca9050bSTomas Winkler /** 9990c53357cSTomas Winkler * mei_cl_send_connect - send connect request 10000c53357cSTomas Winkler * 10010c53357cSTomas Winkler * @cl: host client 10020c53357cSTomas Winkler * @cb: callback block 10030c53357cSTomas Winkler * 10040c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 10050c53357cSTomas Winkler */ 10060c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) 10070c53357cSTomas Winkler { 10080c53357cSTomas Winkler struct mei_device *dev; 10090c53357cSTomas Winkler int ret; 10100c53357cSTomas Winkler 10110c53357cSTomas Winkler dev = cl->dev; 10120c53357cSTomas Winkler 10130c53357cSTomas Winkler ret = mei_hbm_cl_connect_req(dev, cl); 10140c53357cSTomas Winkler cl->status = ret; 10150c53357cSTomas Winkler if (ret) { 10160c53357cSTomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 10170c53357cSTomas Winkler return ret; 10180c53357cSTomas Winkler } 10190c53357cSTomas Winkler 1020962ff7bcSAlexander Usyskin list_move_tail(&cb->list, &dev->ctrl_rd_list); 10210c53357cSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 10221892fc2eSAlexander Usyskin mei_schedule_stall_timer(dev); 10230c53357cSTomas Winkler return 0; 10240c53357cSTomas Winkler } 10250c53357cSTomas Winkler 10260c53357cSTomas Winkler /** 10270c53357cSTomas Winkler * mei_cl_irq_connect - send connect request in irq_thread context 10280c53357cSTomas Winkler * 10290c53357cSTomas Winkler * @cl: host client 10300c53357cSTomas Winkler * @cb: callback block 10310c53357cSTomas Winkler * @cmpl_list: complete list 10320c53357cSTomas Winkler * 10330c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 10340c53357cSTomas Winkler */ 10350c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, 1036962ff7bcSAlexander Usyskin struct list_head *cmpl_list) 10370c53357cSTomas Winkler { 10380c53357cSTomas Winkler struct mei_device *dev = cl->dev; 10390c53357cSTomas Winkler u32 msg_slots; 10400c53357cSTomas Winkler int slots; 10410c53357cSTomas Winkler int rets; 10420c53357cSTomas Winkler 10430c53357cSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 10440c53357cSTomas Winkler slots = mei_hbuf_empty_slots(dev); 10450c53357cSTomas Winkler 10460c53357cSTomas Winkler if (mei_cl_is_other_connecting(cl)) 10470c53357cSTomas Winkler return 0; 10480c53357cSTomas Winkler 10490c53357cSTomas Winkler if (slots < msg_slots) 10500c53357cSTomas Winkler return -EMSGSIZE; 10510c53357cSTomas Winkler 10520c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10530c53357cSTomas Winkler if (rets) 1054962ff7bcSAlexander Usyskin list_move_tail(&cb->list, cmpl_list); 10550c53357cSTomas Winkler 10560c53357cSTomas Winkler return rets; 10570c53357cSTomas Winkler } 10580c53357cSTomas Winkler 10590c53357cSTomas Winkler /** 106083ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 10619f81abdaSTomas Winkler * 10629f81abdaSTomas Winkler * @cl: host client 1063d49ed64aSAlexander Usyskin * @me_cl: me client 10643030dc05STomas Winkler * @fp: pointer to file structure 10659f81abdaSTomas Winkler * 10669f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 10679f81abdaSTomas Winkler * 1068a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 10699f81abdaSTomas Winkler */ 1070d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, 10713030dc05STomas Winkler const struct file *fp) 10729f81abdaSTomas Winkler { 10739f81abdaSTomas Winkler struct mei_device *dev; 10749f81abdaSTomas Winkler struct mei_cl_cb *cb; 10759f81abdaSTomas Winkler int rets; 10769f81abdaSTomas Winkler 10771df629efSAlexander Usyskin if (WARN_ON(!cl || !cl->dev || !me_cl)) 10789f81abdaSTomas Winkler return -ENODEV; 10799f81abdaSTomas Winkler 10809f81abdaSTomas Winkler dev = cl->dev; 10819f81abdaSTomas Winkler 10825d882460SAlexander Usyskin if (!mei_cl_bus_module_get(cl)) 10835d882460SAlexander Usyskin return -ENODEV; 10845d882460SAlexander Usyskin 10851df629efSAlexander Usyskin rets = mei_cl_set_connecting(cl, me_cl); 10861df629efSAlexander Usyskin if (rets) 10875d882460SAlexander Usyskin goto nortpm; 10881df629efSAlexander Usyskin 10891df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 10901df629efSAlexander Usyskin cl->state = MEI_FILE_CONNECTED; 10915d882460SAlexander Usyskin rets = 0; 10925d882460SAlexander Usyskin goto nortpm; 10931df629efSAlexander Usyskin } 10941df629efSAlexander Usyskin 10952bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 109604bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 10972bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 109804bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 10991df629efSAlexander Usyskin goto nortpm; 110004bb139aSTomas Winkler } 110104bb139aSTomas Winkler 11023030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp); 11033030dc05STomas Winkler if (!cb) { 11043030dc05STomas Winkler rets = -ENOMEM; 11059f81abdaSTomas Winkler goto out; 11063030dc05STomas Winkler } 11070c53357cSTomas Winkler 11086aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 11096aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 11100c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 11110c53357cSTomas Winkler if (rets) 11129f81abdaSTomas Winkler goto out; 11139f81abdaSTomas Winkler } 11149f81abdaSTomas Winkler 11159f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 111612f45ed4STomas Winkler wait_event_timeout(cl->wait, 11179f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 11187ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECTED || 111918901357SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REQUIRED || 11203c666182STomas Winkler cl->state == MEI_FILE_DISCONNECT_REPLY), 1121206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 11229f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 11239f81abdaSTomas Winkler 1124f3de9b63STomas Winkler if (!mei_cl_is_connected(cl)) { 112518901357SAlexander Usyskin if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { 112618901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_rd_list, cl); 112718901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_wr_list, cl); 112818901357SAlexander Usyskin /* ignore disconnect return valuue; 112918901357SAlexander Usyskin * in case of failure reset will be invoked 113018901357SAlexander Usyskin */ 113118901357SAlexander Usyskin __mei_cl_disconnect(cl); 113218901357SAlexander Usyskin rets = -EFAULT; 113318901357SAlexander Usyskin goto out; 113418901357SAlexander Usyskin } 113518901357SAlexander Usyskin 11360c53357cSTomas Winkler /* timeout or something went really wrong */ 1137285e2996SAlexander Usyskin if (!cl->status) 1138285e2996SAlexander Usyskin cl->status = -EFAULT; 11399f81abdaSTomas Winkler } 11409f81abdaSTomas Winkler 11419f81abdaSTomas Winkler rets = cl->status; 11429f81abdaSTomas Winkler out: 114304bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 11442bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 11452bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 114604bb139aSTomas Winkler 11479f81abdaSTomas Winkler mei_io_cb_free(cb); 11480c53357cSTomas Winkler 11491df629efSAlexander Usyskin nortpm: 11500c53357cSTomas Winkler if (!mei_cl_is_connected(cl)) 11510c53357cSTomas Winkler mei_cl_set_disconnected(cl); 11520c53357cSTomas Winkler 11539f81abdaSTomas Winkler return rets; 11549f81abdaSTomas Winkler } 11559f81abdaSTomas Winkler 11569f81abdaSTomas Winkler /** 115703b8d341STomas Winkler * mei_cl_alloc_linked - allocate and link host client 115803b8d341STomas Winkler * 115903b8d341STomas Winkler * @dev: the device structure 116003b8d341STomas Winkler * 116103b8d341STomas Winkler * Return: cl on success ERR_PTR on failure 116203b8d341STomas Winkler */ 11637851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) 116403b8d341STomas Winkler { 116503b8d341STomas Winkler struct mei_cl *cl; 116603b8d341STomas Winkler int ret; 116703b8d341STomas Winkler 116803b8d341STomas Winkler cl = mei_cl_allocate(dev); 116903b8d341STomas Winkler if (!cl) { 117003b8d341STomas Winkler ret = -ENOMEM; 117103b8d341STomas Winkler goto err; 117203b8d341STomas Winkler } 117303b8d341STomas Winkler 11747851e008SAlexander Usyskin ret = mei_cl_link(cl); 117503b8d341STomas Winkler if (ret) 117603b8d341STomas Winkler goto err; 117703b8d341STomas Winkler 117803b8d341STomas Winkler return cl; 117903b8d341STomas Winkler err: 118003b8d341STomas Winkler kfree(cl); 118103b8d341STomas Winkler return ERR_PTR(ret); 118203b8d341STomas Winkler } 118303b8d341STomas Winkler 118403b8d341STomas Winkler /** 11854034b81bSTomas Winkler * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl. 11869ca9050bSTomas Winkler * 118706ee536bSAlexander Usyskin * @cl: host client 11889ca9050bSTomas Winkler * 11894034b81bSTomas Winkler * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise. 11909ca9050bSTomas Winkler */ 11914034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl) 11929ca9050bSTomas Winkler { 1193d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 119490e0b5f1STomas Winkler return -EINVAL; 119590e0b5f1STomas Winkler 11964034b81bSTomas Winkler if (cl->tx_flow_ctrl_creds > 0) 11979ca9050bSTomas Winkler return 1; 11989ca9050bSTomas Winkler 1199a808c80cSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 12001df629efSAlexander Usyskin return 1; 12011df629efSAlexander Usyskin 1202d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 12034034b81bSTomas Winkler if (cl->me_cl->tx_flow_ctrl_creds > 0) 1204d49ed64aSAlexander Usyskin return 1; 120512d00665SAlexander Usyskin } 1206d49ed64aSAlexander Usyskin return 0; 12079ca9050bSTomas Winkler } 12089ca9050bSTomas Winkler 12099ca9050bSTomas Winkler /** 12104034b81bSTomas Winkler * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits 12114034b81bSTomas Winkler * for a client 12129ca9050bSTomas Winkler * 12134034b81bSTomas Winkler * @cl: host client 1214393b148fSMasanari Iida * 1215a8605ea2SAlexander Usyskin * Return: 12169ca9050bSTomas Winkler * 0 on success 12179ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 12189ca9050bSTomas Winkler */ 12194034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl) 12209ca9050bSTomas Winkler { 1221d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 122290e0b5f1STomas Winkler return -EINVAL; 122390e0b5f1STomas Winkler 12241df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 12251df629efSAlexander Usyskin return 0; 12261df629efSAlexander Usyskin 1227d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 12284034b81bSTomas Winkler if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0)) 1229d49ed64aSAlexander Usyskin return -EINVAL; 12304034b81bSTomas Winkler cl->me_cl->tx_flow_ctrl_creds--; 12319ca9050bSTomas Winkler } else { 12324034b81bSTomas Winkler if (WARN_ON(cl->tx_flow_ctrl_creds <= 0)) 1233d49ed64aSAlexander Usyskin return -EINVAL; 12344034b81bSTomas Winkler cl->tx_flow_ctrl_creds--; 12359ca9050bSTomas Winkler } 1236d49ed64aSAlexander Usyskin return 0; 12379ca9050bSTomas Winkler } 12389ca9050bSTomas Winkler 12399ca9050bSTomas Winkler /** 124051678ccbSTomas Winkler * mei_cl_notify_fop2req - convert fop to proper request 124151678ccbSTomas Winkler * 124251678ccbSTomas Winkler * @fop: client notification start response command 124351678ccbSTomas Winkler * 124451678ccbSTomas Winkler * Return: MEI_HBM_NOTIFICATION_START/STOP 124551678ccbSTomas Winkler */ 124651678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) 124751678ccbSTomas Winkler { 124851678ccbSTomas Winkler if (fop == MEI_FOP_NOTIFY_START) 124951678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_START; 125051678ccbSTomas Winkler else 125151678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_STOP; 125251678ccbSTomas Winkler } 125351678ccbSTomas Winkler 125451678ccbSTomas Winkler /** 125551678ccbSTomas Winkler * mei_cl_notify_req2fop - convert notification request top file operation type 125651678ccbSTomas Winkler * 125751678ccbSTomas Winkler * @req: hbm notification request type 125851678ccbSTomas Winkler * 125951678ccbSTomas Winkler * Return: MEI_FOP_NOTIFY_START/STOP 126051678ccbSTomas Winkler */ 126151678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) 126251678ccbSTomas Winkler { 126351678ccbSTomas Winkler if (req == MEI_HBM_NOTIFICATION_START) 126451678ccbSTomas Winkler return MEI_FOP_NOTIFY_START; 126551678ccbSTomas Winkler else 126651678ccbSTomas Winkler return MEI_FOP_NOTIFY_STOP; 126751678ccbSTomas Winkler } 126851678ccbSTomas Winkler 126951678ccbSTomas Winkler /** 127051678ccbSTomas Winkler * mei_cl_irq_notify - send notification request in irq_thread context 127151678ccbSTomas Winkler * 127251678ccbSTomas Winkler * @cl: client 127351678ccbSTomas Winkler * @cb: callback block. 127451678ccbSTomas Winkler * @cmpl_list: complete list. 127551678ccbSTomas Winkler * 127651678ccbSTomas Winkler * Return: 0 on such and error otherwise. 127751678ccbSTomas Winkler */ 127851678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, 1279962ff7bcSAlexander Usyskin struct list_head *cmpl_list) 128051678ccbSTomas Winkler { 128151678ccbSTomas Winkler struct mei_device *dev = cl->dev; 128251678ccbSTomas Winkler u32 msg_slots; 128351678ccbSTomas Winkler int slots; 128451678ccbSTomas Winkler int ret; 128551678ccbSTomas Winkler bool request; 128651678ccbSTomas Winkler 128751678ccbSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 128851678ccbSTomas Winkler slots = mei_hbuf_empty_slots(dev); 128951678ccbSTomas Winkler 129051678ccbSTomas Winkler if (slots < msg_slots) 129151678ccbSTomas Winkler return -EMSGSIZE; 129251678ccbSTomas Winkler 129351678ccbSTomas Winkler request = mei_cl_notify_fop2req(cb->fop_type); 129451678ccbSTomas Winkler ret = mei_hbm_cl_notify_req(dev, cl, request); 129551678ccbSTomas Winkler if (ret) { 129651678ccbSTomas Winkler cl->status = ret; 1297962ff7bcSAlexander Usyskin list_move_tail(&cb->list, cmpl_list); 129851678ccbSTomas Winkler return ret; 129951678ccbSTomas Winkler } 130051678ccbSTomas Winkler 1301962ff7bcSAlexander Usyskin list_move_tail(&cb->list, &dev->ctrl_rd_list); 130251678ccbSTomas Winkler return 0; 130351678ccbSTomas Winkler } 130451678ccbSTomas Winkler 130551678ccbSTomas Winkler /** 130651678ccbSTomas Winkler * mei_cl_notify_request - send notification stop/start request 130751678ccbSTomas Winkler * 130851678ccbSTomas Winkler * @cl: host client 13093030dc05STomas Winkler * @fp: associate request with file 131051678ccbSTomas Winkler * @request: 1 for start or 0 for stop 131151678ccbSTomas Winkler * 131251678ccbSTomas Winkler * Locking: called under "dev->device_lock" lock 131351678ccbSTomas Winkler * 131451678ccbSTomas Winkler * Return: 0 on such and error otherwise. 131551678ccbSTomas Winkler */ 1316f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl, 13173030dc05STomas Winkler const struct file *fp, u8 request) 131851678ccbSTomas Winkler { 131951678ccbSTomas Winkler struct mei_device *dev; 132051678ccbSTomas Winkler struct mei_cl_cb *cb; 132151678ccbSTomas Winkler enum mei_cb_file_ops fop_type; 132251678ccbSTomas Winkler int rets; 132351678ccbSTomas Winkler 132451678ccbSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 132551678ccbSTomas Winkler return -ENODEV; 132651678ccbSTomas Winkler 132751678ccbSTomas Winkler dev = cl->dev; 132851678ccbSTomas Winkler 132951678ccbSTomas Winkler if (!dev->hbm_f_ev_supported) { 133051678ccbSTomas Winkler cl_dbg(dev, cl, "notifications not supported\n"); 133151678ccbSTomas Winkler return -EOPNOTSUPP; 133251678ccbSTomas Winkler } 133351678ccbSTomas Winkler 13347c47d2caSAlexander Usyskin if (!mei_cl_is_connected(cl)) 13357c47d2caSAlexander Usyskin return -ENODEV; 13367c47d2caSAlexander Usyskin 133751678ccbSTomas Winkler rets = pm_runtime_get(dev->dev); 133851678ccbSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 133951678ccbSTomas Winkler pm_runtime_put_noidle(dev->dev); 134051678ccbSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 134151678ccbSTomas Winkler return rets; 134251678ccbSTomas Winkler } 134351678ccbSTomas Winkler 134451678ccbSTomas Winkler fop_type = mei_cl_notify_req2fop(request); 13453030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp); 134651678ccbSTomas Winkler if (!cb) { 134751678ccbSTomas Winkler rets = -ENOMEM; 134851678ccbSTomas Winkler goto out; 134951678ccbSTomas Winkler } 135051678ccbSTomas Winkler 135151678ccbSTomas Winkler if (mei_hbuf_acquire(dev)) { 135251678ccbSTomas Winkler if (mei_hbm_cl_notify_req(dev, cl, request)) { 135351678ccbSTomas Winkler rets = -ENODEV; 135451678ccbSTomas Winkler goto out; 135551678ccbSTomas Winkler } 1356962ff7bcSAlexander Usyskin list_move_tail(&cb->list, &dev->ctrl_rd_list); 135751678ccbSTomas Winkler } 135851678ccbSTomas Winkler 135951678ccbSTomas Winkler mutex_unlock(&dev->device_lock); 13607ff4bdd4SAlexander Usyskin wait_event_timeout(cl->wait, 13617ff4bdd4SAlexander Usyskin cl->notify_en == request || !mei_cl_is_connected(cl), 136251678ccbSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 136351678ccbSTomas Winkler mutex_lock(&dev->device_lock); 136451678ccbSTomas Winkler 13654a8eaa96SAlexander Usyskin if (cl->notify_en != request && !cl->status) 136651678ccbSTomas Winkler cl->status = -EFAULT; 136751678ccbSTomas Winkler 136851678ccbSTomas Winkler rets = cl->status; 136951678ccbSTomas Winkler 137051678ccbSTomas Winkler out: 137151678ccbSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 137251678ccbSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 137351678ccbSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 137451678ccbSTomas Winkler 137551678ccbSTomas Winkler mei_io_cb_free(cb); 137651678ccbSTomas Winkler return rets; 137751678ccbSTomas Winkler } 137851678ccbSTomas Winkler 137951678ccbSTomas Winkler /** 1380237092bfSTomas Winkler * mei_cl_notify - raise notification 1381237092bfSTomas Winkler * 1382237092bfSTomas Winkler * @cl: host client 1383237092bfSTomas Winkler * 1384237092bfSTomas Winkler * Locking: called under "dev->device_lock" lock 1385237092bfSTomas Winkler */ 1386237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl) 1387237092bfSTomas Winkler { 1388237092bfSTomas Winkler struct mei_device *dev; 1389237092bfSTomas Winkler 1390237092bfSTomas Winkler if (!cl || !cl->dev) 1391237092bfSTomas Winkler return; 1392237092bfSTomas Winkler 1393237092bfSTomas Winkler dev = cl->dev; 1394237092bfSTomas Winkler 1395237092bfSTomas Winkler if (!cl->notify_en) 1396237092bfSTomas Winkler return; 1397237092bfSTomas Winkler 1398237092bfSTomas Winkler cl_dbg(dev, cl, "notify event"); 1399237092bfSTomas Winkler cl->notify_ev = true; 1400850f8940STomas Winkler if (!mei_cl_bus_notify_event(cl)) 1401850f8940STomas Winkler wake_up_interruptible(&cl->ev_wait); 1402237092bfSTomas Winkler 1403237092bfSTomas Winkler if (cl->ev_async) 1404237092bfSTomas Winkler kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); 1405bb2ef9c3SAlexander Usyskin 1406237092bfSTomas Winkler } 1407237092bfSTomas Winkler 1408237092bfSTomas Winkler /** 1409b38a362fSTomas Winkler * mei_cl_notify_get - get or wait for notification event 1410b38a362fSTomas Winkler * 1411b38a362fSTomas Winkler * @cl: host client 1412b38a362fSTomas Winkler * @block: this request is blocking 1413b38a362fSTomas Winkler * @notify_ev: true if notification event was received 1414b38a362fSTomas Winkler * 1415b38a362fSTomas Winkler * Locking: called under "dev->device_lock" lock 1416b38a362fSTomas Winkler * 1417b38a362fSTomas Winkler * Return: 0 on such and error otherwise. 1418b38a362fSTomas Winkler */ 1419b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) 1420b38a362fSTomas Winkler { 1421b38a362fSTomas Winkler struct mei_device *dev; 1422b38a362fSTomas Winkler int rets; 1423b38a362fSTomas Winkler 1424b38a362fSTomas Winkler *notify_ev = false; 1425b38a362fSTomas Winkler 1426b38a362fSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 1427b38a362fSTomas Winkler return -ENODEV; 1428b38a362fSTomas Winkler 1429b38a362fSTomas Winkler dev = cl->dev; 1430b38a362fSTomas Winkler 14316c0d6701SAlexander Usyskin if (!dev->hbm_f_ev_supported) { 14326c0d6701SAlexander Usyskin cl_dbg(dev, cl, "notifications not supported\n"); 14336c0d6701SAlexander Usyskin return -EOPNOTSUPP; 14346c0d6701SAlexander Usyskin } 14356c0d6701SAlexander Usyskin 1436b38a362fSTomas Winkler if (!mei_cl_is_connected(cl)) 1437b38a362fSTomas Winkler return -ENODEV; 1438b38a362fSTomas Winkler 1439b38a362fSTomas Winkler if (cl->notify_ev) 1440b38a362fSTomas Winkler goto out; 1441b38a362fSTomas Winkler 1442b38a362fSTomas Winkler if (!block) 1443b38a362fSTomas Winkler return -EAGAIN; 1444b38a362fSTomas Winkler 1445b38a362fSTomas Winkler mutex_unlock(&dev->device_lock); 1446b38a362fSTomas Winkler rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); 1447b38a362fSTomas Winkler mutex_lock(&dev->device_lock); 1448b38a362fSTomas Winkler 1449b38a362fSTomas Winkler if (rets < 0) 1450b38a362fSTomas Winkler return rets; 1451b38a362fSTomas Winkler 1452b38a362fSTomas Winkler out: 1453b38a362fSTomas Winkler *notify_ev = cl->notify_ev; 1454b38a362fSTomas Winkler cl->notify_ev = false; 1455b38a362fSTomas Winkler return 0; 1456b38a362fSTomas Winkler } 1457b38a362fSTomas Winkler 1458b38a362fSTomas Winkler /** 1459393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 14609ca9050bSTomas Winkler * 146190e0b5f1STomas Winkler * @cl: host client 1462ce23139cSAlexander Usyskin * @length: number of bytes to read 1463bca67d68STomas Winkler * @fp: pointer to file structure 14649ca9050bSTomas Winkler * 1465a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 14669ca9050bSTomas Winkler */ 1467f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) 14689ca9050bSTomas Winkler { 146990e0b5f1STomas Winkler struct mei_device *dev; 14709ca9050bSTomas Winkler struct mei_cl_cb *cb; 14719ca9050bSTomas Winkler int rets; 14729ca9050bSTomas Winkler 147390e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 147490e0b5f1STomas Winkler return -ENODEV; 147590e0b5f1STomas Winkler 147690e0b5f1STomas Winkler dev = cl->dev; 147790e0b5f1STomas Winkler 1478b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 14799ca9050bSTomas Winkler return -ENODEV; 14809ca9050bSTomas Winkler 1481d49ed64aSAlexander Usyskin if (!mei_me_cl_is_active(cl->me_cl)) { 1482d49ed64aSAlexander Usyskin cl_err(dev, cl, "no such me client\n"); 14837ca96aa2SAlexander Usyskin return -ENOTTY; 14849ca9050bSTomas Winkler } 14851df629efSAlexander Usyskin 14869d27e73cSAlexander Usyskin if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl) 1487e51dfa5aSAlexander Usyskin return 0; 1488e51dfa5aSAlexander Usyskin 148946978adaSAlexander Usyskin /* HW currently supports only one pending read */ 149046978adaSAlexander Usyskin if (cl->rx_flow_ctrl_creds) 149146978adaSAlexander Usyskin return -EBUSY; 149246978adaSAlexander Usyskin 14933030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp); 14941df629efSAlexander Usyskin if (!cb) 14951df629efSAlexander Usyskin return -ENOMEM; 14961df629efSAlexander Usyskin 14972bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 149804bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 14992bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 150004bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 15011df629efSAlexander Usyskin goto nortpm; 150204bb139aSTomas Winkler } 150304bb139aSTomas Winkler 150446978adaSAlexander Usyskin rets = 0; 15056aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 150686113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 150786113500SAlexander Usyskin if (rets < 0) 150804bb139aSTomas Winkler goto out; 150904bb139aSTomas Winkler 151046978adaSAlexander Usyskin list_move_tail(&cb->list, &cl->rd_pending); 15119ca9050bSTomas Winkler } 151246978adaSAlexander Usyskin cl->rx_flow_ctrl_creds++; 1513accb884bSChao Bi 151404bb139aSTomas Winkler out: 151504bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 15162bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 15172bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 15181df629efSAlexander Usyskin nortpm: 151904bb139aSTomas Winkler if (rets) 15209ca9050bSTomas Winkler mei_io_cb_free(cb); 152104bb139aSTomas Winkler 15229ca9050bSTomas Winkler return rets; 15239ca9050bSTomas Winkler } 15249ca9050bSTomas Winkler 1525074b4c01STomas Winkler /** 15269d098192STomas Winkler * mei_cl_irq_write - write a message to device 152721767546STomas Winkler * from the interrupt thread context 152821767546STomas Winkler * 152921767546STomas Winkler * @cl: client 153021767546STomas Winkler * @cb: callback block. 153121767546STomas Winkler * @cmpl_list: complete list. 153221767546STomas Winkler * 1533a8605ea2SAlexander Usyskin * Return: 0, OK; otherwise error. 153421767546STomas Winkler */ 15359d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 1536962ff7bcSAlexander Usyskin struct list_head *cmpl_list) 153721767546STomas Winkler { 1538136698e5STomas Winkler struct mei_device *dev; 1539136698e5STomas Winkler struct mei_msg_data *buf; 154021767546STomas Winkler struct mei_msg_hdr mei_hdr; 1541136698e5STomas Winkler size_t len; 1542136698e5STomas Winkler u32 msg_slots; 15439d098192STomas Winkler int slots; 15442ebf8c94STomas Winkler int rets; 1545b8b73035SAlexander Usyskin bool first_chunk; 154621767546STomas Winkler 1547136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1548136698e5STomas Winkler return -ENODEV; 1549136698e5STomas Winkler 1550136698e5STomas Winkler dev = cl->dev; 1551136698e5STomas Winkler 15525db7514dSTomas Winkler buf = &cb->buf; 1553136698e5STomas Winkler 1554b8b73035SAlexander Usyskin first_chunk = cb->buf_idx == 0; 1555b8b73035SAlexander Usyskin 15564034b81bSTomas Winkler rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1; 1557136698e5STomas Winkler if (rets < 0) 1558e09ee853SAlexander Usyskin goto err; 1559136698e5STomas Winkler 1560136698e5STomas Winkler if (rets == 0) { 1561136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 1562136698e5STomas Winkler return 0; 1563136698e5STomas Winkler } 1564136698e5STomas Winkler 15659d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 1566136698e5STomas Winkler len = buf->size - cb->buf_idx; 1567136698e5STomas Winkler msg_slots = mei_data2slots(len); 1568136698e5STomas Winkler 15691df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1570d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 157121767546STomas Winkler mei_hdr.reserved = 0; 1572479327fcSTomas Winkler mei_hdr.internal = cb->internal; 157321767546STomas Winkler 15749d098192STomas Winkler if (slots >= msg_slots) { 157521767546STomas Winkler mei_hdr.length = len; 157621767546STomas Winkler mei_hdr.msg_complete = 1; 157721767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 15789d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 15799d098192STomas Winkler msg_slots = slots; 15809d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 158121767546STomas Winkler mei_hdr.length = len; 158221767546STomas Winkler mei_hdr.msg_complete = 0; 158321767546STomas Winkler } else { 158421767546STomas Winkler /* wait for next time the host buffer is empty */ 158521767546STomas Winkler return 0; 158621767546STomas Winkler } 158721767546STomas Winkler 158835bf7692SAlexander Usyskin cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n", 15895db7514dSTomas Winkler cb->buf.size, cb->buf_idx); 159021767546STomas Winkler 1591136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 1592e09ee853SAlexander Usyskin if (rets) 1593e09ee853SAlexander Usyskin goto err; 159421767546STomas Winkler 159521767546STomas Winkler cl->status = 0; 15964dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 159721767546STomas Winkler cb->buf_idx += mei_hdr.length; 15988660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 15994dfaa9f7STomas Winkler 1600b8b73035SAlexander Usyskin if (first_chunk) { 1601e09ee853SAlexander Usyskin if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) { 1602e09ee853SAlexander Usyskin rets = -EIO; 1603e09ee853SAlexander Usyskin goto err; 1604e09ee853SAlexander Usyskin } 160521767546STomas Winkler } 160621767546STomas Winkler 1607b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 1608962ff7bcSAlexander Usyskin list_move_tail(&cb->list, &dev->write_waiting_list); 1609b8b73035SAlexander Usyskin 161021767546STomas Winkler return 0; 1611e09ee853SAlexander Usyskin 1612e09ee853SAlexander Usyskin err: 1613e09ee853SAlexander Usyskin cl->status = rets; 1614962ff7bcSAlexander Usyskin list_move_tail(&cb->list, cmpl_list); 1615e09ee853SAlexander Usyskin return rets; 161621767546STomas Winkler } 161721767546STomas Winkler 161821767546STomas Winkler /** 16194234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 1620a8605ea2SAlexander Usyskin * assumes device_lock is locked 16214234a6deSTomas Winkler * 16224234a6deSTomas Winkler * @cl: host client 1623a8605ea2SAlexander Usyskin * @cb: write callback with filled data 16244234a6deSTomas Winkler * 1625a8605ea2SAlexander Usyskin * Return: number of bytes sent on success, <0 on failure. 16264234a6deSTomas Winkler */ 1627e0cb6b2fSAlexander Usyskin int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) 16284234a6deSTomas Winkler { 16294234a6deSTomas Winkler struct mei_device *dev; 16304234a6deSTomas Winkler struct mei_msg_data *buf; 16314234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 163223253c31SAlexander Usyskin int size; 16334234a6deSTomas Winkler int rets; 1634e0cb6b2fSAlexander Usyskin bool blocking; 16354234a6deSTomas Winkler 16364234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 16374234a6deSTomas Winkler return -ENODEV; 16384234a6deSTomas Winkler 16394234a6deSTomas Winkler if (WARN_ON(!cb)) 16404234a6deSTomas Winkler return -EINVAL; 16414234a6deSTomas Winkler 16424234a6deSTomas Winkler dev = cl->dev; 16434234a6deSTomas Winkler 16445db7514dSTomas Winkler buf = &cb->buf; 164523253c31SAlexander Usyskin size = buf->size; 1646e0cb6b2fSAlexander Usyskin blocking = cb->blocking; 16474234a6deSTomas Winkler 164823253c31SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", size); 16494234a6deSTomas Winkler 16502bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 165104bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 16522bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 165304bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 16546cbb097fSAlexander Usyskin goto free; 165504bb139aSTomas Winkler } 16564234a6deSTomas Winkler 16576aae48ffSTomas Winkler cb->buf_idx = 0; 16586aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 16596aae48ffSTomas Winkler 16601df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1661d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 16626aae48ffSTomas Winkler mei_hdr.reserved = 0; 16636aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 16646aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 16654234a6deSTomas Winkler 16664034b81bSTomas Winkler rets = mei_cl_tx_flow_ctrl_creds(cl); 16674234a6deSTomas Winkler if (rets < 0) 16684234a6deSTomas Winkler goto err; 16694234a6deSTomas Winkler 16706aae48ffSTomas Winkler if (rets == 0) { 16716aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 167223253c31SAlexander Usyskin rets = size; 16734234a6deSTomas Winkler goto out; 16744234a6deSTomas Winkler } 16756aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 16766aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 167723253c31SAlexander Usyskin rets = size; 16786aae48ffSTomas Winkler goto out; 16796aae48ffSTomas Winkler } 16804234a6deSTomas Winkler 16814234a6deSTomas Winkler /* Check for a maximum length */ 168223253c31SAlexander Usyskin if (size > mei_hbuf_max_len(dev)) { 16834234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 16844234a6deSTomas Winkler mei_hdr.msg_complete = 0; 16854234a6deSTomas Winkler } else { 168623253c31SAlexander Usyskin mei_hdr.length = size; 16874234a6deSTomas Winkler mei_hdr.msg_complete = 1; 16884234a6deSTomas Winkler } 16894234a6deSTomas Winkler 16902ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 16912ebf8c94STomas Winkler if (rets) 16924234a6deSTomas Winkler goto err; 16934234a6deSTomas Winkler 16944034b81bSTomas Winkler rets = mei_cl_tx_flow_ctrl_creds_reduce(cl); 1695b8b73035SAlexander Usyskin if (rets) 1696b8b73035SAlexander Usyskin goto err; 1697b8b73035SAlexander Usyskin 16984234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 16994234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 17008660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 17014234a6deSTomas Winkler 17024234a6deSTomas Winkler out: 1703b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 1704962ff7bcSAlexander Usyskin list_add_tail(&cb->list, &dev->write_waiting_list); 1705b8b73035SAlexander Usyskin else 1706962ff7bcSAlexander Usyskin list_add_tail(&cb->list, &dev->write_list); 17074234a6deSTomas Winkler 170823253c31SAlexander Usyskin cb = NULL; 17094234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 17104234a6deSTomas Winkler 17114234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 17127ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 17130faf6a3bSAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE || 17140faf6a3bSAlexander Usyskin (!mei_cl_is_connected(cl))); 17157ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 17167ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 17177ca96aa2SAlexander Usyskin if (rets) { 17184234a6deSTomas Winkler if (signal_pending(current)) 17194234a6deSTomas Winkler rets = -EINTR; 17207ca96aa2SAlexander Usyskin goto err; 17214234a6deSTomas Winkler } 17220faf6a3bSAlexander Usyskin if (cl->writing_state != MEI_WRITE_COMPLETE) { 17230faf6a3bSAlexander Usyskin rets = -EFAULT; 17240faf6a3bSAlexander Usyskin goto err; 17250faf6a3bSAlexander Usyskin } 17264234a6deSTomas Winkler } 17277ca96aa2SAlexander Usyskin 172823253c31SAlexander Usyskin rets = size; 17294234a6deSTomas Winkler err: 173004bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 17312bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 17322bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 17336cbb097fSAlexander Usyskin free: 17346cbb097fSAlexander Usyskin mei_io_cb_free(cb); 173504bb139aSTomas Winkler 17364234a6deSTomas Winkler return rets; 17374234a6deSTomas Winkler } 17384234a6deSTomas Winkler 17394234a6deSTomas Winkler 1740db086fa9STomas Winkler /** 1741db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1742db086fa9STomas Winkler * 1743db086fa9STomas Winkler * @cl: private data of the file object. 1744db086fa9STomas Winkler * @cb: callback block. 1745db086fa9STomas Winkler */ 1746db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1747db086fa9STomas Winkler { 1748a1809d38SAlexander Usyskin struct mei_device *dev = cl->dev; 1749a1809d38SAlexander Usyskin 17503c666182STomas Winkler switch (cb->fop_type) { 17513c666182STomas Winkler case MEI_FOP_WRITE: 1752db086fa9STomas Winkler mei_io_cb_free(cb); 1753db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1754a1809d38SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 1755db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1756a1809d38SAlexander Usyskin } else { 1757a1809d38SAlexander Usyskin pm_runtime_mark_last_busy(dev->dev); 1758a1809d38SAlexander Usyskin pm_request_autosuspend(dev->dev); 1759a1809d38SAlexander Usyskin } 17603c666182STomas Winkler break; 1761db086fa9STomas Winkler 17623c666182STomas Winkler case MEI_FOP_READ: 1763a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_completed); 176446978adaSAlexander Usyskin if (!mei_cl_is_fixed_address(cl) && 176546978adaSAlexander Usyskin !WARN_ON(!cl->rx_flow_ctrl_creds)) 176646978adaSAlexander Usyskin cl->rx_flow_ctrl_creds--; 1767a1f9ae2bSTomas Winkler if (!mei_cl_bus_rx_event(cl)) 1768a1f9ae2bSTomas Winkler wake_up_interruptible(&cl->rx_wait); 17693c666182STomas Winkler break; 1770db086fa9STomas Winkler 17713c666182STomas Winkler case MEI_FOP_CONNECT: 17723c666182STomas Winkler case MEI_FOP_DISCONNECT: 177351678ccbSTomas Winkler case MEI_FOP_NOTIFY_STOP: 177451678ccbSTomas Winkler case MEI_FOP_NOTIFY_START: 17753c666182STomas Winkler if (waitqueue_active(&cl->wait)) 17763c666182STomas Winkler wake_up(&cl->wait); 17773c666182STomas Winkler 17783c666182STomas Winkler break; 17796a8d648cSAlexander Usyskin case MEI_FOP_DISCONNECT_RSP: 17806a8d648cSAlexander Usyskin mei_io_cb_free(cb); 17816a8d648cSAlexander Usyskin mei_cl_set_disconnected(cl); 17826a8d648cSAlexander Usyskin break; 17833c666182STomas Winkler default: 17843c666182STomas Winkler BUG_ON(0); 1785db086fa9STomas Winkler } 1786db086fa9STomas Winkler } 1787db086fa9STomas Winkler 17884234a6deSTomas Winkler 17894234a6deSTomas Winkler /** 1790074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1791074b4c01STomas Winkler * 1792a8605ea2SAlexander Usyskin * @dev: mei device 1793074b4c01STomas Winkler */ 1794074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1795074b4c01STomas Winkler { 179631f88f57STomas Winkler struct mei_cl *cl; 1797074b4c01STomas Winkler 17983c666182STomas Winkler list_for_each_entry(cl, &dev->file_list, link) 17993c666182STomas Winkler mei_cl_set_disconnected(cl); 1800074b4c01STomas Winkler } 1801