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 * 382928fa666STomas Winkler * @list: 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 */ 386928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list, 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 */ 392928fa666STomas Winkler list_for_each_entry_safe(cb, next, &list->list, 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 * 404928fa666STomas Winkler * @list: An instance of our list structure 405928fa666STomas Winkler * @cl: host client 406928fa666STomas Winkler */ 407928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 408928fa666STomas Winkler { 409928fa666STomas Winkler __mei_io_list_flush(list, cl, false); 410928fa666STomas Winkler } 411928fa666STomas Winkler 412928fa666STomas Winkler /** 413928fa666STomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 414928fa666STomas Winkler * 415928fa666STomas Winkler * @list: An instance of our list structure 416928fa666STomas Winkler * @cl: host client 417928fa666STomas Winkler */ 418928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) 419928fa666STomas Winkler { 420928fa666STomas Winkler __mei_io_list_flush(list, 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 428bca67d68STomas Winkler * @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 4623030dc05STomas Winkler * @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 4823030dc05STomas Winkler list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.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); 5499ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 550a9bed610STomas Winkler 551a9bed610STomas Winkler mei_cl_read_cb_flush(cl, fp); 552a9bed610STomas Winkler 5539ca9050bSTomas Winkler return 0; 5549ca9050bSTomas Winkler } 5559ca9050bSTomas Winkler 5569ca9050bSTomas Winkler 5579ca9050bSTomas Winkler /** 55883ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 5599ca9050bSTomas Winkler * 5609ca9050bSTomas Winkler * @cl: host client to be initialized 5619ca9050bSTomas Winkler * @dev: mei device 5629ca9050bSTomas Winkler */ 5639ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 5649ca9050bSTomas Winkler { 5659ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 5669ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 5679ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 5689ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 569b38a362fSTomas Winkler init_waitqueue_head(&cl->ev_wait); 570a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_completed); 571a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_pending); 5729ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 5739ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 5743c666182STomas Winkler cl->state = MEI_FILE_INITIALIZING; 5759ca9050bSTomas Winkler cl->dev = dev; 5769ca9050bSTomas Winkler } 5779ca9050bSTomas Winkler 5789ca9050bSTomas Winkler /** 5799ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 5809ca9050bSTomas Winkler * 5819ca9050bSTomas Winkler * @dev: mei device 582a8605ea2SAlexander Usyskin * Return: The allocated file or NULL on failure 5839ca9050bSTomas Winkler */ 5849ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 5859ca9050bSTomas Winkler { 5869ca9050bSTomas Winkler struct mei_cl *cl; 5879ca9050bSTomas Winkler 5889ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 5899ca9050bSTomas Winkler if (!cl) 5909ca9050bSTomas Winkler return NULL; 5919ca9050bSTomas Winkler 5929ca9050bSTomas Winkler mei_cl_init(cl, dev); 5939ca9050bSTomas Winkler 5949ca9050bSTomas Winkler return cl; 5959ca9050bSTomas Winkler } 5969ca9050bSTomas Winkler 59790e0b5f1STomas Winkler /** 5983908be6fSAlexander Usyskin * mei_cl_link - allocate host id in the host map 5999ca9050bSTomas Winkler * 6003908be6fSAlexander Usyskin * @cl: host client 601393b148fSMasanari Iida * 602a8605ea2SAlexander Usyskin * Return: 0 on success 6039ca9050bSTomas Winkler * -EINVAL on incorrect values 60403b8d341STomas Winkler * -EMFILE if open count exceeded. 6059ca9050bSTomas Winkler */ 6067851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl) 6079ca9050bSTomas Winkler { 60890e0b5f1STomas Winkler struct mei_device *dev; 60922f96a0eSTomas Winkler long open_handle_count; 6107851e008SAlexander Usyskin int id; 6119ca9050bSTomas Winkler 612781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 6139ca9050bSTomas Winkler return -EINVAL; 6149ca9050bSTomas Winkler 61590e0b5f1STomas Winkler dev = cl->dev; 61690e0b5f1STomas Winkler 6177851e008SAlexander Usyskin id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); 618781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 6192bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 620e036cc57STomas Winkler return -EMFILE; 621e036cc57STomas Winkler } 622e036cc57STomas Winkler 62322f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 62422f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6252bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 626e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 627e036cc57STomas Winkler return -EMFILE; 6289ca9050bSTomas Winkler } 629781d0d89STomas Winkler 630781d0d89STomas Winkler dev->open_handle_count++; 631781d0d89STomas Winkler 632781d0d89STomas Winkler cl->host_client_id = id; 633781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 634781d0d89STomas Winkler 635781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 636781d0d89STomas Winkler 637781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 638781d0d89STomas Winkler 639c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 640781d0d89STomas Winkler return 0; 641781d0d89STomas Winkler } 642781d0d89STomas Winkler 6439ca9050bSTomas Winkler /** 644d49ed64aSAlexander Usyskin * mei_cl_unlink - remove host client from the list 6459ca9050bSTomas Winkler * 646393b148fSMasanari Iida * @cl: host client 647ce23139cSAlexander Usyskin * 648ce23139cSAlexander Usyskin * Return: always 0 6499ca9050bSTomas Winkler */ 65090e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 6519ca9050bSTomas Winkler { 65290e0b5f1STomas Winkler struct mei_device *dev; 65390e0b5f1STomas Winkler 654781d0d89STomas Winkler /* don't shout on error exit path */ 655781d0d89STomas Winkler if (!cl) 656781d0d89STomas Winkler return 0; 657781d0d89STomas Winkler 658fdd9b865SAlexander Usyskin /* amthif might not be initialized */ 6598e9a4a9aSTomas Winkler if (!cl->dev) 6608e9a4a9aSTomas Winkler return 0; 66190e0b5f1STomas Winkler 66290e0b5f1STomas Winkler dev = cl->dev; 66390e0b5f1STomas Winkler 664a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 665a14c44d8STomas Winkler 66622f96a0eSTomas Winkler if (dev->open_handle_count > 0) 66722f96a0eSTomas Winkler dev->open_handle_count--; 66822f96a0eSTomas Winkler 66922f96a0eSTomas Winkler /* never clear the 0 bit */ 67022f96a0eSTomas Winkler if (cl->host_client_id) 67122f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 67222f96a0eSTomas Winkler 67322f96a0eSTomas Winkler list_del_init(&cl->link); 67422f96a0eSTomas Winkler 67522f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 67622f96a0eSTomas Winkler 67790e0b5f1STomas Winkler return 0; 6789ca9050bSTomas Winkler } 6799ca9050bSTomas Winkler 680025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev) 6819ca9050bSTomas Winkler { 6829ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 6836adb8efbSTomas Winkler dev->reset_count = 0; 68404bb139aSTomas Winkler 685025fb792SAlexander Usyskin schedule_work(&dev->bus_rescan_work); 6866009595aSTomas Winkler 6872bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 6882bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 6892bf94cabSTomas Winkler pm_runtime_autosuspend(dev->dev); 6909ca9050bSTomas Winkler } 6919ca9050bSTomas Winkler 6926aae48ffSTomas Winkler /** 693a8605ea2SAlexander Usyskin * mei_hbuf_acquire - try to acquire host buffer 6946aae48ffSTomas Winkler * 6956aae48ffSTomas Winkler * @dev: the device structure 696a8605ea2SAlexander Usyskin * Return: true if host buffer was acquired 6976aae48ffSTomas Winkler */ 6986aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 6996aae48ffSTomas Winkler { 70004bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 7013dc196eaSAlexander Usyskin mei_pg_in_transition(dev)) { 7022bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 70304bb139aSTomas Winkler return false; 70404bb139aSTomas Winkler } 70504bb139aSTomas Winkler 7066aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 7072bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 7086aae48ffSTomas Winkler return false; 7096aae48ffSTomas Winkler } 7106aae48ffSTomas Winkler 7116aae48ffSTomas Winkler dev->hbuf_is_ready = false; 7126aae48ffSTomas Winkler 7136aae48ffSTomas Winkler return true; 7146aae48ffSTomas Winkler } 7159ca9050bSTomas Winkler 7169ca9050bSTomas Winkler /** 717a4307fe4SAlexander Usyskin * mei_cl_wake_all - wake up readers, writers and event waiters so 718a4307fe4SAlexander Usyskin * they can be interrupted 719a4307fe4SAlexander Usyskin * 720a4307fe4SAlexander Usyskin * @cl: host client 721a4307fe4SAlexander Usyskin */ 722a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl) 723a4307fe4SAlexander Usyskin { 724a4307fe4SAlexander Usyskin struct mei_device *dev = cl->dev; 725a4307fe4SAlexander Usyskin 726a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 727a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->rx_wait)) { 728a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 729a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->rx_wait); 730a4307fe4SAlexander Usyskin } 731a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 732a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 733a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 734a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->tx_wait); 735a4307fe4SAlexander Usyskin } 736a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 737a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->ev_wait)) { 738a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); 739a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->ev_wait); 740a4307fe4SAlexander Usyskin } 7417ff4bdd4SAlexander Usyskin /* synchronized under device mutex */ 7427ff4bdd4SAlexander Usyskin if (waitqueue_active(&cl->wait)) { 7437ff4bdd4SAlexander Usyskin cl_dbg(dev, cl, "Waking up ctrl write clients!\n"); 74469f1804aSAlexander Usyskin wake_up(&cl->wait); 7457ff4bdd4SAlexander Usyskin } 746a4307fe4SAlexander Usyskin } 747a4307fe4SAlexander Usyskin 748a4307fe4SAlexander Usyskin /** 7493c666182STomas Winkler * mei_cl_set_disconnected - set disconnected state and clear 7503c666182STomas Winkler * associated states and resources 7513c666182STomas Winkler * 7523c666182STomas Winkler * @cl: host client 7533c666182STomas Winkler */ 7543c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl) 7553c666182STomas Winkler { 7563c666182STomas Winkler struct mei_device *dev = cl->dev; 7573c666182STomas Winkler 7583c666182STomas Winkler if (cl->state == MEI_FILE_DISCONNECTED || 7593c666182STomas Winkler cl->state == MEI_FILE_INITIALIZING) 7603c666182STomas Winkler return; 7613c666182STomas Winkler 7623c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 763a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_list, cl); 764a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_waiting_list, cl); 7653c666182STomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 7663c666182STomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 767a4307fe4SAlexander Usyskin mei_cl_wake_all(cl); 76846978adaSAlexander Usyskin cl->rx_flow_ctrl_creds = 0; 7694034b81bSTomas Winkler cl->tx_flow_ctrl_creds = 0; 7703c666182STomas Winkler cl->timer_count = 0; 771d49ed64aSAlexander Usyskin 772a03d77f6SAlexander Usyskin if (!cl->me_cl) 773a03d77f6SAlexander Usyskin return; 774a03d77f6SAlexander Usyskin 775a03d77f6SAlexander Usyskin if (!WARN_ON(cl->me_cl->connect_count == 0)) 776a03d77f6SAlexander Usyskin cl->me_cl->connect_count--; 777a03d77f6SAlexander Usyskin 778c241e9b1SAlexander Usyskin if (cl->me_cl->connect_count == 0) 7794034b81bSTomas Winkler cl->me_cl->tx_flow_ctrl_creds = 0; 780c241e9b1SAlexander Usyskin 781d49ed64aSAlexander Usyskin mei_me_cl_put(cl->me_cl); 782d49ed64aSAlexander Usyskin cl->me_cl = NULL; 7833c666182STomas Winkler } 7843c666182STomas Winkler 785a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) 786a03d77f6SAlexander Usyskin { 7871df629efSAlexander Usyskin if (!mei_me_cl_get(me_cl)) 788a03d77f6SAlexander Usyskin return -ENOENT; 789a03d77f6SAlexander Usyskin 7901df629efSAlexander Usyskin /* only one connection is allowed for fixed address clients */ 7911df629efSAlexander Usyskin if (me_cl->props.fixed_address) { 7921df629efSAlexander Usyskin if (me_cl->connect_count) { 7931df629efSAlexander Usyskin mei_me_cl_put(me_cl); 7941df629efSAlexander Usyskin return -EBUSY; 7951df629efSAlexander Usyskin } 7961df629efSAlexander Usyskin } 7971df629efSAlexander Usyskin 7981df629efSAlexander Usyskin cl->me_cl = me_cl; 799a03d77f6SAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 800a03d77f6SAlexander Usyskin cl->me_cl->connect_count++; 801a03d77f6SAlexander Usyskin 802a03d77f6SAlexander Usyskin return 0; 803a03d77f6SAlexander Usyskin } 804a03d77f6SAlexander Usyskin 8053c666182STomas Winkler /* 8063c666182STomas Winkler * mei_cl_send_disconnect - send disconnect request 8073c666182STomas Winkler * 8083c666182STomas Winkler * @cl: host client 8093c666182STomas Winkler * @cb: callback block 8103c666182STomas Winkler * 8113c666182STomas Winkler * Return: 0, OK; otherwise, error. 8123c666182STomas Winkler */ 8133c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 8143c666182STomas Winkler { 8153c666182STomas Winkler struct mei_device *dev; 8163c666182STomas Winkler int ret; 8173c666182STomas Winkler 8183c666182STomas Winkler dev = cl->dev; 8193c666182STomas Winkler 8203c666182STomas Winkler ret = mei_hbm_cl_disconnect_req(dev, cl); 8213c666182STomas Winkler cl->status = ret; 8223c666182STomas Winkler if (ret) { 8233c666182STomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 8243c666182STomas Winkler return ret; 8253c666182STomas Winkler } 8263c666182STomas Winkler 8273c666182STomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 8283c666182STomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 8293c666182STomas Winkler 8303c666182STomas Winkler return 0; 8313c666182STomas Winkler } 8323c666182STomas Winkler 8333c666182STomas Winkler /** 8343c666182STomas Winkler * mei_cl_irq_disconnect - processes close related operation from 8353c666182STomas Winkler * interrupt thread context - send disconnect request 8363c666182STomas Winkler * 8373c666182STomas Winkler * @cl: client 8383c666182STomas Winkler * @cb: callback block. 8393c666182STomas Winkler * @cmpl_list: complete list. 8403c666182STomas Winkler * 8413c666182STomas Winkler * Return: 0, OK; otherwise, error. 8423c666182STomas Winkler */ 8433c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 8443c666182STomas Winkler struct mei_cl_cb *cmpl_list) 8453c666182STomas Winkler { 8463c666182STomas Winkler struct mei_device *dev = cl->dev; 8473c666182STomas Winkler u32 msg_slots; 8483c666182STomas Winkler int slots; 8493c666182STomas Winkler int ret; 8503c666182STomas Winkler 8513c666182STomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 8523c666182STomas Winkler slots = mei_hbuf_empty_slots(dev); 8533c666182STomas Winkler 8543c666182STomas Winkler if (slots < msg_slots) 8553c666182STomas Winkler return -EMSGSIZE; 8563c666182STomas Winkler 8573c666182STomas Winkler ret = mei_cl_send_disconnect(cl, cb); 8583c666182STomas Winkler if (ret) 8593c666182STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 8603c666182STomas Winkler 8613c666182STomas Winkler return ret; 8623c666182STomas Winkler } 8633c666182STomas Winkler 8643c666182STomas Winkler /** 86518901357SAlexander Usyskin * __mei_cl_disconnect - disconnect host client from the me one 86618901357SAlexander Usyskin * internal function runtime pm has to be already acquired 8679ca9050bSTomas Winkler * 86890e0b5f1STomas Winkler * @cl: host client 8699ca9050bSTomas Winkler * 870a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 8719ca9050bSTomas Winkler */ 87218901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl) 8739ca9050bSTomas Winkler { 87490e0b5f1STomas Winkler struct mei_device *dev; 8759ca9050bSTomas Winkler struct mei_cl_cb *cb; 876fe2f17ebSAlexander Usyskin int rets; 8779ca9050bSTomas Winkler 87890e0b5f1STomas Winkler dev = cl->dev; 87990e0b5f1STomas Winkler 8803c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTING; 8813c666182STomas Winkler 8823030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL); 8833030dc05STomas Winkler if (!cb) { 8843030dc05STomas Winkler rets = -ENOMEM; 8853c666182STomas Winkler goto out; 8863030dc05STomas Winkler } 8879ca9050bSTomas Winkler 8883c666182STomas Winkler if (mei_hbuf_acquire(dev)) { 8893c666182STomas Winkler rets = mei_cl_send_disconnect(cl, cb); 8903c666182STomas Winkler if (rets) { 8913c666182STomas Winkler cl_err(dev, cl, "failed to disconnect.\n"); 8923c666182STomas Winkler goto out; 8939ca9050bSTomas Winkler } 8943c666182STomas Winkler } 8953c666182STomas Winkler 8969ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 8977ff4bdd4SAlexander Usyskin wait_event_timeout(cl->wait, 8987ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REPLY || 8997ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECTED, 9009ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 9019ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 902fe2f17ebSAlexander Usyskin 9033c666182STomas Winkler rets = cl->status; 9047ff4bdd4SAlexander Usyskin if (cl->state != MEI_FILE_DISCONNECT_REPLY && 9057ff4bdd4SAlexander Usyskin cl->state != MEI_FILE_DISCONNECTED) { 906fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 907fe2f17ebSAlexander Usyskin rets = -ETIME; 9089ca9050bSTomas Winkler } 9099ca9050bSTomas Winkler 9103c666182STomas Winkler out: 9113c666182STomas Winkler /* we disconnect also on error */ 9123c666182STomas Winkler mei_cl_set_disconnected(cl); 9133c666182STomas Winkler if (!rets) 9143c666182STomas Winkler cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 9153c666182STomas Winkler 91618901357SAlexander Usyskin mei_io_cb_free(cb); 91718901357SAlexander Usyskin return rets; 91818901357SAlexander Usyskin } 91918901357SAlexander Usyskin 92018901357SAlexander Usyskin /** 92118901357SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 92218901357SAlexander Usyskin * 92318901357SAlexander Usyskin * @cl: host client 92418901357SAlexander Usyskin * 92518901357SAlexander Usyskin * Locking: called under "dev->device_lock" lock 92618901357SAlexander Usyskin * 92718901357SAlexander Usyskin * Return: 0 on success, <0 on failure. 92818901357SAlexander Usyskin */ 92918901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl) 93018901357SAlexander Usyskin { 93118901357SAlexander Usyskin struct mei_device *dev; 93218901357SAlexander Usyskin int rets; 93318901357SAlexander Usyskin 93418901357SAlexander Usyskin if (WARN_ON(!cl || !cl->dev)) 93518901357SAlexander Usyskin return -ENODEV; 93618901357SAlexander Usyskin 93718901357SAlexander Usyskin dev = cl->dev; 93818901357SAlexander Usyskin 93918901357SAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 94018901357SAlexander Usyskin 94118901357SAlexander Usyskin if (!mei_cl_is_connected(cl)) 94218901357SAlexander Usyskin return 0; 94318901357SAlexander Usyskin 94418901357SAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 94518901357SAlexander Usyskin mei_cl_set_disconnected(cl); 94618901357SAlexander Usyskin return 0; 94718901357SAlexander Usyskin } 94818901357SAlexander Usyskin 94918901357SAlexander Usyskin rets = pm_runtime_get(dev->dev); 95018901357SAlexander Usyskin if (rets < 0 && rets != -EINPROGRESS) { 95118901357SAlexander Usyskin pm_runtime_put_noidle(dev->dev); 95218901357SAlexander Usyskin cl_err(dev, cl, "rpm: get failed %d\n", rets); 95318901357SAlexander Usyskin return rets; 95418901357SAlexander Usyskin } 95518901357SAlexander Usyskin 95618901357SAlexander Usyskin rets = __mei_cl_disconnect(cl); 95718901357SAlexander Usyskin 95804bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 9592bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 9602bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 96104bb139aSTomas Winkler 9629ca9050bSTomas Winkler return rets; 9639ca9050bSTomas Winkler } 9649ca9050bSTomas Winkler 9659ca9050bSTomas Winkler 9669ca9050bSTomas Winkler /** 96790e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 96890e0b5f1STomas Winkler * client with the same me client id is connecting 9699ca9050bSTomas Winkler * 9709ca9050bSTomas Winkler * @cl: private data of the file object 9719ca9050bSTomas Winkler * 972a8605ea2SAlexander Usyskin * Return: true if other client is connected, false - otherwise. 9739ca9050bSTomas Winkler */ 9740c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl) 9759ca9050bSTomas Winkler { 97690e0b5f1STomas Winkler struct mei_device *dev; 9770c53357cSTomas Winkler struct mei_cl_cb *cb; 97890e0b5f1STomas Winkler 97990e0b5f1STomas Winkler dev = cl->dev; 98090e0b5f1STomas Winkler 9810c53357cSTomas Winkler list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { 9820c53357cSTomas Winkler if (cb->fop_type == MEI_FOP_CONNECT && 983d49ed64aSAlexander Usyskin mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) 98490e0b5f1STomas Winkler return true; 9859ca9050bSTomas Winkler } 98690e0b5f1STomas Winkler 98790e0b5f1STomas Winkler return false; 9889ca9050bSTomas Winkler } 9899ca9050bSTomas Winkler 9909ca9050bSTomas Winkler /** 9910c53357cSTomas Winkler * mei_cl_send_connect - send connect request 9920c53357cSTomas Winkler * 9930c53357cSTomas Winkler * @cl: host client 9940c53357cSTomas Winkler * @cb: callback block 9950c53357cSTomas Winkler * 9960c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 9970c53357cSTomas Winkler */ 9980c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) 9990c53357cSTomas Winkler { 10000c53357cSTomas Winkler struct mei_device *dev; 10010c53357cSTomas Winkler int ret; 10020c53357cSTomas Winkler 10030c53357cSTomas Winkler dev = cl->dev; 10040c53357cSTomas Winkler 10050c53357cSTomas Winkler ret = mei_hbm_cl_connect_req(dev, cl); 10060c53357cSTomas Winkler cl->status = ret; 10070c53357cSTomas Winkler if (ret) { 10080c53357cSTomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 10090c53357cSTomas Winkler return ret; 10100c53357cSTomas Winkler } 10110c53357cSTomas Winkler 10120c53357cSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 10130c53357cSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 10140c53357cSTomas Winkler return 0; 10150c53357cSTomas Winkler } 10160c53357cSTomas Winkler 10170c53357cSTomas Winkler /** 10180c53357cSTomas Winkler * mei_cl_irq_connect - send connect request in irq_thread context 10190c53357cSTomas Winkler * 10200c53357cSTomas Winkler * @cl: host client 10210c53357cSTomas Winkler * @cb: callback block 10220c53357cSTomas Winkler * @cmpl_list: complete list 10230c53357cSTomas Winkler * 10240c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 10250c53357cSTomas Winkler */ 10260c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, 10270c53357cSTomas Winkler struct mei_cl_cb *cmpl_list) 10280c53357cSTomas Winkler { 10290c53357cSTomas Winkler struct mei_device *dev = cl->dev; 10300c53357cSTomas Winkler u32 msg_slots; 10310c53357cSTomas Winkler int slots; 10320c53357cSTomas Winkler int rets; 10330c53357cSTomas Winkler 10340c53357cSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 10350c53357cSTomas Winkler slots = mei_hbuf_empty_slots(dev); 10360c53357cSTomas Winkler 10370c53357cSTomas Winkler if (mei_cl_is_other_connecting(cl)) 10380c53357cSTomas Winkler return 0; 10390c53357cSTomas Winkler 10400c53357cSTomas Winkler if (slots < msg_slots) 10410c53357cSTomas Winkler return -EMSGSIZE; 10420c53357cSTomas Winkler 10430c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10440c53357cSTomas Winkler if (rets) 10450c53357cSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 10460c53357cSTomas Winkler 10470c53357cSTomas Winkler return rets; 10480c53357cSTomas Winkler } 10490c53357cSTomas Winkler 10500c53357cSTomas Winkler /** 105183ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 10529f81abdaSTomas Winkler * 10539f81abdaSTomas Winkler * @cl: host client 1054d49ed64aSAlexander Usyskin * @me_cl: me client 10553030dc05STomas Winkler * @fp: pointer to file structure 10569f81abdaSTomas Winkler * 10579f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 10589f81abdaSTomas Winkler * 1059a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 10609f81abdaSTomas Winkler */ 1061d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, 10623030dc05STomas Winkler const struct file *fp) 10639f81abdaSTomas Winkler { 10649f81abdaSTomas Winkler struct mei_device *dev; 10659f81abdaSTomas Winkler struct mei_cl_cb *cb; 10669f81abdaSTomas Winkler int rets; 10679f81abdaSTomas Winkler 10681df629efSAlexander Usyskin if (WARN_ON(!cl || !cl->dev || !me_cl)) 10699f81abdaSTomas Winkler return -ENODEV; 10709f81abdaSTomas Winkler 10719f81abdaSTomas Winkler dev = cl->dev; 10729f81abdaSTomas Winkler 10731df629efSAlexander Usyskin rets = mei_cl_set_connecting(cl, me_cl); 10741df629efSAlexander Usyskin if (rets) 10751df629efSAlexander Usyskin return rets; 10761df629efSAlexander Usyskin 10771df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 10781df629efSAlexander Usyskin cl->state = MEI_FILE_CONNECTED; 10791df629efSAlexander Usyskin return 0; 10801df629efSAlexander Usyskin } 10811df629efSAlexander Usyskin 10822bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 108304bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 10842bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 108504bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 10861df629efSAlexander Usyskin goto nortpm; 108704bb139aSTomas Winkler } 108804bb139aSTomas Winkler 10893030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp); 10903030dc05STomas Winkler if (!cb) { 10913030dc05STomas Winkler rets = -ENOMEM; 10929f81abdaSTomas Winkler goto out; 10933030dc05STomas Winkler } 10940c53357cSTomas Winkler 10956aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 10966aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 10970c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10980c53357cSTomas Winkler if (rets) 10999f81abdaSTomas Winkler goto out; 11009f81abdaSTomas Winkler } 11019f81abdaSTomas Winkler 11029f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 110312f45ed4STomas Winkler wait_event_timeout(cl->wait, 11049f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 11057ff4bdd4SAlexander Usyskin cl->state == MEI_FILE_DISCONNECTED || 110618901357SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REQUIRED || 11073c666182STomas Winkler cl->state == MEI_FILE_DISCONNECT_REPLY), 1108206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 11099f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 11109f81abdaSTomas Winkler 1111f3de9b63STomas Winkler if (!mei_cl_is_connected(cl)) { 111218901357SAlexander Usyskin if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { 111318901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_rd_list, cl); 111418901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_wr_list, cl); 111518901357SAlexander Usyskin /* ignore disconnect return valuue; 111618901357SAlexander Usyskin * in case of failure reset will be invoked 111718901357SAlexander Usyskin */ 111818901357SAlexander Usyskin __mei_cl_disconnect(cl); 111918901357SAlexander Usyskin rets = -EFAULT; 112018901357SAlexander Usyskin goto out; 112118901357SAlexander Usyskin } 112218901357SAlexander Usyskin 11230c53357cSTomas Winkler /* timeout or something went really wrong */ 1124285e2996SAlexander Usyskin if (!cl->status) 1125285e2996SAlexander Usyskin cl->status = -EFAULT; 11269f81abdaSTomas Winkler } 11279f81abdaSTomas Winkler 11289f81abdaSTomas Winkler rets = cl->status; 11299f81abdaSTomas Winkler out: 113004bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 11312bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 11322bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 113304bb139aSTomas Winkler 11349f81abdaSTomas Winkler mei_io_cb_free(cb); 11350c53357cSTomas Winkler 11361df629efSAlexander Usyskin nortpm: 11370c53357cSTomas Winkler if (!mei_cl_is_connected(cl)) 11380c53357cSTomas Winkler mei_cl_set_disconnected(cl); 11390c53357cSTomas Winkler 11409f81abdaSTomas Winkler return rets; 11419f81abdaSTomas Winkler } 11429f81abdaSTomas Winkler 11439f81abdaSTomas Winkler /** 114403b8d341STomas Winkler * mei_cl_alloc_linked - allocate and link host client 114503b8d341STomas Winkler * 114603b8d341STomas Winkler * @dev: the device structure 114703b8d341STomas Winkler * 114803b8d341STomas Winkler * Return: cl on success ERR_PTR on failure 114903b8d341STomas Winkler */ 11507851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) 115103b8d341STomas Winkler { 115203b8d341STomas Winkler struct mei_cl *cl; 115303b8d341STomas Winkler int ret; 115403b8d341STomas Winkler 115503b8d341STomas Winkler cl = mei_cl_allocate(dev); 115603b8d341STomas Winkler if (!cl) { 115703b8d341STomas Winkler ret = -ENOMEM; 115803b8d341STomas Winkler goto err; 115903b8d341STomas Winkler } 116003b8d341STomas Winkler 11617851e008SAlexander Usyskin ret = mei_cl_link(cl); 116203b8d341STomas Winkler if (ret) 116303b8d341STomas Winkler goto err; 116403b8d341STomas Winkler 116503b8d341STomas Winkler return cl; 116603b8d341STomas Winkler err: 116703b8d341STomas Winkler kfree(cl); 116803b8d341STomas Winkler return ERR_PTR(ret); 116903b8d341STomas Winkler } 117003b8d341STomas Winkler 117103b8d341STomas Winkler /** 11724034b81bSTomas Winkler * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl. 11739ca9050bSTomas Winkler * 117406ee536bSAlexander Usyskin * @cl: host client 11759ca9050bSTomas Winkler * 11764034b81bSTomas Winkler * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise. 11779ca9050bSTomas Winkler */ 11784034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl) 11799ca9050bSTomas Winkler { 1180d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 118190e0b5f1STomas Winkler return -EINVAL; 118290e0b5f1STomas Winkler 11834034b81bSTomas Winkler if (cl->tx_flow_ctrl_creds > 0) 11849ca9050bSTomas Winkler return 1; 11859ca9050bSTomas Winkler 1186a808c80cSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 11871df629efSAlexander Usyskin return 1; 11881df629efSAlexander Usyskin 1189d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 11904034b81bSTomas Winkler if (cl->me_cl->tx_flow_ctrl_creds > 0) 1191d49ed64aSAlexander Usyskin return 1; 119212d00665SAlexander Usyskin } 1193d49ed64aSAlexander Usyskin return 0; 11949ca9050bSTomas Winkler } 11959ca9050bSTomas Winkler 11969ca9050bSTomas Winkler /** 11974034b81bSTomas Winkler * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits 11984034b81bSTomas Winkler * for a client 11999ca9050bSTomas Winkler * 12004034b81bSTomas Winkler * @cl: host client 1201393b148fSMasanari Iida * 1202a8605ea2SAlexander Usyskin * Return: 12039ca9050bSTomas Winkler * 0 on success 12049ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 12059ca9050bSTomas Winkler */ 12064034b81bSTomas Winkler static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl) 12079ca9050bSTomas Winkler { 1208d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 120990e0b5f1STomas Winkler return -EINVAL; 121090e0b5f1STomas Winkler 12111df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 12121df629efSAlexander Usyskin return 0; 12131df629efSAlexander Usyskin 1214d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 12154034b81bSTomas Winkler if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0)) 1216d49ed64aSAlexander Usyskin return -EINVAL; 12174034b81bSTomas Winkler cl->me_cl->tx_flow_ctrl_creds--; 12189ca9050bSTomas Winkler } else { 12194034b81bSTomas Winkler if (WARN_ON(cl->tx_flow_ctrl_creds <= 0)) 1220d49ed64aSAlexander Usyskin return -EINVAL; 12214034b81bSTomas Winkler cl->tx_flow_ctrl_creds--; 12229ca9050bSTomas Winkler } 1223d49ed64aSAlexander Usyskin return 0; 12249ca9050bSTomas Winkler } 12259ca9050bSTomas Winkler 12269ca9050bSTomas Winkler /** 122751678ccbSTomas Winkler * mei_cl_notify_fop2req - convert fop to proper request 122851678ccbSTomas Winkler * 122951678ccbSTomas Winkler * @fop: client notification start response command 123051678ccbSTomas Winkler * 123151678ccbSTomas Winkler * Return: MEI_HBM_NOTIFICATION_START/STOP 123251678ccbSTomas Winkler */ 123351678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) 123451678ccbSTomas Winkler { 123551678ccbSTomas Winkler if (fop == MEI_FOP_NOTIFY_START) 123651678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_START; 123751678ccbSTomas Winkler else 123851678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_STOP; 123951678ccbSTomas Winkler } 124051678ccbSTomas Winkler 124151678ccbSTomas Winkler /** 124251678ccbSTomas Winkler * mei_cl_notify_req2fop - convert notification request top file operation type 124351678ccbSTomas Winkler * 124451678ccbSTomas Winkler * @req: hbm notification request type 124551678ccbSTomas Winkler * 124651678ccbSTomas Winkler * Return: MEI_FOP_NOTIFY_START/STOP 124751678ccbSTomas Winkler */ 124851678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) 124951678ccbSTomas Winkler { 125051678ccbSTomas Winkler if (req == MEI_HBM_NOTIFICATION_START) 125151678ccbSTomas Winkler return MEI_FOP_NOTIFY_START; 125251678ccbSTomas Winkler else 125351678ccbSTomas Winkler return MEI_FOP_NOTIFY_STOP; 125451678ccbSTomas Winkler } 125551678ccbSTomas Winkler 125651678ccbSTomas Winkler /** 125751678ccbSTomas Winkler * mei_cl_irq_notify - send notification request in irq_thread context 125851678ccbSTomas Winkler * 125951678ccbSTomas Winkler * @cl: client 126051678ccbSTomas Winkler * @cb: callback block. 126151678ccbSTomas Winkler * @cmpl_list: complete list. 126251678ccbSTomas Winkler * 126351678ccbSTomas Winkler * Return: 0 on such and error otherwise. 126451678ccbSTomas Winkler */ 126551678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, 126651678ccbSTomas Winkler struct mei_cl_cb *cmpl_list) 126751678ccbSTomas Winkler { 126851678ccbSTomas Winkler struct mei_device *dev = cl->dev; 126951678ccbSTomas Winkler u32 msg_slots; 127051678ccbSTomas Winkler int slots; 127151678ccbSTomas Winkler int ret; 127251678ccbSTomas Winkler bool request; 127351678ccbSTomas Winkler 127451678ccbSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 127551678ccbSTomas Winkler slots = mei_hbuf_empty_slots(dev); 127651678ccbSTomas Winkler 127751678ccbSTomas Winkler if (slots < msg_slots) 127851678ccbSTomas Winkler return -EMSGSIZE; 127951678ccbSTomas Winkler 128051678ccbSTomas Winkler request = mei_cl_notify_fop2req(cb->fop_type); 128151678ccbSTomas Winkler ret = mei_hbm_cl_notify_req(dev, cl, request); 128251678ccbSTomas Winkler if (ret) { 128351678ccbSTomas Winkler cl->status = ret; 128451678ccbSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 128551678ccbSTomas Winkler return ret; 128651678ccbSTomas Winkler } 128751678ccbSTomas Winkler 128851678ccbSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 128951678ccbSTomas Winkler return 0; 129051678ccbSTomas Winkler } 129151678ccbSTomas Winkler 129251678ccbSTomas Winkler /** 129351678ccbSTomas Winkler * mei_cl_notify_request - send notification stop/start request 129451678ccbSTomas Winkler * 129551678ccbSTomas Winkler * @cl: host client 12963030dc05STomas Winkler * @fp: associate request with file 129751678ccbSTomas Winkler * @request: 1 for start or 0 for stop 129851678ccbSTomas Winkler * 129951678ccbSTomas Winkler * Locking: called under "dev->device_lock" lock 130051678ccbSTomas Winkler * 130151678ccbSTomas Winkler * Return: 0 on such and error otherwise. 130251678ccbSTomas Winkler */ 1303f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl, 13043030dc05STomas Winkler const struct file *fp, u8 request) 130551678ccbSTomas Winkler { 130651678ccbSTomas Winkler struct mei_device *dev; 130751678ccbSTomas Winkler struct mei_cl_cb *cb; 130851678ccbSTomas Winkler enum mei_cb_file_ops fop_type; 130951678ccbSTomas Winkler int rets; 131051678ccbSTomas Winkler 131151678ccbSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 131251678ccbSTomas Winkler return -ENODEV; 131351678ccbSTomas Winkler 131451678ccbSTomas Winkler dev = cl->dev; 131551678ccbSTomas Winkler 131651678ccbSTomas Winkler if (!dev->hbm_f_ev_supported) { 131751678ccbSTomas Winkler cl_dbg(dev, cl, "notifications not supported\n"); 131851678ccbSTomas Winkler return -EOPNOTSUPP; 131951678ccbSTomas Winkler } 132051678ccbSTomas Winkler 132151678ccbSTomas Winkler rets = pm_runtime_get(dev->dev); 132251678ccbSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 132351678ccbSTomas Winkler pm_runtime_put_noidle(dev->dev); 132451678ccbSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 132551678ccbSTomas Winkler return rets; 132651678ccbSTomas Winkler } 132751678ccbSTomas Winkler 132851678ccbSTomas Winkler fop_type = mei_cl_notify_req2fop(request); 13293030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp); 133051678ccbSTomas Winkler if (!cb) { 133151678ccbSTomas Winkler rets = -ENOMEM; 133251678ccbSTomas Winkler goto out; 133351678ccbSTomas Winkler } 133451678ccbSTomas Winkler 133551678ccbSTomas Winkler if (mei_hbuf_acquire(dev)) { 133651678ccbSTomas Winkler if (mei_hbm_cl_notify_req(dev, cl, request)) { 133751678ccbSTomas Winkler rets = -ENODEV; 133851678ccbSTomas Winkler goto out; 133951678ccbSTomas Winkler } 13403030dc05STomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 134151678ccbSTomas Winkler } 134251678ccbSTomas Winkler 134351678ccbSTomas Winkler mutex_unlock(&dev->device_lock); 13447ff4bdd4SAlexander Usyskin wait_event_timeout(cl->wait, 13457ff4bdd4SAlexander Usyskin cl->notify_en == request || !mei_cl_is_connected(cl), 134651678ccbSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 134751678ccbSTomas Winkler mutex_lock(&dev->device_lock); 134851678ccbSTomas Winkler 13494a8eaa96SAlexander Usyskin if (cl->notify_en != request && !cl->status) 135051678ccbSTomas Winkler cl->status = -EFAULT; 135151678ccbSTomas Winkler 135251678ccbSTomas Winkler rets = cl->status; 135351678ccbSTomas Winkler 135451678ccbSTomas Winkler out: 135551678ccbSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 135651678ccbSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 135751678ccbSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 135851678ccbSTomas Winkler 135951678ccbSTomas Winkler mei_io_cb_free(cb); 136051678ccbSTomas Winkler return rets; 136151678ccbSTomas Winkler } 136251678ccbSTomas Winkler 136351678ccbSTomas Winkler /** 1364237092bfSTomas Winkler * mei_cl_notify - raise notification 1365237092bfSTomas Winkler * 1366237092bfSTomas Winkler * @cl: host client 1367237092bfSTomas Winkler * 1368237092bfSTomas Winkler * Locking: called under "dev->device_lock" lock 1369237092bfSTomas Winkler */ 1370237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl) 1371237092bfSTomas Winkler { 1372237092bfSTomas Winkler struct mei_device *dev; 1373237092bfSTomas Winkler 1374237092bfSTomas Winkler if (!cl || !cl->dev) 1375237092bfSTomas Winkler return; 1376237092bfSTomas Winkler 1377237092bfSTomas Winkler dev = cl->dev; 1378237092bfSTomas Winkler 1379237092bfSTomas Winkler if (!cl->notify_en) 1380237092bfSTomas Winkler return; 1381237092bfSTomas Winkler 1382237092bfSTomas Winkler cl_dbg(dev, cl, "notify event"); 1383237092bfSTomas Winkler cl->notify_ev = true; 1384850f8940STomas Winkler if (!mei_cl_bus_notify_event(cl)) 1385850f8940STomas Winkler wake_up_interruptible(&cl->ev_wait); 1386237092bfSTomas Winkler 1387237092bfSTomas Winkler if (cl->ev_async) 1388237092bfSTomas Winkler kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); 1389bb2ef9c3SAlexander Usyskin 1390237092bfSTomas Winkler } 1391237092bfSTomas Winkler 1392237092bfSTomas Winkler /** 1393b38a362fSTomas Winkler * mei_cl_notify_get - get or wait for notification event 1394b38a362fSTomas Winkler * 1395b38a362fSTomas Winkler * @cl: host client 1396b38a362fSTomas Winkler * @block: this request is blocking 1397b38a362fSTomas Winkler * @notify_ev: true if notification event was received 1398b38a362fSTomas Winkler * 1399b38a362fSTomas Winkler * Locking: called under "dev->device_lock" lock 1400b38a362fSTomas Winkler * 1401b38a362fSTomas Winkler * Return: 0 on such and error otherwise. 1402b38a362fSTomas Winkler */ 1403b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) 1404b38a362fSTomas Winkler { 1405b38a362fSTomas Winkler struct mei_device *dev; 1406b38a362fSTomas Winkler int rets; 1407b38a362fSTomas Winkler 1408b38a362fSTomas Winkler *notify_ev = false; 1409b38a362fSTomas Winkler 1410b38a362fSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 1411b38a362fSTomas Winkler return -ENODEV; 1412b38a362fSTomas Winkler 1413b38a362fSTomas Winkler dev = cl->dev; 1414b38a362fSTomas Winkler 1415b38a362fSTomas Winkler if (!mei_cl_is_connected(cl)) 1416b38a362fSTomas Winkler return -ENODEV; 1417b38a362fSTomas Winkler 1418b38a362fSTomas Winkler if (cl->notify_ev) 1419b38a362fSTomas Winkler goto out; 1420b38a362fSTomas Winkler 1421b38a362fSTomas Winkler if (!block) 1422b38a362fSTomas Winkler return -EAGAIN; 1423b38a362fSTomas Winkler 1424b38a362fSTomas Winkler mutex_unlock(&dev->device_lock); 1425b38a362fSTomas Winkler rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); 1426b38a362fSTomas Winkler mutex_lock(&dev->device_lock); 1427b38a362fSTomas Winkler 1428b38a362fSTomas Winkler if (rets < 0) 1429b38a362fSTomas Winkler return rets; 1430b38a362fSTomas Winkler 1431b38a362fSTomas Winkler out: 1432b38a362fSTomas Winkler *notify_ev = cl->notify_ev; 1433b38a362fSTomas Winkler cl->notify_ev = false; 1434b38a362fSTomas Winkler return 0; 1435b38a362fSTomas Winkler } 1436b38a362fSTomas Winkler 1437b38a362fSTomas Winkler /** 1438393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 14399ca9050bSTomas Winkler * 144090e0b5f1STomas Winkler * @cl: host client 1441ce23139cSAlexander Usyskin * @length: number of bytes to read 1442bca67d68STomas Winkler * @fp: pointer to file structure 14439ca9050bSTomas Winkler * 1444a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 14459ca9050bSTomas Winkler */ 1446f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) 14479ca9050bSTomas Winkler { 144890e0b5f1STomas Winkler struct mei_device *dev; 14499ca9050bSTomas Winkler struct mei_cl_cb *cb; 14509ca9050bSTomas Winkler int rets; 14519ca9050bSTomas Winkler 145290e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 145390e0b5f1STomas Winkler return -ENODEV; 145490e0b5f1STomas Winkler 145590e0b5f1STomas Winkler dev = cl->dev; 145690e0b5f1STomas Winkler 1457b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 14589ca9050bSTomas Winkler return -ENODEV; 14599ca9050bSTomas Winkler 1460d49ed64aSAlexander Usyskin if (!mei_me_cl_is_active(cl->me_cl)) { 1461d49ed64aSAlexander Usyskin cl_err(dev, cl, "no such me client\n"); 14627ca96aa2SAlexander Usyskin return -ENOTTY; 14639ca9050bSTomas Winkler } 14641df629efSAlexander Usyskin 14659d27e73cSAlexander Usyskin if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl) 1466e51dfa5aSAlexander Usyskin return 0; 1467e51dfa5aSAlexander Usyskin 146846978adaSAlexander Usyskin /* HW currently supports only one pending read */ 146946978adaSAlexander Usyskin if (cl->rx_flow_ctrl_creds) 147046978adaSAlexander Usyskin return -EBUSY; 147146978adaSAlexander Usyskin 14723030dc05STomas Winkler cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp); 14731df629efSAlexander Usyskin if (!cb) 14741df629efSAlexander Usyskin return -ENOMEM; 14751df629efSAlexander Usyskin 14762bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 147704bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 14782bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 147904bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 14801df629efSAlexander Usyskin goto nortpm; 148104bb139aSTomas Winkler } 148204bb139aSTomas Winkler 148346978adaSAlexander Usyskin rets = 0; 14846aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 148586113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 148686113500SAlexander Usyskin if (rets < 0) 148704bb139aSTomas Winkler goto out; 148804bb139aSTomas Winkler 148946978adaSAlexander Usyskin list_move_tail(&cb->list, &cl->rd_pending); 14909ca9050bSTomas Winkler } 149146978adaSAlexander Usyskin cl->rx_flow_ctrl_creds++; 1492accb884bSChao Bi 149304bb139aSTomas Winkler out: 149404bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 14952bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 14962bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 14971df629efSAlexander Usyskin nortpm: 149804bb139aSTomas Winkler if (rets) 14999ca9050bSTomas Winkler mei_io_cb_free(cb); 150004bb139aSTomas Winkler 15019ca9050bSTomas Winkler return rets; 15029ca9050bSTomas Winkler } 15039ca9050bSTomas Winkler 1504074b4c01STomas Winkler /** 15059d098192STomas Winkler * mei_cl_irq_write - write a message to device 150621767546STomas Winkler * from the interrupt thread context 150721767546STomas Winkler * 150821767546STomas Winkler * @cl: client 150921767546STomas Winkler * @cb: callback block. 151021767546STomas Winkler * @cmpl_list: complete list. 151121767546STomas Winkler * 1512a8605ea2SAlexander Usyskin * Return: 0, OK; otherwise error. 151321767546STomas Winkler */ 15149d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 15159d098192STomas Winkler struct mei_cl_cb *cmpl_list) 151621767546STomas Winkler { 1517136698e5STomas Winkler struct mei_device *dev; 1518136698e5STomas Winkler struct mei_msg_data *buf; 151921767546STomas Winkler struct mei_msg_hdr mei_hdr; 1520136698e5STomas Winkler size_t len; 1521136698e5STomas Winkler u32 msg_slots; 15229d098192STomas Winkler int slots; 15232ebf8c94STomas Winkler int rets; 1524b8b73035SAlexander Usyskin bool first_chunk; 152521767546STomas Winkler 1526136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1527136698e5STomas Winkler return -ENODEV; 1528136698e5STomas Winkler 1529136698e5STomas Winkler dev = cl->dev; 1530136698e5STomas Winkler 15315db7514dSTomas Winkler buf = &cb->buf; 1532136698e5STomas Winkler 1533b8b73035SAlexander Usyskin first_chunk = cb->buf_idx == 0; 1534b8b73035SAlexander Usyskin 15354034b81bSTomas Winkler rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1; 1536136698e5STomas Winkler if (rets < 0) 1537136698e5STomas Winkler return rets; 1538136698e5STomas Winkler 1539136698e5STomas Winkler if (rets == 0) { 1540136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 1541136698e5STomas Winkler return 0; 1542136698e5STomas Winkler } 1543136698e5STomas Winkler 15449d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 1545136698e5STomas Winkler len = buf->size - cb->buf_idx; 1546136698e5STomas Winkler msg_slots = mei_data2slots(len); 1547136698e5STomas Winkler 15481df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1549d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 155021767546STomas Winkler mei_hdr.reserved = 0; 1551479327fcSTomas Winkler mei_hdr.internal = cb->internal; 155221767546STomas Winkler 15539d098192STomas Winkler if (slots >= msg_slots) { 155421767546STomas Winkler mei_hdr.length = len; 155521767546STomas Winkler mei_hdr.msg_complete = 1; 155621767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 15579d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 15589d098192STomas Winkler msg_slots = slots; 15599d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 156021767546STomas Winkler mei_hdr.length = len; 156121767546STomas Winkler mei_hdr.msg_complete = 0; 156221767546STomas Winkler } else { 156321767546STomas Winkler /* wait for next time the host buffer is empty */ 156421767546STomas Winkler return 0; 156521767546STomas Winkler } 156621767546STomas Winkler 156735bf7692SAlexander Usyskin cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n", 15685db7514dSTomas Winkler cb->buf.size, cb->buf_idx); 156921767546STomas Winkler 1570136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 15712ebf8c94STomas Winkler if (rets) { 15722ebf8c94STomas Winkler cl->status = rets; 157321767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 15742ebf8c94STomas Winkler return rets; 157521767546STomas Winkler } 157621767546STomas Winkler 157721767546STomas Winkler cl->status = 0; 15784dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 157921767546STomas Winkler cb->buf_idx += mei_hdr.length; 15808660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 15814dfaa9f7STomas Winkler 1582b8b73035SAlexander Usyskin if (first_chunk) { 15834034b81bSTomas Winkler if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) 15842ebf8c94STomas Winkler return -EIO; 158521767546STomas Winkler } 158621767546STomas Winkler 1587b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 1588b8b73035SAlexander Usyskin list_move_tail(&cb->list, &dev->write_waiting_list.list); 1589b8b73035SAlexander Usyskin 159021767546STomas Winkler return 0; 159121767546STomas Winkler } 159221767546STomas Winkler 159321767546STomas Winkler /** 15944234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 1595a8605ea2SAlexander Usyskin * assumes device_lock is locked 15964234a6deSTomas Winkler * 15974234a6deSTomas Winkler * @cl: host client 1598a8605ea2SAlexander Usyskin * @cb: write callback with filled data 1599ce23139cSAlexander Usyskin * @blocking: block until completed 16004234a6deSTomas Winkler * 1601a8605ea2SAlexander Usyskin * Return: number of bytes sent on success, <0 on failure. 16024234a6deSTomas Winkler */ 16034234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 16044234a6deSTomas Winkler { 16054234a6deSTomas Winkler struct mei_device *dev; 16064234a6deSTomas Winkler struct mei_msg_data *buf; 16074234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 160823253c31SAlexander Usyskin int size; 16094234a6deSTomas Winkler int rets; 16104234a6deSTomas Winkler 16114234a6deSTomas Winkler 16124234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 16134234a6deSTomas Winkler return -ENODEV; 16144234a6deSTomas Winkler 16154234a6deSTomas Winkler if (WARN_ON(!cb)) 16164234a6deSTomas Winkler return -EINVAL; 16174234a6deSTomas Winkler 16184234a6deSTomas Winkler dev = cl->dev; 16194234a6deSTomas Winkler 16205db7514dSTomas Winkler buf = &cb->buf; 162123253c31SAlexander Usyskin size = buf->size; 16224234a6deSTomas Winkler 162323253c31SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", size); 16244234a6deSTomas Winkler 16252bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 162604bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 16272bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 162804bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 16296cbb097fSAlexander Usyskin goto free; 163004bb139aSTomas Winkler } 16314234a6deSTomas Winkler 16326aae48ffSTomas Winkler cb->buf_idx = 0; 16336aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 16346aae48ffSTomas Winkler 16351df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1636d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 16376aae48ffSTomas Winkler mei_hdr.reserved = 0; 16386aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 16396aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 16404234a6deSTomas Winkler 16414034b81bSTomas Winkler rets = mei_cl_tx_flow_ctrl_creds(cl); 16424234a6deSTomas Winkler if (rets < 0) 16434234a6deSTomas Winkler goto err; 16444234a6deSTomas Winkler 16456aae48ffSTomas Winkler if (rets == 0) { 16466aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 164723253c31SAlexander Usyskin rets = size; 16484234a6deSTomas Winkler goto out; 16494234a6deSTomas Winkler } 16506aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 16516aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 165223253c31SAlexander Usyskin rets = size; 16536aae48ffSTomas Winkler goto out; 16546aae48ffSTomas Winkler } 16554234a6deSTomas Winkler 16564234a6deSTomas Winkler /* Check for a maximum length */ 165723253c31SAlexander Usyskin if (size > mei_hbuf_max_len(dev)) { 16584234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 16594234a6deSTomas Winkler mei_hdr.msg_complete = 0; 16604234a6deSTomas Winkler } else { 166123253c31SAlexander Usyskin mei_hdr.length = size; 16624234a6deSTomas Winkler mei_hdr.msg_complete = 1; 16634234a6deSTomas Winkler } 16644234a6deSTomas Winkler 16652ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 16662ebf8c94STomas Winkler if (rets) 16674234a6deSTomas Winkler goto err; 16684234a6deSTomas Winkler 16694034b81bSTomas Winkler rets = mei_cl_tx_flow_ctrl_creds_reduce(cl); 1670b8b73035SAlexander Usyskin if (rets) 1671b8b73035SAlexander Usyskin goto err; 1672b8b73035SAlexander Usyskin 16734234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 16744234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 16758660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 16764234a6deSTomas Winkler 16774234a6deSTomas Winkler out: 1678b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 16794234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 1680b8b73035SAlexander Usyskin else 16814234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 16824234a6deSTomas Winkler 168323253c31SAlexander Usyskin cb = NULL; 16844234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 16854234a6deSTomas Winkler 16864234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 16877ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 16880faf6a3bSAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE || 16890faf6a3bSAlexander Usyskin (!mei_cl_is_connected(cl))); 16907ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 16917ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 16927ca96aa2SAlexander Usyskin if (rets) { 16934234a6deSTomas Winkler if (signal_pending(current)) 16944234a6deSTomas Winkler rets = -EINTR; 16957ca96aa2SAlexander Usyskin goto err; 16964234a6deSTomas Winkler } 16970faf6a3bSAlexander Usyskin if (cl->writing_state != MEI_WRITE_COMPLETE) { 16980faf6a3bSAlexander Usyskin rets = -EFAULT; 16990faf6a3bSAlexander Usyskin goto err; 17000faf6a3bSAlexander Usyskin } 17014234a6deSTomas Winkler } 17027ca96aa2SAlexander Usyskin 170323253c31SAlexander Usyskin rets = size; 17044234a6deSTomas Winkler err: 170504bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 17062bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 17072bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 17086cbb097fSAlexander Usyskin free: 17096cbb097fSAlexander Usyskin mei_io_cb_free(cb); 171004bb139aSTomas Winkler 17114234a6deSTomas Winkler return rets; 17124234a6deSTomas Winkler } 17134234a6deSTomas Winkler 17144234a6deSTomas Winkler 1715db086fa9STomas Winkler /** 1716db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1717db086fa9STomas Winkler * 1718db086fa9STomas Winkler * @cl: private data of the file object. 1719db086fa9STomas Winkler * @cb: callback block. 1720db086fa9STomas Winkler */ 1721db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1722db086fa9STomas Winkler { 1723a1809d38SAlexander Usyskin struct mei_device *dev = cl->dev; 1724a1809d38SAlexander Usyskin 17253c666182STomas Winkler switch (cb->fop_type) { 17263c666182STomas Winkler case MEI_FOP_WRITE: 1727db086fa9STomas Winkler mei_io_cb_free(cb); 1728db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1729a1809d38SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 1730db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1731a1809d38SAlexander Usyskin } else { 1732a1809d38SAlexander Usyskin pm_runtime_mark_last_busy(dev->dev); 1733a1809d38SAlexander Usyskin pm_request_autosuspend(dev->dev); 1734a1809d38SAlexander Usyskin } 17353c666182STomas Winkler break; 1736db086fa9STomas Winkler 17373c666182STomas Winkler case MEI_FOP_READ: 1738a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_completed); 173946978adaSAlexander Usyskin if (!mei_cl_is_fixed_address(cl) && 174046978adaSAlexander Usyskin !WARN_ON(!cl->rx_flow_ctrl_creds)) 174146978adaSAlexander Usyskin cl->rx_flow_ctrl_creds--; 1742a1f9ae2bSTomas Winkler if (!mei_cl_bus_rx_event(cl)) 1743a1f9ae2bSTomas Winkler wake_up_interruptible(&cl->rx_wait); 17443c666182STomas Winkler break; 1745db086fa9STomas Winkler 17463c666182STomas Winkler case MEI_FOP_CONNECT: 17473c666182STomas Winkler case MEI_FOP_DISCONNECT: 174851678ccbSTomas Winkler case MEI_FOP_NOTIFY_STOP: 174951678ccbSTomas Winkler case MEI_FOP_NOTIFY_START: 17503c666182STomas Winkler if (waitqueue_active(&cl->wait)) 17513c666182STomas Winkler wake_up(&cl->wait); 17523c666182STomas Winkler 17533c666182STomas Winkler break; 17546a8d648cSAlexander Usyskin case MEI_FOP_DISCONNECT_RSP: 17556a8d648cSAlexander Usyskin mei_io_cb_free(cb); 17566a8d648cSAlexander Usyskin mei_cl_set_disconnected(cl); 17576a8d648cSAlexander Usyskin break; 17583c666182STomas Winkler default: 17593c666182STomas Winkler BUG_ON(0); 1760db086fa9STomas Winkler } 1761db086fa9STomas Winkler } 1762db086fa9STomas Winkler 17634234a6deSTomas Winkler 17644234a6deSTomas Winkler /** 1765074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1766074b4c01STomas Winkler * 1767a8605ea2SAlexander Usyskin * @dev: mei device 1768074b4c01STomas Winkler */ 1769074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1770074b4c01STomas Winkler { 177131f88f57STomas Winkler struct mei_cl *cl; 1772074b4c01STomas Winkler 17733c666182STomas Winkler list_for_each_entry(cl, &dev->file_list, link) 17743c666182STomas Winkler mei_cl_set_disconnected(cl); 1775074b4c01STomas Winkler } 1776