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 */ 361bca67d68STomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, 362f23e2cc4STomas Winkler const struct file *fp) 3639ca9050bSTomas Winkler { 3649ca9050bSTomas Winkler struct mei_cl_cb *cb; 3659ca9050bSTomas Winkler 3669ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 3679ca9050bSTomas Winkler if (!cb) 3689ca9050bSTomas Winkler return NULL; 3699ca9050bSTomas Winkler 370928fa666STomas Winkler INIT_LIST_HEAD(&cb->list); 37162e8e6adSTomas Winkler cb->fp = fp; 3729ca9050bSTomas Winkler cb->cl = cl; 3739ca9050bSTomas Winkler cb->buf_idx = 0; 374bca67d68STomas Winkler cb->fop_type = type; 3759ca9050bSTomas Winkler return cb; 3769ca9050bSTomas Winkler } 3779ca9050bSTomas Winkler 3789ca9050bSTomas Winkler /** 379928fa666STomas Winkler * __mei_io_list_flush - removes and frees cbs belonging to cl. 380928fa666STomas Winkler * 381928fa666STomas Winkler * @list: an instance of our list structure 382928fa666STomas Winkler * @cl: host client, can be NULL for flushing the whole list 383928fa666STomas Winkler * @free: whether to free the cbs 384928fa666STomas Winkler */ 385928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list, 386928fa666STomas Winkler struct mei_cl *cl, bool free) 387928fa666STomas Winkler { 388928fa666STomas Winkler struct mei_cl_cb *cb, *next; 389928fa666STomas Winkler 390928fa666STomas Winkler /* enable removing everything if no cl is specified */ 391928fa666STomas Winkler list_for_each_entry_safe(cb, next, &list->list, list) { 392928fa666STomas Winkler if (!cl || mei_cl_cmp_id(cl, cb->cl)) { 393928fa666STomas Winkler list_del_init(&cb->list); 394928fa666STomas Winkler if (free) 395928fa666STomas Winkler mei_io_cb_free(cb); 396928fa666STomas Winkler } 397928fa666STomas Winkler } 398928fa666STomas Winkler } 399928fa666STomas Winkler 400928fa666STomas Winkler /** 401928fa666STomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 402928fa666STomas Winkler * 403928fa666STomas Winkler * @list: An instance of our list structure 404928fa666STomas Winkler * @cl: host client 405928fa666STomas Winkler */ 406928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 407928fa666STomas Winkler { 408928fa666STomas Winkler __mei_io_list_flush(list, cl, false); 409928fa666STomas Winkler } 410928fa666STomas Winkler 411928fa666STomas Winkler /** 412928fa666STomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 413928fa666STomas Winkler * 414928fa666STomas Winkler * @list: An instance of our list structure 415928fa666STomas Winkler * @cl: host client 416928fa666STomas Winkler */ 417928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) 418928fa666STomas Winkler { 419928fa666STomas Winkler __mei_io_list_flush(list, cl, true); 420928fa666STomas Winkler } 421928fa666STomas Winkler 422928fa666STomas Winkler /** 4235db7514dSTomas Winkler * mei_io_cb_alloc_buf - allocate callback buffer 4249ca9050bSTomas Winkler * 425393b148fSMasanari Iida * @cb: io callback structure 426393b148fSMasanari Iida * @length: size of the buffer 4279ca9050bSTomas Winkler * 428a8605ea2SAlexander Usyskin * Return: 0 on success 4299ca9050bSTomas Winkler * -EINVAL if cb is NULL 4309ca9050bSTomas Winkler * -ENOMEM if allocation failed 4319ca9050bSTomas Winkler */ 4325db7514dSTomas Winkler int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) 4339ca9050bSTomas Winkler { 4349ca9050bSTomas Winkler if (!cb) 4359ca9050bSTomas Winkler return -EINVAL; 4369ca9050bSTomas Winkler 4379ca9050bSTomas Winkler if (length == 0) 4389ca9050bSTomas Winkler return 0; 4399ca9050bSTomas Winkler 4405db7514dSTomas Winkler cb->buf.data = kmalloc(length, GFP_KERNEL); 4415db7514dSTomas Winkler if (!cb->buf.data) 4429ca9050bSTomas Winkler return -ENOMEM; 4435db7514dSTomas Winkler cb->buf.size = length; 4449ca9050bSTomas Winkler return 0; 4459ca9050bSTomas Winkler } 4469ca9050bSTomas Winkler 4479ca9050bSTomas Winkler /** 448bca67d68STomas Winkler * mei_cl_alloc_cb - a convenient wrapper for allocating read cb 449bca67d68STomas Winkler * 450bca67d68STomas Winkler * @cl: host client 451bca67d68STomas Winkler * @length: size of the buffer 452bca67d68STomas Winkler * @type: operation type 453bca67d68STomas Winkler * @fp: associated file pointer (might be NULL) 454bca67d68STomas Winkler * 455bca67d68STomas Winkler * Return: cb on success and NULL on failure 456bca67d68STomas Winkler */ 457bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, 458f23e2cc4STomas Winkler enum mei_cb_file_ops type, 459f23e2cc4STomas Winkler const struct file *fp) 460bca67d68STomas Winkler { 461bca67d68STomas Winkler struct mei_cl_cb *cb; 462bca67d68STomas Winkler 463bca67d68STomas Winkler cb = mei_io_cb_init(cl, type, fp); 464bca67d68STomas Winkler if (!cb) 465bca67d68STomas Winkler return NULL; 466bca67d68STomas Winkler 467bca67d68STomas Winkler if (mei_io_cb_alloc_buf(cb, length)) { 468bca67d68STomas Winkler mei_io_cb_free(cb); 469bca67d68STomas Winkler return NULL; 470bca67d68STomas Winkler } 471bca67d68STomas Winkler 472bca67d68STomas Winkler return cb; 473bca67d68STomas Winkler } 474bca67d68STomas Winkler 475bca67d68STomas Winkler /** 476a9bed610STomas Winkler * mei_cl_read_cb - find this cl's callback in the read list 477a9bed610STomas Winkler * for a specific file 478a9bed610STomas Winkler * 479a9bed610STomas Winkler * @cl: host client 480a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 481a9bed610STomas Winkler * 482a9bed610STomas Winkler * Return: cb on success, NULL if cb is not found 483a9bed610STomas Winkler */ 484a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) 485a9bed610STomas Winkler { 486a9bed610STomas Winkler struct mei_cl_cb *cb; 487a9bed610STomas Winkler 488a9bed610STomas Winkler list_for_each_entry(cb, &cl->rd_completed, list) 48962e8e6adSTomas Winkler if (!fp || fp == cb->fp) 490a9bed610STomas Winkler return cb; 491a9bed610STomas Winkler 492a9bed610STomas Winkler return NULL; 493a9bed610STomas Winkler } 494a9bed610STomas Winkler 495a9bed610STomas Winkler /** 496a9bed610STomas Winkler * mei_cl_read_cb_flush - free client's read pending and completed cbs 497a9bed610STomas Winkler * for a specific file 498a9bed610STomas Winkler * 499a9bed610STomas Winkler * @cl: host client 500a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 501a9bed610STomas Winkler */ 502a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) 503a9bed610STomas Winkler { 504a9bed610STomas Winkler struct mei_cl_cb *cb, *next; 505a9bed610STomas Winkler 506a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_completed, list) 50762e8e6adSTomas Winkler if (!fp || fp == cb->fp) 508a9bed610STomas Winkler mei_io_cb_free(cb); 509a9bed610STomas Winkler 510a9bed610STomas Winkler 511a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_pending, list) 51262e8e6adSTomas Winkler if (!fp || fp == cb->fp) 513a9bed610STomas Winkler mei_io_cb_free(cb); 514a9bed610STomas Winkler } 515a9bed610STomas Winkler 516a9bed610STomas Winkler /** 5179ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 5189ca9050bSTomas Winkler * 5199ca9050bSTomas Winkler * @cl: host client 520a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 521ce23139cSAlexander Usyskin * 522ce23139cSAlexander Usyskin * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. 5239ca9050bSTomas Winkler */ 524a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) 5259ca9050bSTomas Winkler { 526c0abffbdSAlexander Usyskin struct mei_device *dev; 527c0abffbdSAlexander Usyskin 52890e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5299ca9050bSTomas Winkler return -EINVAL; 5309ca9050bSTomas Winkler 531c0abffbdSAlexander Usyskin dev = cl->dev; 532c0abffbdSAlexander Usyskin 533c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 534cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 535cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 5369ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 5379ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 5389ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 539a9bed610STomas Winkler 540a9bed610STomas Winkler mei_cl_read_cb_flush(cl, fp); 541a9bed610STomas Winkler 5429ca9050bSTomas Winkler return 0; 5439ca9050bSTomas Winkler } 5449ca9050bSTomas Winkler 5459ca9050bSTomas Winkler 5469ca9050bSTomas Winkler /** 54783ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 5489ca9050bSTomas Winkler * 5499ca9050bSTomas Winkler * @cl: host client to be initialized 5509ca9050bSTomas Winkler * @dev: mei device 5519ca9050bSTomas Winkler */ 5529ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 5539ca9050bSTomas Winkler { 5549ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 5559ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 5569ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 5579ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 558b38a362fSTomas Winkler init_waitqueue_head(&cl->ev_wait); 559a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_completed); 560a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_pending); 5619ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 5629ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 5633c666182STomas Winkler cl->state = MEI_FILE_INITIALIZING; 5649ca9050bSTomas Winkler cl->dev = dev; 5659ca9050bSTomas Winkler } 5669ca9050bSTomas Winkler 5679ca9050bSTomas Winkler /** 5689ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 5699ca9050bSTomas Winkler * 5709ca9050bSTomas Winkler * @dev: mei device 571a8605ea2SAlexander Usyskin * Return: The allocated file or NULL on failure 5729ca9050bSTomas Winkler */ 5739ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 5749ca9050bSTomas Winkler { 5759ca9050bSTomas Winkler struct mei_cl *cl; 5769ca9050bSTomas Winkler 5779ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 5789ca9050bSTomas Winkler if (!cl) 5799ca9050bSTomas Winkler return NULL; 5809ca9050bSTomas Winkler 5819ca9050bSTomas Winkler mei_cl_init(cl, dev); 5829ca9050bSTomas Winkler 5839ca9050bSTomas Winkler return cl; 5849ca9050bSTomas Winkler } 5859ca9050bSTomas Winkler 58690e0b5f1STomas Winkler /** 5873908be6fSAlexander Usyskin * mei_cl_link - allocate host id in the host map 5889ca9050bSTomas Winkler * 5893908be6fSAlexander Usyskin * @cl: host client 590393b148fSMasanari Iida * 591a8605ea2SAlexander Usyskin * Return: 0 on success 5929ca9050bSTomas Winkler * -EINVAL on incorrect values 59303b8d341STomas Winkler * -EMFILE if open count exceeded. 5949ca9050bSTomas Winkler */ 5957851e008SAlexander Usyskin int mei_cl_link(struct mei_cl *cl) 5969ca9050bSTomas Winkler { 59790e0b5f1STomas Winkler struct mei_device *dev; 59822f96a0eSTomas Winkler long open_handle_count; 5997851e008SAlexander Usyskin int id; 6009ca9050bSTomas Winkler 601781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 6029ca9050bSTomas Winkler return -EINVAL; 6039ca9050bSTomas Winkler 60490e0b5f1STomas Winkler dev = cl->dev; 60590e0b5f1STomas Winkler 6067851e008SAlexander Usyskin id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX); 607781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 6082bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 609e036cc57STomas Winkler return -EMFILE; 610e036cc57STomas Winkler } 611e036cc57STomas Winkler 61222f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 61322f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6142bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 615e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 616e036cc57STomas Winkler return -EMFILE; 6179ca9050bSTomas Winkler } 618781d0d89STomas Winkler 619781d0d89STomas Winkler dev->open_handle_count++; 620781d0d89STomas Winkler 621781d0d89STomas Winkler cl->host_client_id = id; 622781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 623781d0d89STomas Winkler 624781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 625781d0d89STomas Winkler 626781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 627781d0d89STomas Winkler 628c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 629781d0d89STomas Winkler return 0; 630781d0d89STomas Winkler } 631781d0d89STomas Winkler 6329ca9050bSTomas Winkler /** 633d49ed64aSAlexander Usyskin * mei_cl_unlink - remove host client from the list 6349ca9050bSTomas Winkler * 635393b148fSMasanari Iida * @cl: host client 636ce23139cSAlexander Usyskin * 637ce23139cSAlexander Usyskin * Return: always 0 6389ca9050bSTomas Winkler */ 63990e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 6409ca9050bSTomas Winkler { 64190e0b5f1STomas Winkler struct mei_device *dev; 64290e0b5f1STomas Winkler 643781d0d89STomas Winkler /* don't shout on error exit path */ 644781d0d89STomas Winkler if (!cl) 645781d0d89STomas Winkler return 0; 646781d0d89STomas Winkler 647fdd9b865SAlexander Usyskin /* amthif might not be initialized */ 6488e9a4a9aSTomas Winkler if (!cl->dev) 6498e9a4a9aSTomas Winkler return 0; 65090e0b5f1STomas Winkler 65190e0b5f1STomas Winkler dev = cl->dev; 65290e0b5f1STomas Winkler 653a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 654a14c44d8STomas Winkler 65522f96a0eSTomas Winkler if (dev->open_handle_count > 0) 65622f96a0eSTomas Winkler dev->open_handle_count--; 65722f96a0eSTomas Winkler 65822f96a0eSTomas Winkler /* never clear the 0 bit */ 65922f96a0eSTomas Winkler if (cl->host_client_id) 66022f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 66122f96a0eSTomas Winkler 66222f96a0eSTomas Winkler list_del_init(&cl->link); 66322f96a0eSTomas Winkler 66422f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 66522f96a0eSTomas Winkler 66690e0b5f1STomas Winkler return 0; 6679ca9050bSTomas Winkler } 6689ca9050bSTomas Winkler 669025fb792SAlexander Usyskin void mei_host_client_init(struct mei_device *dev) 6709ca9050bSTomas Winkler { 6719ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 6726adb8efbSTomas Winkler dev->reset_count = 0; 67304bb139aSTomas Winkler 674025fb792SAlexander Usyskin schedule_work(&dev->bus_rescan_work); 6756009595aSTomas Winkler 6762bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 6772bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 6782bf94cabSTomas Winkler pm_runtime_autosuspend(dev->dev); 6799ca9050bSTomas Winkler } 6809ca9050bSTomas Winkler 6816aae48ffSTomas Winkler /** 682a8605ea2SAlexander Usyskin * mei_hbuf_acquire - try to acquire host buffer 6836aae48ffSTomas Winkler * 6846aae48ffSTomas Winkler * @dev: the device structure 685a8605ea2SAlexander Usyskin * Return: true if host buffer was acquired 6866aae48ffSTomas Winkler */ 6876aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 6886aae48ffSTomas Winkler { 68904bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 6903dc196eaSAlexander Usyskin mei_pg_in_transition(dev)) { 6912bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 69204bb139aSTomas Winkler return false; 69304bb139aSTomas Winkler } 69404bb139aSTomas Winkler 6956aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 6962bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 6976aae48ffSTomas Winkler return false; 6986aae48ffSTomas Winkler } 6996aae48ffSTomas Winkler 7006aae48ffSTomas Winkler dev->hbuf_is_ready = false; 7016aae48ffSTomas Winkler 7026aae48ffSTomas Winkler return true; 7036aae48ffSTomas Winkler } 7049ca9050bSTomas Winkler 7059ca9050bSTomas Winkler /** 706a4307fe4SAlexander Usyskin * mei_cl_wake_all - wake up readers, writers and event waiters so 707a4307fe4SAlexander Usyskin * they can be interrupted 708a4307fe4SAlexander Usyskin * 709a4307fe4SAlexander Usyskin * @cl: host client 710a4307fe4SAlexander Usyskin */ 711a4307fe4SAlexander Usyskin static void mei_cl_wake_all(struct mei_cl *cl) 712a4307fe4SAlexander Usyskin { 713a4307fe4SAlexander Usyskin struct mei_device *dev = cl->dev; 714a4307fe4SAlexander Usyskin 715a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 716a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->rx_wait)) { 717a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 718a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->rx_wait); 719a4307fe4SAlexander Usyskin } 720a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 721a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 722a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 723a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->tx_wait); 724a4307fe4SAlexander Usyskin } 725a4307fe4SAlexander Usyskin /* synchronized under device mutex */ 726a4307fe4SAlexander Usyskin if (waitqueue_active(&cl->ev_wait)) { 727a4307fe4SAlexander Usyskin cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); 728a4307fe4SAlexander Usyskin wake_up_interruptible(&cl->ev_wait); 729a4307fe4SAlexander Usyskin } 730a4307fe4SAlexander Usyskin } 731a4307fe4SAlexander Usyskin 732a4307fe4SAlexander Usyskin /** 7333c666182STomas Winkler * mei_cl_set_disconnected - set disconnected state and clear 7343c666182STomas Winkler * associated states and resources 7353c666182STomas Winkler * 7363c666182STomas Winkler * @cl: host client 7373c666182STomas Winkler */ 7383c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl) 7393c666182STomas Winkler { 7403c666182STomas Winkler struct mei_device *dev = cl->dev; 7413c666182STomas Winkler 7423c666182STomas Winkler if (cl->state == MEI_FILE_DISCONNECTED || 7433c666182STomas Winkler cl->state == MEI_FILE_INITIALIZING) 7443c666182STomas Winkler return; 7453c666182STomas Winkler 7463c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 747a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_list, cl); 748a4307fe4SAlexander Usyskin mei_io_list_free(&dev->write_waiting_list, cl); 7493c666182STomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 7503c666182STomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 751a4307fe4SAlexander Usyskin mei_cl_wake_all(cl); 7523c666182STomas Winkler cl->mei_flow_ctrl_creds = 0; 7533c666182STomas Winkler cl->timer_count = 0; 754d49ed64aSAlexander Usyskin 755a03d77f6SAlexander Usyskin if (!cl->me_cl) 756a03d77f6SAlexander Usyskin return; 757a03d77f6SAlexander Usyskin 758a03d77f6SAlexander Usyskin if (!WARN_ON(cl->me_cl->connect_count == 0)) 759a03d77f6SAlexander Usyskin cl->me_cl->connect_count--; 760a03d77f6SAlexander Usyskin 761c241e9b1SAlexander Usyskin if (cl->me_cl->connect_count == 0) 762c241e9b1SAlexander Usyskin cl->me_cl->mei_flow_ctrl_creds = 0; 763c241e9b1SAlexander Usyskin 764d49ed64aSAlexander Usyskin mei_me_cl_put(cl->me_cl); 765d49ed64aSAlexander Usyskin cl->me_cl = NULL; 7663c666182STomas Winkler } 7673c666182STomas Winkler 768a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) 769a03d77f6SAlexander Usyskin { 7701df629efSAlexander Usyskin if (!mei_me_cl_get(me_cl)) 771a03d77f6SAlexander Usyskin return -ENOENT; 772a03d77f6SAlexander Usyskin 7731df629efSAlexander Usyskin /* only one connection is allowed for fixed address clients */ 7741df629efSAlexander Usyskin if (me_cl->props.fixed_address) { 7751df629efSAlexander Usyskin if (me_cl->connect_count) { 7761df629efSAlexander Usyskin mei_me_cl_put(me_cl); 7771df629efSAlexander Usyskin return -EBUSY; 7781df629efSAlexander Usyskin } 7791df629efSAlexander Usyskin } 7801df629efSAlexander Usyskin 7811df629efSAlexander Usyskin cl->me_cl = me_cl; 782a03d77f6SAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 783a03d77f6SAlexander Usyskin cl->me_cl->connect_count++; 784a03d77f6SAlexander Usyskin 785a03d77f6SAlexander Usyskin return 0; 786a03d77f6SAlexander Usyskin } 787a03d77f6SAlexander Usyskin 7883c666182STomas Winkler /* 7893c666182STomas Winkler * mei_cl_send_disconnect - send disconnect request 7903c666182STomas Winkler * 7913c666182STomas Winkler * @cl: host client 7923c666182STomas Winkler * @cb: callback block 7933c666182STomas Winkler * 7943c666182STomas Winkler * Return: 0, OK; otherwise, error. 7953c666182STomas Winkler */ 7963c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 7973c666182STomas Winkler { 7983c666182STomas Winkler struct mei_device *dev; 7993c666182STomas Winkler int ret; 8003c666182STomas Winkler 8013c666182STomas Winkler dev = cl->dev; 8023c666182STomas Winkler 8033c666182STomas Winkler ret = mei_hbm_cl_disconnect_req(dev, cl); 8043c666182STomas Winkler cl->status = ret; 8053c666182STomas Winkler if (ret) { 8063c666182STomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 8073c666182STomas Winkler return ret; 8083c666182STomas Winkler } 8093c666182STomas Winkler 8103c666182STomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 8113c666182STomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 8123c666182STomas Winkler 8133c666182STomas Winkler return 0; 8143c666182STomas Winkler } 8153c666182STomas Winkler 8163c666182STomas Winkler /** 8173c666182STomas Winkler * mei_cl_irq_disconnect - processes close related operation from 8183c666182STomas Winkler * interrupt thread context - send disconnect request 8193c666182STomas Winkler * 8203c666182STomas Winkler * @cl: client 8213c666182STomas Winkler * @cb: callback block. 8223c666182STomas Winkler * @cmpl_list: complete list. 8233c666182STomas Winkler * 8243c666182STomas Winkler * Return: 0, OK; otherwise, error. 8253c666182STomas Winkler */ 8263c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 8273c666182STomas Winkler struct mei_cl_cb *cmpl_list) 8283c666182STomas Winkler { 8293c666182STomas Winkler struct mei_device *dev = cl->dev; 8303c666182STomas Winkler u32 msg_slots; 8313c666182STomas Winkler int slots; 8323c666182STomas Winkler int ret; 8333c666182STomas Winkler 8343c666182STomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 8353c666182STomas Winkler slots = mei_hbuf_empty_slots(dev); 8363c666182STomas Winkler 8373c666182STomas Winkler if (slots < msg_slots) 8383c666182STomas Winkler return -EMSGSIZE; 8393c666182STomas Winkler 8403c666182STomas Winkler ret = mei_cl_send_disconnect(cl, cb); 8413c666182STomas Winkler if (ret) 8423c666182STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 8433c666182STomas Winkler 8443c666182STomas Winkler return ret; 8453c666182STomas Winkler } 8463c666182STomas Winkler 8473c666182STomas Winkler /** 84818901357SAlexander Usyskin * __mei_cl_disconnect - disconnect host client from the me one 84918901357SAlexander Usyskin * internal function runtime pm has to be already acquired 8509ca9050bSTomas Winkler * 85190e0b5f1STomas Winkler * @cl: host client 8529ca9050bSTomas Winkler * 853a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 8549ca9050bSTomas Winkler */ 85518901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl) 8569ca9050bSTomas Winkler { 85790e0b5f1STomas Winkler struct mei_device *dev; 8589ca9050bSTomas Winkler struct mei_cl_cb *cb; 859fe2f17ebSAlexander Usyskin int rets; 8609ca9050bSTomas Winkler 86190e0b5f1STomas Winkler dev = cl->dev; 86290e0b5f1STomas Winkler 8633c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTING; 8643c666182STomas Winkler 865bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); 866bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 867bca67d68STomas Winkler if (rets) 8683c666182STomas Winkler goto out; 8695a8373fbSTomas Winkler 870c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 8719ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 8729ca9050bSTomas Winkler 8733c666182STomas Winkler if (mei_hbuf_acquire(dev)) { 8743c666182STomas Winkler rets = mei_cl_send_disconnect(cl, cb); 8753c666182STomas Winkler if (rets) { 8763c666182STomas Winkler cl_err(dev, cl, "failed to disconnect.\n"); 8773c666182STomas Winkler goto out; 8789ca9050bSTomas Winkler } 8793c666182STomas Winkler } 8803c666182STomas Winkler 8819ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 8823c666182STomas Winkler wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY, 8839ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 8849ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 885fe2f17ebSAlexander Usyskin 8863c666182STomas Winkler rets = cl->status; 8873c666182STomas Winkler if (cl->state != MEI_FILE_DISCONNECT_REPLY) { 888fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 889fe2f17ebSAlexander Usyskin rets = -ETIME; 8909ca9050bSTomas Winkler } 8919ca9050bSTomas Winkler 8923c666182STomas Winkler out: 8933c666182STomas Winkler /* we disconnect also on error */ 8943c666182STomas Winkler mei_cl_set_disconnected(cl); 8953c666182STomas Winkler if (!rets) 8963c666182STomas Winkler cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 8973c666182STomas Winkler 89818901357SAlexander Usyskin mei_io_cb_free(cb); 89918901357SAlexander Usyskin return rets; 90018901357SAlexander Usyskin } 90118901357SAlexander Usyskin 90218901357SAlexander Usyskin /** 90318901357SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 90418901357SAlexander Usyskin * 90518901357SAlexander Usyskin * @cl: host client 90618901357SAlexander Usyskin * 90718901357SAlexander Usyskin * Locking: called under "dev->device_lock" lock 90818901357SAlexander Usyskin * 90918901357SAlexander Usyskin * Return: 0 on success, <0 on failure. 91018901357SAlexander Usyskin */ 91118901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl) 91218901357SAlexander Usyskin { 91318901357SAlexander Usyskin struct mei_device *dev; 91418901357SAlexander Usyskin int rets; 91518901357SAlexander Usyskin 91618901357SAlexander Usyskin if (WARN_ON(!cl || !cl->dev)) 91718901357SAlexander Usyskin return -ENODEV; 91818901357SAlexander Usyskin 91918901357SAlexander Usyskin dev = cl->dev; 92018901357SAlexander Usyskin 92118901357SAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 92218901357SAlexander Usyskin 92318901357SAlexander Usyskin if (!mei_cl_is_connected(cl)) 92418901357SAlexander Usyskin return 0; 92518901357SAlexander Usyskin 92618901357SAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 92718901357SAlexander Usyskin mei_cl_set_disconnected(cl); 92818901357SAlexander Usyskin return 0; 92918901357SAlexander Usyskin } 93018901357SAlexander Usyskin 93118901357SAlexander Usyskin rets = pm_runtime_get(dev->dev); 93218901357SAlexander Usyskin if (rets < 0 && rets != -EINPROGRESS) { 93318901357SAlexander Usyskin pm_runtime_put_noidle(dev->dev); 93418901357SAlexander Usyskin cl_err(dev, cl, "rpm: get failed %d\n", rets); 93518901357SAlexander Usyskin return rets; 93618901357SAlexander Usyskin } 93718901357SAlexander Usyskin 93818901357SAlexander Usyskin rets = __mei_cl_disconnect(cl); 93918901357SAlexander Usyskin 94004bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 9412bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 9422bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 94304bb139aSTomas Winkler 9449ca9050bSTomas Winkler return rets; 9459ca9050bSTomas Winkler } 9469ca9050bSTomas Winkler 9479ca9050bSTomas Winkler 9489ca9050bSTomas Winkler /** 94990e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 95090e0b5f1STomas Winkler * client with the same me client id is connecting 9519ca9050bSTomas Winkler * 9529ca9050bSTomas Winkler * @cl: private data of the file object 9539ca9050bSTomas Winkler * 954a8605ea2SAlexander Usyskin * Return: true if other client is connected, false - otherwise. 9559ca9050bSTomas Winkler */ 9560c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl) 9579ca9050bSTomas Winkler { 95890e0b5f1STomas Winkler struct mei_device *dev; 9590c53357cSTomas Winkler struct mei_cl_cb *cb; 96090e0b5f1STomas Winkler 96190e0b5f1STomas Winkler dev = cl->dev; 96290e0b5f1STomas Winkler 9630c53357cSTomas Winkler list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { 9640c53357cSTomas Winkler if (cb->fop_type == MEI_FOP_CONNECT && 965d49ed64aSAlexander Usyskin mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) 96690e0b5f1STomas Winkler return true; 9679ca9050bSTomas Winkler } 96890e0b5f1STomas Winkler 96990e0b5f1STomas Winkler return false; 9709ca9050bSTomas Winkler } 9719ca9050bSTomas Winkler 9729ca9050bSTomas Winkler /** 9730c53357cSTomas Winkler * mei_cl_send_connect - send connect request 9740c53357cSTomas Winkler * 9750c53357cSTomas Winkler * @cl: host client 9760c53357cSTomas Winkler * @cb: callback block 9770c53357cSTomas Winkler * 9780c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 9790c53357cSTomas Winkler */ 9800c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) 9810c53357cSTomas Winkler { 9820c53357cSTomas Winkler struct mei_device *dev; 9830c53357cSTomas Winkler int ret; 9840c53357cSTomas Winkler 9850c53357cSTomas Winkler dev = cl->dev; 9860c53357cSTomas Winkler 9870c53357cSTomas Winkler ret = mei_hbm_cl_connect_req(dev, cl); 9880c53357cSTomas Winkler cl->status = ret; 9890c53357cSTomas Winkler if (ret) { 9900c53357cSTomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 9910c53357cSTomas Winkler return ret; 9920c53357cSTomas Winkler } 9930c53357cSTomas Winkler 9940c53357cSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 9950c53357cSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 9960c53357cSTomas Winkler return 0; 9970c53357cSTomas Winkler } 9980c53357cSTomas Winkler 9990c53357cSTomas Winkler /** 10000c53357cSTomas Winkler * mei_cl_irq_connect - send connect request in irq_thread context 10010c53357cSTomas Winkler * 10020c53357cSTomas Winkler * @cl: host client 10030c53357cSTomas Winkler * @cb: callback block 10040c53357cSTomas Winkler * @cmpl_list: complete list 10050c53357cSTomas Winkler * 10060c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 10070c53357cSTomas Winkler */ 10080c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, 10090c53357cSTomas Winkler struct mei_cl_cb *cmpl_list) 10100c53357cSTomas Winkler { 10110c53357cSTomas Winkler struct mei_device *dev = cl->dev; 10120c53357cSTomas Winkler u32 msg_slots; 10130c53357cSTomas Winkler int slots; 10140c53357cSTomas Winkler int rets; 10150c53357cSTomas Winkler 10160c53357cSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 10170c53357cSTomas Winkler slots = mei_hbuf_empty_slots(dev); 10180c53357cSTomas Winkler 10190c53357cSTomas Winkler if (mei_cl_is_other_connecting(cl)) 10200c53357cSTomas Winkler return 0; 10210c53357cSTomas Winkler 10220c53357cSTomas Winkler if (slots < msg_slots) 10230c53357cSTomas Winkler return -EMSGSIZE; 10240c53357cSTomas Winkler 10250c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10260c53357cSTomas Winkler if (rets) 10270c53357cSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 10280c53357cSTomas Winkler 10290c53357cSTomas Winkler return rets; 10300c53357cSTomas Winkler } 10310c53357cSTomas Winkler 10320c53357cSTomas Winkler /** 103383ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 10349f81abdaSTomas Winkler * 10359f81abdaSTomas Winkler * @cl: host client 1036d49ed64aSAlexander Usyskin * @me_cl: me client 1037a8605ea2SAlexander Usyskin * @file: pointer to file structure 10389f81abdaSTomas Winkler * 10399f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 10409f81abdaSTomas Winkler * 1041a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 10429f81abdaSTomas Winkler */ 1043d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, 1044f23e2cc4STomas Winkler const struct file *file) 10459f81abdaSTomas Winkler { 10469f81abdaSTomas Winkler struct mei_device *dev; 10479f81abdaSTomas Winkler struct mei_cl_cb *cb; 10489f81abdaSTomas Winkler int rets; 10499f81abdaSTomas Winkler 10501df629efSAlexander Usyskin if (WARN_ON(!cl || !cl->dev || !me_cl)) 10519f81abdaSTomas Winkler return -ENODEV; 10529f81abdaSTomas Winkler 10539f81abdaSTomas Winkler dev = cl->dev; 10549f81abdaSTomas Winkler 10551df629efSAlexander Usyskin rets = mei_cl_set_connecting(cl, me_cl); 10561df629efSAlexander Usyskin if (rets) 10571df629efSAlexander Usyskin return rets; 10581df629efSAlexander Usyskin 10591df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 10601df629efSAlexander Usyskin cl->state = MEI_FILE_CONNECTED; 10611df629efSAlexander Usyskin return 0; 10621df629efSAlexander Usyskin } 10631df629efSAlexander Usyskin 10642bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 106504bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 10662bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 106704bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 10681df629efSAlexander Usyskin goto nortpm; 106904bb139aSTomas Winkler } 107004bb139aSTomas Winkler 1071bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); 1072bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 1073bca67d68STomas Winkler if (rets) 10749f81abdaSTomas Winkler goto out; 10759f81abdaSTomas Winkler 10760c53357cSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 10770c53357cSTomas Winkler 10786aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 10796aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 10800c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10810c53357cSTomas Winkler if (rets) 10829f81abdaSTomas Winkler goto out; 10839f81abdaSTomas Winkler } 10849f81abdaSTomas Winkler 10859f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 108612f45ed4STomas Winkler wait_event_timeout(cl->wait, 10879f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 108818901357SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REQUIRED || 10893c666182STomas Winkler cl->state == MEI_FILE_DISCONNECT_REPLY), 1090206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 10919f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 10929f81abdaSTomas Winkler 1093f3de9b63STomas Winkler if (!mei_cl_is_connected(cl)) { 109418901357SAlexander Usyskin if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { 109518901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_rd_list, cl); 109618901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_wr_list, cl); 109718901357SAlexander Usyskin /* ignore disconnect return valuue; 109818901357SAlexander Usyskin * in case of failure reset will be invoked 109918901357SAlexander Usyskin */ 110018901357SAlexander Usyskin __mei_cl_disconnect(cl); 110118901357SAlexander Usyskin rets = -EFAULT; 110218901357SAlexander Usyskin goto out; 110318901357SAlexander Usyskin } 110418901357SAlexander Usyskin 11050c53357cSTomas Winkler /* timeout or something went really wrong */ 1106285e2996SAlexander Usyskin if (!cl->status) 1107285e2996SAlexander Usyskin cl->status = -EFAULT; 11089f81abdaSTomas Winkler } 11099f81abdaSTomas Winkler 11109f81abdaSTomas Winkler rets = cl->status; 11119f81abdaSTomas Winkler out: 111204bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 11132bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 11142bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 111504bb139aSTomas Winkler 11169f81abdaSTomas Winkler mei_io_cb_free(cb); 11170c53357cSTomas Winkler 11181df629efSAlexander Usyskin nortpm: 11190c53357cSTomas Winkler if (!mei_cl_is_connected(cl)) 11200c53357cSTomas Winkler mei_cl_set_disconnected(cl); 11210c53357cSTomas Winkler 11229f81abdaSTomas Winkler return rets; 11239f81abdaSTomas Winkler } 11249f81abdaSTomas Winkler 11259f81abdaSTomas Winkler /** 112603b8d341STomas Winkler * mei_cl_alloc_linked - allocate and link host client 112703b8d341STomas Winkler * 112803b8d341STomas Winkler * @dev: the device structure 112903b8d341STomas Winkler * 113003b8d341STomas Winkler * Return: cl on success ERR_PTR on failure 113103b8d341STomas Winkler */ 11327851e008SAlexander Usyskin struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) 113303b8d341STomas Winkler { 113403b8d341STomas Winkler struct mei_cl *cl; 113503b8d341STomas Winkler int ret; 113603b8d341STomas Winkler 113703b8d341STomas Winkler cl = mei_cl_allocate(dev); 113803b8d341STomas Winkler if (!cl) { 113903b8d341STomas Winkler ret = -ENOMEM; 114003b8d341STomas Winkler goto err; 114103b8d341STomas Winkler } 114203b8d341STomas Winkler 11437851e008SAlexander Usyskin ret = mei_cl_link(cl); 114403b8d341STomas Winkler if (ret) 114503b8d341STomas Winkler goto err; 114603b8d341STomas Winkler 114703b8d341STomas Winkler return cl; 114803b8d341STomas Winkler err: 114903b8d341STomas Winkler kfree(cl); 115003b8d341STomas Winkler return ERR_PTR(ret); 115103b8d341STomas Winkler } 115203b8d341STomas Winkler 115303b8d341STomas Winkler 115403b8d341STomas Winkler 115503b8d341STomas Winkler /** 115690e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 11579ca9050bSTomas Winkler * 115806ee536bSAlexander Usyskin * @cl: host client 115906ee536bSAlexander Usyskin * @fp: the file pointer associated with the pointer 11609ca9050bSTomas Winkler * 1161a8605ea2SAlexander Usyskin * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 11629ca9050bSTomas Winkler */ 116306ee536bSAlexander Usyskin static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp) 11649ca9050bSTomas Winkler { 11651df629efSAlexander Usyskin int rets; 11661df629efSAlexander Usyskin 1167d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 116890e0b5f1STomas Winkler return -EINVAL; 116990e0b5f1STomas Winkler 11709ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 11719ca9050bSTomas Winkler return 1; 11729ca9050bSTomas Winkler 11731df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 117406ee536bSAlexander Usyskin rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp); 11751df629efSAlexander Usyskin if (rets && rets != -EBUSY) 11761df629efSAlexander Usyskin return rets; 11771df629efSAlexander Usyskin return 1; 11781df629efSAlexander Usyskin } 11791df629efSAlexander Usyskin 1180d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 1181d49ed64aSAlexander Usyskin if (cl->me_cl->mei_flow_ctrl_creds > 0) 1182d49ed64aSAlexander Usyskin return 1; 118312d00665SAlexander Usyskin } 1184d49ed64aSAlexander Usyskin return 0; 11859ca9050bSTomas Winkler } 11869ca9050bSTomas Winkler 11879ca9050bSTomas Winkler /** 118890e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 11899ca9050bSTomas Winkler * 11909ca9050bSTomas Winkler * @cl: private data of the file object 1191393b148fSMasanari Iida * 1192a8605ea2SAlexander Usyskin * Return: 11939ca9050bSTomas Winkler * 0 on success 11949ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 11959ca9050bSTomas Winkler */ 1196fdd9b865SAlexander Usyskin static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 11979ca9050bSTomas Winkler { 1198d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 119990e0b5f1STomas Winkler return -EINVAL; 120090e0b5f1STomas Winkler 12011df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 12021df629efSAlexander Usyskin return 0; 12031df629efSAlexander Usyskin 1204d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 1205d49ed64aSAlexander Usyskin if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0)) 1206d49ed64aSAlexander Usyskin return -EINVAL; 1207d49ed64aSAlexander Usyskin cl->me_cl->mei_flow_ctrl_creds--; 12089ca9050bSTomas Winkler } else { 1209d49ed64aSAlexander Usyskin if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 1210d49ed64aSAlexander Usyskin return -EINVAL; 12119ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 12129ca9050bSTomas Winkler } 1213d49ed64aSAlexander Usyskin return 0; 12149ca9050bSTomas Winkler } 12159ca9050bSTomas Winkler 12169ca9050bSTomas Winkler /** 121751678ccbSTomas Winkler * mei_cl_notify_fop2req - convert fop to proper request 121851678ccbSTomas Winkler * 121951678ccbSTomas Winkler * @fop: client notification start response command 122051678ccbSTomas Winkler * 122151678ccbSTomas Winkler * Return: MEI_HBM_NOTIFICATION_START/STOP 122251678ccbSTomas Winkler */ 122351678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) 122451678ccbSTomas Winkler { 122551678ccbSTomas Winkler if (fop == MEI_FOP_NOTIFY_START) 122651678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_START; 122751678ccbSTomas Winkler else 122851678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_STOP; 122951678ccbSTomas Winkler } 123051678ccbSTomas Winkler 123151678ccbSTomas Winkler /** 123251678ccbSTomas Winkler * mei_cl_notify_req2fop - convert notification request top file operation type 123351678ccbSTomas Winkler * 123451678ccbSTomas Winkler * @req: hbm notification request type 123551678ccbSTomas Winkler * 123651678ccbSTomas Winkler * Return: MEI_FOP_NOTIFY_START/STOP 123751678ccbSTomas Winkler */ 123851678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) 123951678ccbSTomas Winkler { 124051678ccbSTomas Winkler if (req == MEI_HBM_NOTIFICATION_START) 124151678ccbSTomas Winkler return MEI_FOP_NOTIFY_START; 124251678ccbSTomas Winkler else 124351678ccbSTomas Winkler return MEI_FOP_NOTIFY_STOP; 124451678ccbSTomas Winkler } 124551678ccbSTomas Winkler 124651678ccbSTomas Winkler /** 124751678ccbSTomas Winkler * mei_cl_irq_notify - send notification request in irq_thread context 124851678ccbSTomas Winkler * 124951678ccbSTomas Winkler * @cl: client 125051678ccbSTomas Winkler * @cb: callback block. 125151678ccbSTomas Winkler * @cmpl_list: complete list. 125251678ccbSTomas Winkler * 125351678ccbSTomas Winkler * Return: 0 on such and error otherwise. 125451678ccbSTomas Winkler */ 125551678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, 125651678ccbSTomas Winkler struct mei_cl_cb *cmpl_list) 125751678ccbSTomas Winkler { 125851678ccbSTomas Winkler struct mei_device *dev = cl->dev; 125951678ccbSTomas Winkler u32 msg_slots; 126051678ccbSTomas Winkler int slots; 126151678ccbSTomas Winkler int ret; 126251678ccbSTomas Winkler bool request; 126351678ccbSTomas Winkler 126451678ccbSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 126551678ccbSTomas Winkler slots = mei_hbuf_empty_slots(dev); 126651678ccbSTomas Winkler 126751678ccbSTomas Winkler if (slots < msg_slots) 126851678ccbSTomas Winkler return -EMSGSIZE; 126951678ccbSTomas Winkler 127051678ccbSTomas Winkler request = mei_cl_notify_fop2req(cb->fop_type); 127151678ccbSTomas Winkler ret = mei_hbm_cl_notify_req(dev, cl, request); 127251678ccbSTomas Winkler if (ret) { 127351678ccbSTomas Winkler cl->status = ret; 127451678ccbSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 127551678ccbSTomas Winkler return ret; 127651678ccbSTomas Winkler } 127751678ccbSTomas Winkler 127851678ccbSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 127951678ccbSTomas Winkler return 0; 128051678ccbSTomas Winkler } 128151678ccbSTomas Winkler 128251678ccbSTomas Winkler /** 128351678ccbSTomas Winkler * mei_cl_notify_request - send notification stop/start request 128451678ccbSTomas Winkler * 128551678ccbSTomas Winkler * @cl: host client 128651678ccbSTomas Winkler * @file: associate request with file 128751678ccbSTomas Winkler * @request: 1 for start or 0 for stop 128851678ccbSTomas Winkler * 128951678ccbSTomas Winkler * Locking: called under "dev->device_lock" lock 129051678ccbSTomas Winkler * 129151678ccbSTomas Winkler * Return: 0 on such and error otherwise. 129251678ccbSTomas Winkler */ 1293f23e2cc4STomas Winkler int mei_cl_notify_request(struct mei_cl *cl, 1294f23e2cc4STomas Winkler const struct file *file, u8 request) 129551678ccbSTomas Winkler { 129651678ccbSTomas Winkler struct mei_device *dev; 129751678ccbSTomas Winkler struct mei_cl_cb *cb; 129851678ccbSTomas Winkler enum mei_cb_file_ops fop_type; 129951678ccbSTomas Winkler int rets; 130051678ccbSTomas Winkler 130151678ccbSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 130251678ccbSTomas Winkler return -ENODEV; 130351678ccbSTomas Winkler 130451678ccbSTomas Winkler dev = cl->dev; 130551678ccbSTomas Winkler 130651678ccbSTomas Winkler if (!dev->hbm_f_ev_supported) { 130751678ccbSTomas Winkler cl_dbg(dev, cl, "notifications not supported\n"); 130851678ccbSTomas Winkler return -EOPNOTSUPP; 130951678ccbSTomas Winkler } 131051678ccbSTomas Winkler 131151678ccbSTomas Winkler rets = pm_runtime_get(dev->dev); 131251678ccbSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 131351678ccbSTomas Winkler pm_runtime_put_noidle(dev->dev); 131451678ccbSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 131551678ccbSTomas Winkler return rets; 131651678ccbSTomas Winkler } 131751678ccbSTomas Winkler 131851678ccbSTomas Winkler fop_type = mei_cl_notify_req2fop(request); 131951678ccbSTomas Winkler cb = mei_io_cb_init(cl, fop_type, file); 132051678ccbSTomas Winkler if (!cb) { 132151678ccbSTomas Winkler rets = -ENOMEM; 132251678ccbSTomas Winkler goto out; 132351678ccbSTomas Winkler } 132451678ccbSTomas Winkler 132551678ccbSTomas Winkler if (mei_hbuf_acquire(dev)) { 132651678ccbSTomas Winkler if (mei_hbm_cl_notify_req(dev, cl, request)) { 132751678ccbSTomas Winkler rets = -ENODEV; 132851678ccbSTomas Winkler goto out; 132951678ccbSTomas Winkler } 133051678ccbSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 133151678ccbSTomas Winkler } else { 133251678ccbSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 133351678ccbSTomas Winkler } 133451678ccbSTomas Winkler 133551678ccbSTomas Winkler mutex_unlock(&dev->device_lock); 133651678ccbSTomas Winkler wait_event_timeout(cl->wait, cl->notify_en == request, 133751678ccbSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 133851678ccbSTomas Winkler mutex_lock(&dev->device_lock); 133951678ccbSTomas Winkler 134051678ccbSTomas Winkler if (cl->notify_en != request) { 134151678ccbSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 134251678ccbSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 134351678ccbSTomas Winkler if (!cl->status) 134451678ccbSTomas Winkler cl->status = -EFAULT; 134551678ccbSTomas Winkler } 134651678ccbSTomas Winkler 134751678ccbSTomas Winkler rets = cl->status; 134851678ccbSTomas Winkler 134951678ccbSTomas Winkler out: 135051678ccbSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 135151678ccbSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 135251678ccbSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 135351678ccbSTomas Winkler 135451678ccbSTomas Winkler mei_io_cb_free(cb); 135551678ccbSTomas Winkler return rets; 135651678ccbSTomas Winkler } 135751678ccbSTomas Winkler 135851678ccbSTomas Winkler /** 1359237092bfSTomas Winkler * mei_cl_notify - raise notification 1360237092bfSTomas Winkler * 1361237092bfSTomas Winkler * @cl: host client 1362237092bfSTomas Winkler * 1363237092bfSTomas Winkler * Locking: called under "dev->device_lock" lock 1364237092bfSTomas Winkler */ 1365237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl) 1366237092bfSTomas Winkler { 1367237092bfSTomas Winkler struct mei_device *dev; 1368237092bfSTomas Winkler 1369237092bfSTomas Winkler if (!cl || !cl->dev) 1370237092bfSTomas Winkler return; 1371237092bfSTomas Winkler 1372237092bfSTomas Winkler dev = cl->dev; 1373237092bfSTomas Winkler 1374237092bfSTomas Winkler if (!cl->notify_en) 1375237092bfSTomas Winkler return; 1376237092bfSTomas Winkler 1377237092bfSTomas Winkler cl_dbg(dev, cl, "notify event"); 1378237092bfSTomas Winkler cl->notify_ev = true; 1379850f8940STomas Winkler if (!mei_cl_bus_notify_event(cl)) 1380850f8940STomas Winkler wake_up_interruptible(&cl->ev_wait); 1381237092bfSTomas Winkler 1382237092bfSTomas Winkler if (cl->ev_async) 1383237092bfSTomas Winkler kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); 1384bb2ef9c3SAlexander Usyskin 1385237092bfSTomas Winkler } 1386237092bfSTomas Winkler 1387237092bfSTomas Winkler /** 1388b38a362fSTomas Winkler * mei_cl_notify_get - get or wait for notification event 1389b38a362fSTomas Winkler * 1390b38a362fSTomas Winkler * @cl: host client 1391b38a362fSTomas Winkler * @block: this request is blocking 1392b38a362fSTomas Winkler * @notify_ev: true if notification event was received 1393b38a362fSTomas Winkler * 1394b38a362fSTomas Winkler * Locking: called under "dev->device_lock" lock 1395b38a362fSTomas Winkler * 1396b38a362fSTomas Winkler * Return: 0 on such and error otherwise. 1397b38a362fSTomas Winkler */ 1398b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) 1399b38a362fSTomas Winkler { 1400b38a362fSTomas Winkler struct mei_device *dev; 1401b38a362fSTomas Winkler int rets; 1402b38a362fSTomas Winkler 1403b38a362fSTomas Winkler *notify_ev = false; 1404b38a362fSTomas Winkler 1405b38a362fSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 1406b38a362fSTomas Winkler return -ENODEV; 1407b38a362fSTomas Winkler 1408b38a362fSTomas Winkler dev = cl->dev; 1409b38a362fSTomas Winkler 1410b38a362fSTomas Winkler if (!mei_cl_is_connected(cl)) 1411b38a362fSTomas Winkler return -ENODEV; 1412b38a362fSTomas Winkler 1413b38a362fSTomas Winkler if (cl->notify_ev) 1414b38a362fSTomas Winkler goto out; 1415b38a362fSTomas Winkler 1416b38a362fSTomas Winkler if (!block) 1417b38a362fSTomas Winkler return -EAGAIN; 1418b38a362fSTomas Winkler 1419b38a362fSTomas Winkler mutex_unlock(&dev->device_lock); 1420b38a362fSTomas Winkler rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); 1421b38a362fSTomas Winkler mutex_lock(&dev->device_lock); 1422b38a362fSTomas Winkler 1423b38a362fSTomas Winkler if (rets < 0) 1424b38a362fSTomas Winkler return rets; 1425b38a362fSTomas Winkler 1426b38a362fSTomas Winkler out: 1427b38a362fSTomas Winkler *notify_ev = cl->notify_ev; 1428b38a362fSTomas Winkler cl->notify_ev = false; 1429b38a362fSTomas Winkler return 0; 1430b38a362fSTomas Winkler } 1431b38a362fSTomas Winkler 1432b38a362fSTomas Winkler /** 143313cf9885SAlexander Usyskin * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control 143413cf9885SAlexander Usyskin * for given host client 143513cf9885SAlexander Usyskin * 143613cf9885SAlexander Usyskin * @cl: host client 143713cf9885SAlexander Usyskin * 143813cf9885SAlexander Usyskin * Return: true, if found at least one cb. 143913cf9885SAlexander Usyskin */ 144013cf9885SAlexander Usyskin static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) 144113cf9885SAlexander Usyskin { 144213cf9885SAlexander Usyskin struct mei_device *dev = cl->dev; 144313cf9885SAlexander Usyskin struct mei_cl_cb *cb; 144413cf9885SAlexander Usyskin 144513cf9885SAlexander Usyskin list_for_each_entry(cb, &dev->ctrl_wr_list.list, list) 144613cf9885SAlexander Usyskin if (cb->fop_type == MEI_FOP_READ && cb->cl == cl) 144713cf9885SAlexander Usyskin return true; 144813cf9885SAlexander Usyskin return false; 144913cf9885SAlexander Usyskin } 145013cf9885SAlexander Usyskin 145113cf9885SAlexander Usyskin /** 1452393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 14539ca9050bSTomas Winkler * 145490e0b5f1STomas Winkler * @cl: host client 1455ce23139cSAlexander Usyskin * @length: number of bytes to read 1456bca67d68STomas Winkler * @fp: pointer to file structure 14579ca9050bSTomas Winkler * 1458a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 14599ca9050bSTomas Winkler */ 1460f23e2cc4STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) 14619ca9050bSTomas Winkler { 146290e0b5f1STomas Winkler struct mei_device *dev; 14639ca9050bSTomas Winkler struct mei_cl_cb *cb; 14649ca9050bSTomas Winkler int rets; 14659ca9050bSTomas Winkler 146690e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 146790e0b5f1STomas Winkler return -ENODEV; 146890e0b5f1STomas Winkler 146990e0b5f1STomas Winkler dev = cl->dev; 147090e0b5f1STomas Winkler 1471b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 14729ca9050bSTomas Winkler return -ENODEV; 14739ca9050bSTomas Winkler 1474a9bed610STomas Winkler /* HW currently supports only one pending read */ 147513cf9885SAlexander Usyskin if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl)) 14769ca9050bSTomas Winkler return -EBUSY; 1477a9bed610STomas Winkler 1478d49ed64aSAlexander Usyskin if (!mei_me_cl_is_active(cl->me_cl)) { 1479d49ed64aSAlexander Usyskin cl_err(dev, cl, "no such me client\n"); 14807ca96aa2SAlexander Usyskin return -ENOTTY; 14819ca9050bSTomas Winkler } 14821df629efSAlexander Usyskin 148379563db9STomas Winkler /* always allocate at least client max message */ 1484d49ed64aSAlexander Usyskin length = max_t(size_t, length, mei_cl_mtu(cl)); 14851df629efSAlexander Usyskin cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); 14861df629efSAlexander Usyskin if (!cb) 14871df629efSAlexander Usyskin return -ENOMEM; 14881df629efSAlexander Usyskin 14891df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 14901df629efSAlexander Usyskin list_add_tail(&cb->list, &cl->rd_pending); 14911df629efSAlexander Usyskin return 0; 14921df629efSAlexander Usyskin } 14939ca9050bSTomas Winkler 14942bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 149504bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 14962bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 149704bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 14981df629efSAlexander Usyskin goto nortpm; 149904bb139aSTomas Winkler } 150004bb139aSTomas Winkler 15016aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 150286113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 150386113500SAlexander Usyskin if (rets < 0) 150404bb139aSTomas Winkler goto out; 150504bb139aSTomas Winkler 1506a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_pending); 15079ca9050bSTomas Winkler } else { 15081df629efSAlexander Usyskin rets = 0; 15099ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 15109ca9050bSTomas Winkler } 1511accb884bSChao Bi 151204bb139aSTomas Winkler out: 151304bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 15142bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 15152bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 15161df629efSAlexander Usyskin nortpm: 151704bb139aSTomas Winkler if (rets) 15189ca9050bSTomas Winkler mei_io_cb_free(cb); 151904bb139aSTomas Winkler 15209ca9050bSTomas Winkler return rets; 15219ca9050bSTomas Winkler } 15229ca9050bSTomas Winkler 1523074b4c01STomas Winkler /** 15249d098192STomas Winkler * mei_cl_irq_write - write a message to device 152521767546STomas Winkler * from the interrupt thread context 152621767546STomas Winkler * 152721767546STomas Winkler * @cl: client 152821767546STomas Winkler * @cb: callback block. 152921767546STomas Winkler * @cmpl_list: complete list. 153021767546STomas Winkler * 1531a8605ea2SAlexander Usyskin * Return: 0, OK; otherwise error. 153221767546STomas Winkler */ 15339d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 15349d098192STomas Winkler struct mei_cl_cb *cmpl_list) 153521767546STomas Winkler { 1536136698e5STomas Winkler struct mei_device *dev; 1537136698e5STomas Winkler struct mei_msg_data *buf; 153821767546STomas Winkler struct mei_msg_hdr mei_hdr; 1539136698e5STomas Winkler size_t len; 1540136698e5STomas Winkler u32 msg_slots; 15419d098192STomas Winkler int slots; 15422ebf8c94STomas Winkler int rets; 1543b8b73035SAlexander Usyskin bool first_chunk; 154421767546STomas Winkler 1545136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1546136698e5STomas Winkler return -ENODEV; 1547136698e5STomas Winkler 1548136698e5STomas Winkler dev = cl->dev; 1549136698e5STomas Winkler 15505db7514dSTomas Winkler buf = &cb->buf; 1551136698e5STomas Winkler 1552b8b73035SAlexander Usyskin first_chunk = cb->buf_idx == 0; 1553b8b73035SAlexander Usyskin 155406ee536bSAlexander Usyskin rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1; 1555136698e5STomas Winkler if (rets < 0) 1556136698e5STomas Winkler return rets; 1557136698e5STomas Winkler 1558136698e5STomas Winkler if (rets == 0) { 1559136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 1560136698e5STomas Winkler return 0; 1561136698e5STomas Winkler } 1562136698e5STomas Winkler 15639d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 1564136698e5STomas Winkler len = buf->size - cb->buf_idx; 1565136698e5STomas Winkler msg_slots = mei_data2slots(len); 1566136698e5STomas Winkler 15671df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1568d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 156921767546STomas Winkler mei_hdr.reserved = 0; 1570479327fcSTomas Winkler mei_hdr.internal = cb->internal; 157121767546STomas Winkler 15729d098192STomas Winkler if (slots >= msg_slots) { 157321767546STomas Winkler mei_hdr.length = len; 157421767546STomas Winkler mei_hdr.msg_complete = 1; 157521767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 15769d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 15779d098192STomas Winkler msg_slots = slots; 15789d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 157921767546STomas Winkler mei_hdr.length = len; 158021767546STomas Winkler mei_hdr.msg_complete = 0; 158121767546STomas Winkler } else { 158221767546STomas Winkler /* wait for next time the host buffer is empty */ 158321767546STomas Winkler return 0; 158421767546STomas Winkler } 158521767546STomas Winkler 158635bf7692SAlexander Usyskin cl_dbg(dev, cl, "buf: size = %zu idx = %zu\n", 15875db7514dSTomas Winkler cb->buf.size, cb->buf_idx); 158821767546STomas Winkler 1589136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 15902ebf8c94STomas Winkler if (rets) { 15912ebf8c94STomas Winkler cl->status = rets; 159221767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 15932ebf8c94STomas Winkler return rets; 159421767546STomas Winkler } 159521767546STomas Winkler 159621767546STomas Winkler cl->status = 0; 15974dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 159821767546STomas Winkler cb->buf_idx += mei_hdr.length; 15998660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 16004dfaa9f7STomas Winkler 1601b8b73035SAlexander Usyskin if (first_chunk) { 160221767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 16032ebf8c94STomas Winkler return -EIO; 160421767546STomas Winkler } 160521767546STomas Winkler 1606b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 1607b8b73035SAlexander Usyskin list_move_tail(&cb->list, &dev->write_waiting_list.list); 1608b8b73035SAlexander Usyskin 160921767546STomas Winkler return 0; 161021767546STomas Winkler } 161121767546STomas Winkler 161221767546STomas Winkler /** 16134234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 1614a8605ea2SAlexander Usyskin * assumes device_lock is locked 16154234a6deSTomas Winkler * 16164234a6deSTomas Winkler * @cl: host client 1617a8605ea2SAlexander Usyskin * @cb: write callback with filled data 1618ce23139cSAlexander Usyskin * @blocking: block until completed 16194234a6deSTomas Winkler * 1620a8605ea2SAlexander Usyskin * Return: number of bytes sent on success, <0 on failure. 16214234a6deSTomas Winkler */ 16224234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 16234234a6deSTomas Winkler { 16244234a6deSTomas Winkler struct mei_device *dev; 16254234a6deSTomas Winkler struct mei_msg_data *buf; 16264234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 162723253c31SAlexander Usyskin int size; 16284234a6deSTomas Winkler int rets; 16294234a6deSTomas Winkler 16304234a6deSTomas Winkler 16314234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 16324234a6deSTomas Winkler return -ENODEV; 16334234a6deSTomas Winkler 16344234a6deSTomas Winkler if (WARN_ON(!cb)) 16354234a6deSTomas Winkler return -EINVAL; 16364234a6deSTomas Winkler 16374234a6deSTomas Winkler dev = cl->dev; 16384234a6deSTomas Winkler 16395db7514dSTomas Winkler buf = &cb->buf; 164023253c31SAlexander Usyskin size = buf->size; 16414234a6deSTomas Winkler 164223253c31SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", size); 16434234a6deSTomas Winkler 16442bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 164504bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 16462bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 164704bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 16486cbb097fSAlexander Usyskin goto free; 164904bb139aSTomas Winkler } 16504234a6deSTomas Winkler 16516aae48ffSTomas Winkler cb->buf_idx = 0; 16526aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 16536aae48ffSTomas Winkler 16541df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1655d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 16566aae48ffSTomas Winkler mei_hdr.reserved = 0; 16576aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 16586aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 16594234a6deSTomas Winkler 166006ee536bSAlexander Usyskin rets = mei_cl_flow_ctrl_creds(cl, cb->fp); 16614234a6deSTomas Winkler if (rets < 0) 16624234a6deSTomas Winkler goto err; 16634234a6deSTomas Winkler 16646aae48ffSTomas Winkler if (rets == 0) { 16656aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 166623253c31SAlexander Usyskin rets = size; 16674234a6deSTomas Winkler goto out; 16684234a6deSTomas Winkler } 16696aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 16706aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 167123253c31SAlexander Usyskin rets = size; 16726aae48ffSTomas Winkler goto out; 16736aae48ffSTomas Winkler } 16744234a6deSTomas Winkler 16754234a6deSTomas Winkler /* Check for a maximum length */ 167623253c31SAlexander Usyskin if (size > mei_hbuf_max_len(dev)) { 16774234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 16784234a6deSTomas Winkler mei_hdr.msg_complete = 0; 16794234a6deSTomas Winkler } else { 168023253c31SAlexander Usyskin mei_hdr.length = size; 16814234a6deSTomas Winkler mei_hdr.msg_complete = 1; 16824234a6deSTomas Winkler } 16834234a6deSTomas Winkler 16842ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 16852ebf8c94STomas Winkler if (rets) 16864234a6deSTomas Winkler goto err; 16874234a6deSTomas Winkler 1688b8b73035SAlexander Usyskin rets = mei_cl_flow_ctrl_reduce(cl); 1689b8b73035SAlexander Usyskin if (rets) 1690b8b73035SAlexander Usyskin goto err; 1691b8b73035SAlexander Usyskin 16924234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 16934234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 16948660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 16954234a6deSTomas Winkler 16964234a6deSTomas Winkler out: 1697b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 16984234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 1699b8b73035SAlexander Usyskin else 17004234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 17014234a6deSTomas Winkler 170223253c31SAlexander Usyskin cb = NULL; 17034234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 17044234a6deSTomas Winkler 17054234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 17067ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 17070faf6a3bSAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE || 17080faf6a3bSAlexander Usyskin (!mei_cl_is_connected(cl))); 17097ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 17107ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 17117ca96aa2SAlexander Usyskin if (rets) { 17124234a6deSTomas Winkler if (signal_pending(current)) 17134234a6deSTomas Winkler rets = -EINTR; 17147ca96aa2SAlexander Usyskin goto err; 17154234a6deSTomas Winkler } 17160faf6a3bSAlexander Usyskin if (cl->writing_state != MEI_WRITE_COMPLETE) { 17170faf6a3bSAlexander Usyskin rets = -EFAULT; 17180faf6a3bSAlexander Usyskin goto err; 17190faf6a3bSAlexander Usyskin } 17204234a6deSTomas Winkler } 17217ca96aa2SAlexander Usyskin 172223253c31SAlexander Usyskin rets = size; 17234234a6deSTomas Winkler err: 172404bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 17252bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 17262bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 17276cbb097fSAlexander Usyskin free: 17286cbb097fSAlexander Usyskin mei_io_cb_free(cb); 172904bb139aSTomas Winkler 17304234a6deSTomas Winkler return rets; 17314234a6deSTomas Winkler } 17324234a6deSTomas Winkler 17334234a6deSTomas Winkler 1734db086fa9STomas Winkler /** 1735db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1736db086fa9STomas Winkler * 1737db086fa9STomas Winkler * @cl: private data of the file object. 1738db086fa9STomas Winkler * @cb: callback block. 1739db086fa9STomas Winkler */ 1740db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1741db086fa9STomas Winkler { 1742a1809d38SAlexander Usyskin struct mei_device *dev = cl->dev; 1743a1809d38SAlexander Usyskin 17443c666182STomas Winkler switch (cb->fop_type) { 17453c666182STomas Winkler case MEI_FOP_WRITE: 1746db086fa9STomas Winkler mei_io_cb_free(cb); 1747db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1748a1809d38SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 1749db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1750a1809d38SAlexander Usyskin } else { 1751a1809d38SAlexander Usyskin pm_runtime_mark_last_busy(dev->dev); 1752a1809d38SAlexander Usyskin pm_request_autosuspend(dev->dev); 1753a1809d38SAlexander Usyskin } 17543c666182STomas Winkler break; 1755db086fa9STomas Winkler 17563c666182STomas Winkler case MEI_FOP_READ: 1757a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_completed); 1758a1f9ae2bSTomas Winkler if (!mei_cl_bus_rx_event(cl)) 1759a1f9ae2bSTomas Winkler wake_up_interruptible(&cl->rx_wait); 17603c666182STomas Winkler break; 1761db086fa9STomas Winkler 17623c666182STomas Winkler case MEI_FOP_CONNECT: 17633c666182STomas Winkler case MEI_FOP_DISCONNECT: 176451678ccbSTomas Winkler case MEI_FOP_NOTIFY_STOP: 176551678ccbSTomas Winkler case MEI_FOP_NOTIFY_START: 17663c666182STomas Winkler if (waitqueue_active(&cl->wait)) 17673c666182STomas Winkler wake_up(&cl->wait); 17683c666182STomas Winkler 17693c666182STomas Winkler break; 17703c666182STomas Winkler default: 17713c666182STomas Winkler BUG_ON(0); 1772db086fa9STomas Winkler } 1773db086fa9STomas Winkler } 1774db086fa9STomas Winkler 17754234a6deSTomas Winkler 17764234a6deSTomas Winkler /** 1777074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1778074b4c01STomas Winkler * 1779a8605ea2SAlexander Usyskin * @dev: mei device 1780074b4c01STomas Winkler */ 1781074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1782074b4c01STomas Winkler { 178331f88f57STomas Winkler struct mei_cl *cl; 1784074b4c01STomas Winkler 17853c666182STomas Winkler list_for_each_entry(cl, &dev->file_list, link) 17863c666182STomas Winkler mei_cl_set_disconnected(cl); 1787074b4c01STomas Winkler } 1788