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, 362bca67d68STomas Winkler 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); 3719ca9050bSTomas Winkler cb->file_object = 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, 458bca67d68STomas Winkler enum mei_cb_file_ops type, struct file *fp) 459bca67d68STomas Winkler { 460bca67d68STomas Winkler struct mei_cl_cb *cb; 461bca67d68STomas Winkler 462bca67d68STomas Winkler cb = mei_io_cb_init(cl, type, fp); 463bca67d68STomas Winkler if (!cb) 464bca67d68STomas Winkler return NULL; 465bca67d68STomas Winkler 466bca67d68STomas Winkler if (mei_io_cb_alloc_buf(cb, length)) { 467bca67d68STomas Winkler mei_io_cb_free(cb); 468bca67d68STomas Winkler return NULL; 469bca67d68STomas Winkler } 470bca67d68STomas Winkler 471bca67d68STomas Winkler return cb; 472bca67d68STomas Winkler } 473bca67d68STomas Winkler 474bca67d68STomas Winkler /** 475a9bed610STomas Winkler * mei_cl_read_cb - find this cl's callback in the read list 476a9bed610STomas Winkler * for a specific file 477a9bed610STomas Winkler * 478a9bed610STomas Winkler * @cl: host client 479a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 480a9bed610STomas Winkler * 481a9bed610STomas Winkler * Return: cb on success, NULL if cb is not found 482a9bed610STomas Winkler */ 483a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) 484a9bed610STomas Winkler { 485a9bed610STomas Winkler struct mei_cl_cb *cb; 486a9bed610STomas Winkler 487a9bed610STomas Winkler list_for_each_entry(cb, &cl->rd_completed, list) 488a9bed610STomas Winkler if (!fp || fp == cb->file_object) 489a9bed610STomas Winkler return cb; 490a9bed610STomas Winkler 491a9bed610STomas Winkler return NULL; 492a9bed610STomas Winkler } 493a9bed610STomas Winkler 494a9bed610STomas Winkler /** 495a9bed610STomas Winkler * mei_cl_read_cb_flush - free client's read pending and completed cbs 496a9bed610STomas Winkler * for a specific file 497a9bed610STomas Winkler * 498a9bed610STomas Winkler * @cl: host client 499a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 500a9bed610STomas Winkler */ 501a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) 502a9bed610STomas Winkler { 503a9bed610STomas Winkler struct mei_cl_cb *cb, *next; 504a9bed610STomas Winkler 505a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_completed, list) 506a9bed610STomas Winkler if (!fp || fp == cb->file_object) 507a9bed610STomas Winkler mei_io_cb_free(cb); 508a9bed610STomas Winkler 509a9bed610STomas Winkler 510a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_pending, list) 511a9bed610STomas Winkler if (!fp || fp == cb->file_object) 512a9bed610STomas Winkler mei_io_cb_free(cb); 513a9bed610STomas Winkler } 514a9bed610STomas Winkler 515a9bed610STomas Winkler /** 5169ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 5179ca9050bSTomas Winkler * 5189ca9050bSTomas Winkler * @cl: host client 519a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 520ce23139cSAlexander Usyskin * 521ce23139cSAlexander Usyskin * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. 5229ca9050bSTomas Winkler */ 523a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) 5249ca9050bSTomas Winkler { 525c0abffbdSAlexander Usyskin struct mei_device *dev; 526c0abffbdSAlexander Usyskin 52790e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5289ca9050bSTomas Winkler return -EINVAL; 5299ca9050bSTomas Winkler 530c0abffbdSAlexander Usyskin dev = cl->dev; 531c0abffbdSAlexander Usyskin 532c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 533cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 534cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 5359ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 5369ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 5379ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 5389ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_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 59003b8d341STomas Winkler * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one 591393b148fSMasanari Iida * 592a8605ea2SAlexander Usyskin * Return: 0 on success 5939ca9050bSTomas Winkler * -EINVAL on incorrect values 59403b8d341STomas Winkler * -EMFILE if open count exceeded. 5959ca9050bSTomas Winkler */ 596781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 5979ca9050bSTomas Winkler { 59890e0b5f1STomas Winkler struct mei_device *dev; 59922f96a0eSTomas Winkler long open_handle_count; 6009ca9050bSTomas Winkler 601781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 6029ca9050bSTomas Winkler return -EINVAL; 6039ca9050bSTomas Winkler 60490e0b5f1STomas Winkler dev = cl->dev; 60590e0b5f1STomas Winkler 60683ce0741SAlexander Usyskin /* If Id is not assigned get one*/ 607781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 608781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 609781d0d89STomas Winkler MEI_CLIENTS_MAX); 6109ca9050bSTomas Winkler 611781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 6122bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 613e036cc57STomas Winkler return -EMFILE; 614e036cc57STomas Winkler } 615e036cc57STomas Winkler 61622f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 61722f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6182bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 619e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 620e036cc57STomas Winkler return -EMFILE; 6219ca9050bSTomas Winkler } 622781d0d89STomas Winkler 623781d0d89STomas Winkler dev->open_handle_count++; 624781d0d89STomas Winkler 625781d0d89STomas Winkler cl->host_client_id = id; 626781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 627781d0d89STomas Winkler 628781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 629781d0d89STomas Winkler 630781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 631781d0d89STomas Winkler 632c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 633781d0d89STomas Winkler return 0; 634781d0d89STomas Winkler } 635781d0d89STomas Winkler 6369ca9050bSTomas Winkler /** 637d49ed64aSAlexander Usyskin * mei_cl_unlink - remove host client from the list 6389ca9050bSTomas Winkler * 639393b148fSMasanari Iida * @cl: host client 640ce23139cSAlexander Usyskin * 641ce23139cSAlexander Usyskin * Return: always 0 6429ca9050bSTomas Winkler */ 64390e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 6449ca9050bSTomas Winkler { 64590e0b5f1STomas Winkler struct mei_device *dev; 64690e0b5f1STomas Winkler 647781d0d89STomas Winkler /* don't shout on error exit path */ 648781d0d89STomas Winkler if (!cl) 649781d0d89STomas Winkler return 0; 650781d0d89STomas Winkler 6518e9a4a9aSTomas Winkler /* wd and amthif might not be initialized */ 6528e9a4a9aSTomas Winkler if (!cl->dev) 6538e9a4a9aSTomas Winkler return 0; 65490e0b5f1STomas Winkler 65590e0b5f1STomas Winkler dev = cl->dev; 65690e0b5f1STomas Winkler 657a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 658a14c44d8STomas Winkler 65922f96a0eSTomas Winkler if (dev->open_handle_count > 0) 66022f96a0eSTomas Winkler dev->open_handle_count--; 66122f96a0eSTomas Winkler 66222f96a0eSTomas Winkler /* never clear the 0 bit */ 66322f96a0eSTomas Winkler if (cl->host_client_id) 66422f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 66522f96a0eSTomas Winkler 66622f96a0eSTomas Winkler list_del_init(&cl->link); 66722f96a0eSTomas Winkler 66822f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 66922f96a0eSTomas Winkler 67090e0b5f1STomas Winkler return 0; 6719ca9050bSTomas Winkler } 6729ca9050bSTomas Winkler 6739ca9050bSTomas Winkler 6749ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 6759ca9050bSTomas Winkler { 676b7d88514STomas Winkler struct mei_device *dev = 677b7d88514STomas Winkler container_of(work, struct mei_device, init_work); 6785ca2d388STomas Winkler struct mei_me_client *me_cl; 6799ca9050bSTomas Winkler 6809ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 6819ca9050bSTomas Winkler 6829ca9050bSTomas Winkler 683b7d88514STomas Winkler me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); 684b7d88514STomas Winkler if (me_cl) 685d49ed64aSAlexander Usyskin mei_amthif_host_init(dev, me_cl); 686b43baf69STomas Winkler mei_me_cl_put(me_cl); 687b7d88514STomas Winkler 688b7d88514STomas Winkler me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); 689b7d88514STomas Winkler if (me_cl) 690d49ed64aSAlexander Usyskin mei_wd_host_init(dev, me_cl); 691b43baf69STomas Winkler mei_me_cl_put(me_cl); 692b7d88514STomas Winkler 6939ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 6946adb8efbSTomas Winkler dev->reset_count = 0; 6959ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 69604bb139aSTomas Winkler 6976009595aSTomas Winkler mei_cl_bus_rescan(dev); 6986009595aSTomas Winkler 6992bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 7002bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 7012bf94cabSTomas Winkler pm_runtime_autosuspend(dev->dev); 7029ca9050bSTomas Winkler } 7039ca9050bSTomas Winkler 7046aae48ffSTomas Winkler /** 705a8605ea2SAlexander Usyskin * mei_hbuf_acquire - try to acquire host buffer 7066aae48ffSTomas Winkler * 7076aae48ffSTomas Winkler * @dev: the device structure 708a8605ea2SAlexander Usyskin * Return: true if host buffer was acquired 7096aae48ffSTomas Winkler */ 7106aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 7116aae48ffSTomas Winkler { 71204bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 7133dc196eaSAlexander Usyskin mei_pg_in_transition(dev)) { 7142bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 71504bb139aSTomas Winkler return false; 71604bb139aSTomas Winkler } 71704bb139aSTomas Winkler 7186aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 7192bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 7206aae48ffSTomas Winkler return false; 7216aae48ffSTomas Winkler } 7226aae48ffSTomas Winkler 7236aae48ffSTomas Winkler dev->hbuf_is_ready = false; 7246aae48ffSTomas Winkler 7256aae48ffSTomas Winkler return true; 7266aae48ffSTomas Winkler } 7279ca9050bSTomas Winkler 7289ca9050bSTomas Winkler /** 7293c666182STomas Winkler * mei_cl_set_disconnected - set disconnected state and clear 7303c666182STomas Winkler * associated states and resources 7313c666182STomas Winkler * 7323c666182STomas Winkler * @cl: host client 7333c666182STomas Winkler */ 7343c666182STomas Winkler void mei_cl_set_disconnected(struct mei_cl *cl) 7353c666182STomas Winkler { 7363c666182STomas Winkler struct mei_device *dev = cl->dev; 7373c666182STomas Winkler 7383c666182STomas Winkler if (cl->state == MEI_FILE_DISCONNECTED || 7393c666182STomas Winkler cl->state == MEI_FILE_INITIALIZING) 7403c666182STomas Winkler return; 7413c666182STomas Winkler 7423c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 7433c666182STomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 7443c666182STomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 7453c666182STomas Winkler cl->mei_flow_ctrl_creds = 0; 7463c666182STomas Winkler cl->timer_count = 0; 747d49ed64aSAlexander Usyskin 748a03d77f6SAlexander Usyskin if (!cl->me_cl) 749a03d77f6SAlexander Usyskin return; 750a03d77f6SAlexander Usyskin 751a03d77f6SAlexander Usyskin if (!WARN_ON(cl->me_cl->connect_count == 0)) 752a03d77f6SAlexander Usyskin cl->me_cl->connect_count--; 753a03d77f6SAlexander Usyskin 754c241e9b1SAlexander Usyskin if (cl->me_cl->connect_count == 0) 755c241e9b1SAlexander Usyskin cl->me_cl->mei_flow_ctrl_creds = 0; 756c241e9b1SAlexander Usyskin 757d49ed64aSAlexander Usyskin mei_me_cl_put(cl->me_cl); 758d49ed64aSAlexander Usyskin cl->me_cl = NULL; 7593c666182STomas Winkler } 7603c666182STomas Winkler 761a03d77f6SAlexander Usyskin static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) 762a03d77f6SAlexander Usyskin { 7631df629efSAlexander Usyskin if (!mei_me_cl_get(me_cl)) 764a03d77f6SAlexander Usyskin return -ENOENT; 765a03d77f6SAlexander Usyskin 7661df629efSAlexander Usyskin /* only one connection is allowed for fixed address clients */ 7671df629efSAlexander Usyskin if (me_cl->props.fixed_address) { 7681df629efSAlexander Usyskin if (me_cl->connect_count) { 7691df629efSAlexander Usyskin mei_me_cl_put(me_cl); 7701df629efSAlexander Usyskin return -EBUSY; 7711df629efSAlexander Usyskin } 7721df629efSAlexander Usyskin } 7731df629efSAlexander Usyskin 7741df629efSAlexander Usyskin cl->me_cl = me_cl; 775a03d77f6SAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 776a03d77f6SAlexander Usyskin cl->me_cl->connect_count++; 777a03d77f6SAlexander Usyskin 778a03d77f6SAlexander Usyskin return 0; 779a03d77f6SAlexander Usyskin } 780a03d77f6SAlexander Usyskin 7813c666182STomas Winkler /* 7823c666182STomas Winkler * mei_cl_send_disconnect - send disconnect request 7833c666182STomas Winkler * 7843c666182STomas Winkler * @cl: host client 7853c666182STomas Winkler * @cb: callback block 7863c666182STomas Winkler * 7873c666182STomas Winkler * Return: 0, OK; otherwise, error. 7883c666182STomas Winkler */ 7893c666182STomas Winkler static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 7903c666182STomas Winkler { 7913c666182STomas Winkler struct mei_device *dev; 7923c666182STomas Winkler int ret; 7933c666182STomas Winkler 7943c666182STomas Winkler dev = cl->dev; 7953c666182STomas Winkler 7963c666182STomas Winkler ret = mei_hbm_cl_disconnect_req(dev, cl); 7973c666182STomas Winkler cl->status = ret; 7983c666182STomas Winkler if (ret) { 7993c666182STomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 8003c666182STomas Winkler return ret; 8013c666182STomas Winkler } 8023c666182STomas Winkler 8033c666182STomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 8043c666182STomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 8053c666182STomas Winkler 8063c666182STomas Winkler return 0; 8073c666182STomas Winkler } 8083c666182STomas Winkler 8093c666182STomas Winkler /** 8103c666182STomas Winkler * mei_cl_irq_disconnect - processes close related operation from 8113c666182STomas Winkler * interrupt thread context - send disconnect request 8123c666182STomas Winkler * 8133c666182STomas Winkler * @cl: client 8143c666182STomas Winkler * @cb: callback block. 8153c666182STomas Winkler * @cmpl_list: complete list. 8163c666182STomas Winkler * 8173c666182STomas Winkler * Return: 0, OK; otherwise, error. 8183c666182STomas Winkler */ 8193c666182STomas Winkler int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 8203c666182STomas Winkler struct mei_cl_cb *cmpl_list) 8213c666182STomas Winkler { 8223c666182STomas Winkler struct mei_device *dev = cl->dev; 8233c666182STomas Winkler u32 msg_slots; 8243c666182STomas Winkler int slots; 8253c666182STomas Winkler int ret; 8263c666182STomas Winkler 8273c666182STomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 8283c666182STomas Winkler slots = mei_hbuf_empty_slots(dev); 8293c666182STomas Winkler 8303c666182STomas Winkler if (slots < msg_slots) 8313c666182STomas Winkler return -EMSGSIZE; 8323c666182STomas Winkler 8333c666182STomas Winkler ret = mei_cl_send_disconnect(cl, cb); 8343c666182STomas Winkler if (ret) 8353c666182STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 8363c666182STomas Winkler 8373c666182STomas Winkler return ret; 8383c666182STomas Winkler } 8393c666182STomas Winkler 8403c666182STomas Winkler /** 84118901357SAlexander Usyskin * __mei_cl_disconnect - disconnect host client from the me one 84218901357SAlexander Usyskin * internal function runtime pm has to be already acquired 8439ca9050bSTomas Winkler * 84490e0b5f1STomas Winkler * @cl: host client 8459ca9050bSTomas Winkler * 846a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 8479ca9050bSTomas Winkler */ 84818901357SAlexander Usyskin static int __mei_cl_disconnect(struct mei_cl *cl) 8499ca9050bSTomas Winkler { 85090e0b5f1STomas Winkler struct mei_device *dev; 8519ca9050bSTomas Winkler struct mei_cl_cb *cb; 852fe2f17ebSAlexander Usyskin int rets; 8539ca9050bSTomas Winkler 85490e0b5f1STomas Winkler dev = cl->dev; 85590e0b5f1STomas Winkler 8563c666182STomas Winkler cl->state = MEI_FILE_DISCONNECTING; 8573c666182STomas Winkler 858bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); 859bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 860bca67d68STomas Winkler if (rets) 8613c666182STomas Winkler goto out; 8625a8373fbSTomas Winkler 863c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 8649ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 8659ca9050bSTomas Winkler 8663c666182STomas Winkler if (mei_hbuf_acquire(dev)) { 8673c666182STomas Winkler rets = mei_cl_send_disconnect(cl, cb); 8683c666182STomas Winkler if (rets) { 8693c666182STomas Winkler cl_err(dev, cl, "failed to disconnect.\n"); 8703c666182STomas Winkler goto out; 8719ca9050bSTomas Winkler } 8723c666182STomas Winkler } 8733c666182STomas Winkler 8749ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 8753c666182STomas Winkler wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY, 8769ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 8779ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 878fe2f17ebSAlexander Usyskin 8793c666182STomas Winkler rets = cl->status; 8803c666182STomas Winkler if (cl->state != MEI_FILE_DISCONNECT_REPLY) { 881fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 882fe2f17ebSAlexander Usyskin rets = -ETIME; 8839ca9050bSTomas Winkler } 8849ca9050bSTomas Winkler 8853c666182STomas Winkler out: 8863c666182STomas Winkler /* we disconnect also on error */ 8873c666182STomas Winkler mei_cl_set_disconnected(cl); 8883c666182STomas Winkler if (!rets) 8893c666182STomas Winkler cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 8903c666182STomas Winkler 89118901357SAlexander Usyskin mei_io_cb_free(cb); 89218901357SAlexander Usyskin return rets; 89318901357SAlexander Usyskin } 89418901357SAlexander Usyskin 89518901357SAlexander Usyskin /** 89618901357SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 89718901357SAlexander Usyskin * 89818901357SAlexander Usyskin * @cl: host client 89918901357SAlexander Usyskin * 90018901357SAlexander Usyskin * Locking: called under "dev->device_lock" lock 90118901357SAlexander Usyskin * 90218901357SAlexander Usyskin * Return: 0 on success, <0 on failure. 90318901357SAlexander Usyskin */ 90418901357SAlexander Usyskin int mei_cl_disconnect(struct mei_cl *cl) 90518901357SAlexander Usyskin { 90618901357SAlexander Usyskin struct mei_device *dev; 90718901357SAlexander Usyskin int rets; 90818901357SAlexander Usyskin 90918901357SAlexander Usyskin if (WARN_ON(!cl || !cl->dev)) 91018901357SAlexander Usyskin return -ENODEV; 91118901357SAlexander Usyskin 91218901357SAlexander Usyskin dev = cl->dev; 91318901357SAlexander Usyskin 91418901357SAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 91518901357SAlexander Usyskin 91618901357SAlexander Usyskin if (!mei_cl_is_connected(cl)) 91718901357SAlexander Usyskin return 0; 91818901357SAlexander Usyskin 91918901357SAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 92018901357SAlexander Usyskin mei_cl_set_disconnected(cl); 92118901357SAlexander Usyskin return 0; 92218901357SAlexander Usyskin } 92318901357SAlexander Usyskin 92418901357SAlexander Usyskin rets = pm_runtime_get(dev->dev); 92518901357SAlexander Usyskin if (rets < 0 && rets != -EINPROGRESS) { 92618901357SAlexander Usyskin pm_runtime_put_noidle(dev->dev); 92718901357SAlexander Usyskin cl_err(dev, cl, "rpm: get failed %d\n", rets); 92818901357SAlexander Usyskin return rets; 92918901357SAlexander Usyskin } 93018901357SAlexander Usyskin 93118901357SAlexander Usyskin rets = __mei_cl_disconnect(cl); 93218901357SAlexander Usyskin 93304bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 9342bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 9352bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 93604bb139aSTomas Winkler 9379ca9050bSTomas Winkler return rets; 9389ca9050bSTomas Winkler } 9399ca9050bSTomas Winkler 9409ca9050bSTomas Winkler 9419ca9050bSTomas Winkler /** 94290e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 94390e0b5f1STomas Winkler * client with the same me client id is connecting 9449ca9050bSTomas Winkler * 9459ca9050bSTomas Winkler * @cl: private data of the file object 9469ca9050bSTomas Winkler * 947a8605ea2SAlexander Usyskin * Return: true if other client is connected, false - otherwise. 9489ca9050bSTomas Winkler */ 9490c53357cSTomas Winkler static bool mei_cl_is_other_connecting(struct mei_cl *cl) 9509ca9050bSTomas Winkler { 95190e0b5f1STomas Winkler struct mei_device *dev; 9520c53357cSTomas Winkler struct mei_cl_cb *cb; 95390e0b5f1STomas Winkler 95490e0b5f1STomas Winkler dev = cl->dev; 95590e0b5f1STomas Winkler 9560c53357cSTomas Winkler list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { 9570c53357cSTomas Winkler if (cb->fop_type == MEI_FOP_CONNECT && 958d49ed64aSAlexander Usyskin mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) 95990e0b5f1STomas Winkler return true; 9609ca9050bSTomas Winkler } 96190e0b5f1STomas Winkler 96290e0b5f1STomas Winkler return false; 9639ca9050bSTomas Winkler } 9649ca9050bSTomas Winkler 9659ca9050bSTomas Winkler /** 9660c53357cSTomas Winkler * mei_cl_send_connect - send connect request 9670c53357cSTomas Winkler * 9680c53357cSTomas Winkler * @cl: host client 9690c53357cSTomas Winkler * @cb: callback block 9700c53357cSTomas Winkler * 9710c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 9720c53357cSTomas Winkler */ 9730c53357cSTomas Winkler static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) 9740c53357cSTomas Winkler { 9750c53357cSTomas Winkler struct mei_device *dev; 9760c53357cSTomas Winkler int ret; 9770c53357cSTomas Winkler 9780c53357cSTomas Winkler dev = cl->dev; 9790c53357cSTomas Winkler 9800c53357cSTomas Winkler ret = mei_hbm_cl_connect_req(dev, cl); 9810c53357cSTomas Winkler cl->status = ret; 9820c53357cSTomas Winkler if (ret) { 9830c53357cSTomas Winkler cl->state = MEI_FILE_DISCONNECT_REPLY; 9840c53357cSTomas Winkler return ret; 9850c53357cSTomas Winkler } 9860c53357cSTomas Winkler 9870c53357cSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 9880c53357cSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 9890c53357cSTomas Winkler return 0; 9900c53357cSTomas Winkler } 9910c53357cSTomas Winkler 9920c53357cSTomas Winkler /** 9930c53357cSTomas Winkler * mei_cl_irq_connect - send connect request in irq_thread context 9940c53357cSTomas Winkler * 9950c53357cSTomas Winkler * @cl: host client 9960c53357cSTomas Winkler * @cb: callback block 9970c53357cSTomas Winkler * @cmpl_list: complete list 9980c53357cSTomas Winkler * 9990c53357cSTomas Winkler * Return: 0, OK; otherwise, error. 10000c53357cSTomas Winkler */ 10010c53357cSTomas Winkler int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, 10020c53357cSTomas Winkler struct mei_cl_cb *cmpl_list) 10030c53357cSTomas Winkler { 10040c53357cSTomas Winkler struct mei_device *dev = cl->dev; 10050c53357cSTomas Winkler u32 msg_slots; 10060c53357cSTomas Winkler int slots; 10070c53357cSTomas Winkler int rets; 10080c53357cSTomas Winkler 10090c53357cSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 10100c53357cSTomas Winkler slots = mei_hbuf_empty_slots(dev); 10110c53357cSTomas Winkler 10120c53357cSTomas Winkler if (mei_cl_is_other_connecting(cl)) 10130c53357cSTomas Winkler return 0; 10140c53357cSTomas Winkler 10150c53357cSTomas Winkler if (slots < msg_slots) 10160c53357cSTomas Winkler return -EMSGSIZE; 10170c53357cSTomas Winkler 10180c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10190c53357cSTomas Winkler if (rets) 10200c53357cSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 10210c53357cSTomas Winkler 10220c53357cSTomas Winkler return rets; 10230c53357cSTomas Winkler } 10240c53357cSTomas Winkler 10250c53357cSTomas Winkler /** 102683ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 10279f81abdaSTomas Winkler * 10289f81abdaSTomas Winkler * @cl: host client 1029d49ed64aSAlexander Usyskin * @me_cl: me client 1030a8605ea2SAlexander Usyskin * @file: pointer to file structure 10319f81abdaSTomas Winkler * 10329f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 10339f81abdaSTomas Winkler * 1034a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 10359f81abdaSTomas Winkler */ 1036d49ed64aSAlexander Usyskin int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, 1037d49ed64aSAlexander Usyskin struct file *file) 10389f81abdaSTomas Winkler { 10399f81abdaSTomas Winkler struct mei_device *dev; 10409f81abdaSTomas Winkler struct mei_cl_cb *cb; 10419f81abdaSTomas Winkler int rets; 10429f81abdaSTomas Winkler 10431df629efSAlexander Usyskin if (WARN_ON(!cl || !cl->dev || !me_cl)) 10449f81abdaSTomas Winkler return -ENODEV; 10459f81abdaSTomas Winkler 10469f81abdaSTomas Winkler dev = cl->dev; 10479f81abdaSTomas Winkler 10481df629efSAlexander Usyskin rets = mei_cl_set_connecting(cl, me_cl); 10491df629efSAlexander Usyskin if (rets) 10501df629efSAlexander Usyskin return rets; 10511df629efSAlexander Usyskin 10521df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 10531df629efSAlexander Usyskin cl->state = MEI_FILE_CONNECTED; 10541df629efSAlexander Usyskin return 0; 10551df629efSAlexander Usyskin } 10561df629efSAlexander Usyskin 10572bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 105804bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 10592bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 106004bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 10611df629efSAlexander Usyskin goto nortpm; 106204bb139aSTomas Winkler } 106304bb139aSTomas Winkler 1064bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); 1065bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 1066bca67d68STomas Winkler if (rets) 10679f81abdaSTomas Winkler goto out; 10689f81abdaSTomas Winkler 10690c53357cSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 10700c53357cSTomas Winkler 10716aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 10726aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 10730c53357cSTomas Winkler rets = mei_cl_send_connect(cl, cb); 10740c53357cSTomas Winkler if (rets) 10759f81abdaSTomas Winkler goto out; 10769f81abdaSTomas Winkler } 10779f81abdaSTomas Winkler 10789f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 107912f45ed4STomas Winkler wait_event_timeout(cl->wait, 10809f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 108118901357SAlexander Usyskin cl->state == MEI_FILE_DISCONNECT_REQUIRED || 10823c666182STomas Winkler cl->state == MEI_FILE_DISCONNECT_REPLY), 1083206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 10849f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 10859f81abdaSTomas Winkler 1086f3de9b63STomas Winkler if (!mei_cl_is_connected(cl)) { 108718901357SAlexander Usyskin if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { 108818901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_rd_list, cl); 108918901357SAlexander Usyskin mei_io_list_flush(&dev->ctrl_wr_list, cl); 109018901357SAlexander Usyskin /* ignore disconnect return valuue; 109118901357SAlexander Usyskin * in case of failure reset will be invoked 109218901357SAlexander Usyskin */ 109318901357SAlexander Usyskin __mei_cl_disconnect(cl); 109418901357SAlexander Usyskin rets = -EFAULT; 109518901357SAlexander Usyskin goto out; 109618901357SAlexander Usyskin } 109718901357SAlexander Usyskin 10980c53357cSTomas Winkler /* timeout or something went really wrong */ 1099285e2996SAlexander Usyskin if (!cl->status) 1100285e2996SAlexander Usyskin cl->status = -EFAULT; 11019f81abdaSTomas Winkler } 11029f81abdaSTomas Winkler 11039f81abdaSTomas Winkler rets = cl->status; 11049f81abdaSTomas Winkler out: 110504bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 11062bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 11072bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 110804bb139aSTomas Winkler 11099f81abdaSTomas Winkler mei_io_cb_free(cb); 11100c53357cSTomas Winkler 11111df629efSAlexander Usyskin nortpm: 11120c53357cSTomas Winkler if (!mei_cl_is_connected(cl)) 11130c53357cSTomas Winkler mei_cl_set_disconnected(cl); 11140c53357cSTomas Winkler 11159f81abdaSTomas Winkler return rets; 11169f81abdaSTomas Winkler } 11179f81abdaSTomas Winkler 11189f81abdaSTomas Winkler /** 111903b8d341STomas Winkler * mei_cl_alloc_linked - allocate and link host client 112003b8d341STomas Winkler * 112103b8d341STomas Winkler * @dev: the device structure 112203b8d341STomas Winkler * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one 112303b8d341STomas Winkler * 112403b8d341STomas Winkler * Return: cl on success ERR_PTR on failure 112503b8d341STomas Winkler */ 112603b8d341STomas Winkler struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) 112703b8d341STomas Winkler { 112803b8d341STomas Winkler struct mei_cl *cl; 112903b8d341STomas Winkler int ret; 113003b8d341STomas Winkler 113103b8d341STomas Winkler cl = mei_cl_allocate(dev); 113203b8d341STomas Winkler if (!cl) { 113303b8d341STomas Winkler ret = -ENOMEM; 113403b8d341STomas Winkler goto err; 113503b8d341STomas Winkler } 113603b8d341STomas Winkler 113703b8d341STomas Winkler ret = mei_cl_link(cl, id); 113803b8d341STomas Winkler if (ret) 113903b8d341STomas Winkler goto err; 114003b8d341STomas Winkler 114103b8d341STomas Winkler return cl; 114203b8d341STomas Winkler err: 114303b8d341STomas Winkler kfree(cl); 114403b8d341STomas Winkler return ERR_PTR(ret); 114503b8d341STomas Winkler } 114603b8d341STomas Winkler 114703b8d341STomas Winkler 114803b8d341STomas Winkler 114903b8d341STomas Winkler /** 115090e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 11519ca9050bSTomas Winkler * 11529ca9050bSTomas Winkler * @cl: private data of the file object 11539ca9050bSTomas Winkler * 1154a8605ea2SAlexander Usyskin * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 11559ca9050bSTomas Winkler */ 115690e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 11579ca9050bSTomas Winkler { 11581df629efSAlexander Usyskin int rets; 11591df629efSAlexander Usyskin 1160d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 116190e0b5f1STomas Winkler return -EINVAL; 116290e0b5f1STomas Winkler 11639ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 11649ca9050bSTomas Winkler return 1; 11659ca9050bSTomas Winkler 11661df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 11671df629efSAlexander Usyskin rets = mei_cl_read_start(cl, mei_cl_mtu(cl), NULL); 11681df629efSAlexander Usyskin if (rets && rets != -EBUSY) 11691df629efSAlexander Usyskin return rets; 11701df629efSAlexander Usyskin return 1; 11711df629efSAlexander Usyskin } 11721df629efSAlexander Usyskin 1173d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 1174d49ed64aSAlexander Usyskin if (cl->me_cl->mei_flow_ctrl_creds > 0) 1175d49ed64aSAlexander Usyskin return 1; 117612d00665SAlexander Usyskin } 1177d49ed64aSAlexander Usyskin return 0; 11789ca9050bSTomas Winkler } 11799ca9050bSTomas Winkler 11809ca9050bSTomas Winkler /** 118190e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 11829ca9050bSTomas Winkler * 11839ca9050bSTomas Winkler * @cl: private data of the file object 1184393b148fSMasanari Iida * 1185a8605ea2SAlexander Usyskin * Return: 11869ca9050bSTomas Winkler * 0 on success 11879ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 11889ca9050bSTomas Winkler */ 118990e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 11909ca9050bSTomas Winkler { 1191d49ed64aSAlexander Usyskin if (WARN_ON(!cl || !cl->me_cl)) 119290e0b5f1STomas Winkler return -EINVAL; 119390e0b5f1STomas Winkler 11941df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) 11951df629efSAlexander Usyskin return 0; 11961df629efSAlexander Usyskin 1197d49ed64aSAlexander Usyskin if (mei_cl_is_single_recv_buf(cl)) { 1198d49ed64aSAlexander Usyskin if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0)) 1199d49ed64aSAlexander Usyskin return -EINVAL; 1200d49ed64aSAlexander Usyskin cl->me_cl->mei_flow_ctrl_creds--; 12019ca9050bSTomas Winkler } else { 1202d49ed64aSAlexander Usyskin if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 1203d49ed64aSAlexander Usyskin return -EINVAL; 12049ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 12059ca9050bSTomas Winkler } 1206d49ed64aSAlexander Usyskin return 0; 12079ca9050bSTomas Winkler } 12089ca9050bSTomas Winkler 12099ca9050bSTomas Winkler /** 121051678ccbSTomas Winkler * mei_cl_notify_fop2req - convert fop to proper request 121151678ccbSTomas Winkler * 121251678ccbSTomas Winkler * @fop: client notification start response command 121351678ccbSTomas Winkler * 121451678ccbSTomas Winkler * Return: MEI_HBM_NOTIFICATION_START/STOP 121551678ccbSTomas Winkler */ 121651678ccbSTomas Winkler u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) 121751678ccbSTomas Winkler { 121851678ccbSTomas Winkler if (fop == MEI_FOP_NOTIFY_START) 121951678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_START; 122051678ccbSTomas Winkler else 122151678ccbSTomas Winkler return MEI_HBM_NOTIFICATION_STOP; 122251678ccbSTomas Winkler } 122351678ccbSTomas Winkler 122451678ccbSTomas Winkler /** 122551678ccbSTomas Winkler * mei_cl_notify_req2fop - convert notification request top file operation type 122651678ccbSTomas Winkler * 122751678ccbSTomas Winkler * @req: hbm notification request type 122851678ccbSTomas Winkler * 122951678ccbSTomas Winkler * Return: MEI_FOP_NOTIFY_START/STOP 123051678ccbSTomas Winkler */ 123151678ccbSTomas Winkler enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) 123251678ccbSTomas Winkler { 123351678ccbSTomas Winkler if (req == MEI_HBM_NOTIFICATION_START) 123451678ccbSTomas Winkler return MEI_FOP_NOTIFY_START; 123551678ccbSTomas Winkler else 123651678ccbSTomas Winkler return MEI_FOP_NOTIFY_STOP; 123751678ccbSTomas Winkler } 123851678ccbSTomas Winkler 123951678ccbSTomas Winkler /** 124051678ccbSTomas Winkler * mei_cl_irq_notify - send notification request in irq_thread context 124151678ccbSTomas Winkler * 124251678ccbSTomas Winkler * @cl: client 124351678ccbSTomas Winkler * @cb: callback block. 124451678ccbSTomas Winkler * @cmpl_list: complete list. 124551678ccbSTomas Winkler * 124651678ccbSTomas Winkler * Return: 0 on such and error otherwise. 124751678ccbSTomas Winkler */ 124851678ccbSTomas Winkler int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, 124951678ccbSTomas Winkler struct mei_cl_cb *cmpl_list) 125051678ccbSTomas Winkler { 125151678ccbSTomas Winkler struct mei_device *dev = cl->dev; 125251678ccbSTomas Winkler u32 msg_slots; 125351678ccbSTomas Winkler int slots; 125451678ccbSTomas Winkler int ret; 125551678ccbSTomas Winkler bool request; 125651678ccbSTomas Winkler 125751678ccbSTomas Winkler msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 125851678ccbSTomas Winkler slots = mei_hbuf_empty_slots(dev); 125951678ccbSTomas Winkler 126051678ccbSTomas Winkler if (slots < msg_slots) 126151678ccbSTomas Winkler return -EMSGSIZE; 126251678ccbSTomas Winkler 126351678ccbSTomas Winkler request = mei_cl_notify_fop2req(cb->fop_type); 126451678ccbSTomas Winkler ret = mei_hbm_cl_notify_req(dev, cl, request); 126551678ccbSTomas Winkler if (ret) { 126651678ccbSTomas Winkler cl->status = ret; 126751678ccbSTomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 126851678ccbSTomas Winkler return ret; 126951678ccbSTomas Winkler } 127051678ccbSTomas Winkler 127151678ccbSTomas Winkler list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 127251678ccbSTomas Winkler return 0; 127351678ccbSTomas Winkler } 127451678ccbSTomas Winkler 127551678ccbSTomas Winkler /** 127651678ccbSTomas Winkler * mei_cl_notify_request - send notification stop/start request 127751678ccbSTomas Winkler * 127851678ccbSTomas Winkler * @cl: host client 127951678ccbSTomas Winkler * @file: associate request with file 128051678ccbSTomas Winkler * @request: 1 for start or 0 for stop 128151678ccbSTomas Winkler * 128251678ccbSTomas Winkler * Locking: called under "dev->device_lock" lock 128351678ccbSTomas Winkler * 128451678ccbSTomas Winkler * Return: 0 on such and error otherwise. 128551678ccbSTomas Winkler */ 128651678ccbSTomas Winkler int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request) 128751678ccbSTomas Winkler { 128851678ccbSTomas Winkler struct mei_device *dev; 128951678ccbSTomas Winkler struct mei_cl_cb *cb; 129051678ccbSTomas Winkler enum mei_cb_file_ops fop_type; 129151678ccbSTomas Winkler int rets; 129251678ccbSTomas Winkler 129351678ccbSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 129451678ccbSTomas Winkler return -ENODEV; 129551678ccbSTomas Winkler 129651678ccbSTomas Winkler dev = cl->dev; 129751678ccbSTomas Winkler 129851678ccbSTomas Winkler if (!dev->hbm_f_ev_supported) { 129951678ccbSTomas Winkler cl_dbg(dev, cl, "notifications not supported\n"); 130051678ccbSTomas Winkler return -EOPNOTSUPP; 130151678ccbSTomas Winkler } 130251678ccbSTomas Winkler 130351678ccbSTomas Winkler rets = pm_runtime_get(dev->dev); 130451678ccbSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 130551678ccbSTomas Winkler pm_runtime_put_noidle(dev->dev); 130651678ccbSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 130751678ccbSTomas Winkler return rets; 130851678ccbSTomas Winkler } 130951678ccbSTomas Winkler 131051678ccbSTomas Winkler fop_type = mei_cl_notify_req2fop(request); 131151678ccbSTomas Winkler cb = mei_io_cb_init(cl, fop_type, file); 131251678ccbSTomas Winkler if (!cb) { 131351678ccbSTomas Winkler rets = -ENOMEM; 131451678ccbSTomas Winkler goto out; 131551678ccbSTomas Winkler } 131651678ccbSTomas Winkler 131751678ccbSTomas Winkler if (mei_hbuf_acquire(dev)) { 131851678ccbSTomas Winkler if (mei_hbm_cl_notify_req(dev, cl, request)) { 131951678ccbSTomas Winkler rets = -ENODEV; 132051678ccbSTomas Winkler goto out; 132151678ccbSTomas Winkler } 132251678ccbSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 132351678ccbSTomas Winkler } else { 132451678ccbSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 132551678ccbSTomas Winkler } 132651678ccbSTomas Winkler 132751678ccbSTomas Winkler mutex_unlock(&dev->device_lock); 132851678ccbSTomas Winkler wait_event_timeout(cl->wait, cl->notify_en == request, 132951678ccbSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 133051678ccbSTomas Winkler mutex_lock(&dev->device_lock); 133151678ccbSTomas Winkler 133251678ccbSTomas Winkler if (cl->notify_en != request) { 133351678ccbSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 133451678ccbSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 133551678ccbSTomas Winkler if (!cl->status) 133651678ccbSTomas Winkler cl->status = -EFAULT; 133751678ccbSTomas Winkler } 133851678ccbSTomas Winkler 133951678ccbSTomas Winkler rets = cl->status; 134051678ccbSTomas Winkler 134151678ccbSTomas Winkler out: 134251678ccbSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 134351678ccbSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 134451678ccbSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 134551678ccbSTomas Winkler 134651678ccbSTomas Winkler mei_io_cb_free(cb); 134751678ccbSTomas Winkler return rets; 134851678ccbSTomas Winkler } 134951678ccbSTomas Winkler 135051678ccbSTomas Winkler /** 1351237092bfSTomas Winkler * mei_cl_notify - raise notification 1352237092bfSTomas Winkler * 1353237092bfSTomas Winkler * @cl: host client 1354237092bfSTomas Winkler * 1355237092bfSTomas Winkler * Locking: called under "dev->device_lock" lock 1356237092bfSTomas Winkler */ 1357237092bfSTomas Winkler void mei_cl_notify(struct mei_cl *cl) 1358237092bfSTomas Winkler { 1359237092bfSTomas Winkler struct mei_device *dev; 1360237092bfSTomas Winkler 1361237092bfSTomas Winkler if (!cl || !cl->dev) 1362237092bfSTomas Winkler return; 1363237092bfSTomas Winkler 1364237092bfSTomas Winkler dev = cl->dev; 1365237092bfSTomas Winkler 1366237092bfSTomas Winkler if (!cl->notify_en) 1367237092bfSTomas Winkler return; 1368237092bfSTomas Winkler 1369237092bfSTomas Winkler cl_dbg(dev, cl, "notify event"); 1370237092bfSTomas Winkler cl->notify_ev = true; 1371237092bfSTomas Winkler wake_up_interruptible_all(&cl->ev_wait); 1372237092bfSTomas Winkler 1373237092bfSTomas Winkler if (cl->ev_async) 1374237092bfSTomas Winkler kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); 1375bb2ef9c3SAlexander Usyskin 1376bb2ef9c3SAlexander Usyskin mei_cl_bus_notify_event(cl); 1377237092bfSTomas Winkler } 1378237092bfSTomas Winkler 1379237092bfSTomas Winkler /** 1380b38a362fSTomas Winkler * mei_cl_notify_get - get or wait for notification event 1381b38a362fSTomas Winkler * 1382b38a362fSTomas Winkler * @cl: host client 1383b38a362fSTomas Winkler * @block: this request is blocking 1384b38a362fSTomas Winkler * @notify_ev: true if notification event was received 1385b38a362fSTomas Winkler * 1386b38a362fSTomas Winkler * Locking: called under "dev->device_lock" lock 1387b38a362fSTomas Winkler * 1388b38a362fSTomas Winkler * Return: 0 on such and error otherwise. 1389b38a362fSTomas Winkler */ 1390b38a362fSTomas Winkler int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) 1391b38a362fSTomas Winkler { 1392b38a362fSTomas Winkler struct mei_device *dev; 1393b38a362fSTomas Winkler int rets; 1394b38a362fSTomas Winkler 1395b38a362fSTomas Winkler *notify_ev = false; 1396b38a362fSTomas Winkler 1397b38a362fSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 1398b38a362fSTomas Winkler return -ENODEV; 1399b38a362fSTomas Winkler 1400b38a362fSTomas Winkler dev = cl->dev; 1401b38a362fSTomas Winkler 1402b38a362fSTomas Winkler if (!mei_cl_is_connected(cl)) 1403b38a362fSTomas Winkler return -ENODEV; 1404b38a362fSTomas Winkler 1405b38a362fSTomas Winkler if (cl->notify_ev) 1406b38a362fSTomas Winkler goto out; 1407b38a362fSTomas Winkler 1408b38a362fSTomas Winkler if (!block) 1409b38a362fSTomas Winkler return -EAGAIN; 1410b38a362fSTomas Winkler 1411b38a362fSTomas Winkler mutex_unlock(&dev->device_lock); 1412b38a362fSTomas Winkler rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); 1413b38a362fSTomas Winkler mutex_lock(&dev->device_lock); 1414b38a362fSTomas Winkler 1415b38a362fSTomas Winkler if (rets < 0) 1416b38a362fSTomas Winkler return rets; 1417b38a362fSTomas Winkler 1418b38a362fSTomas Winkler out: 1419b38a362fSTomas Winkler *notify_ev = cl->notify_ev; 1420b38a362fSTomas Winkler cl->notify_ev = false; 1421b38a362fSTomas Winkler return 0; 1422b38a362fSTomas Winkler } 1423b38a362fSTomas Winkler 1424b38a362fSTomas Winkler /** 142513cf9885SAlexander Usyskin * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control 142613cf9885SAlexander Usyskin * for given host client 142713cf9885SAlexander Usyskin * 142813cf9885SAlexander Usyskin * @cl: host client 142913cf9885SAlexander Usyskin * 143013cf9885SAlexander Usyskin * Return: true, if found at least one cb. 143113cf9885SAlexander Usyskin */ 143213cf9885SAlexander Usyskin static bool mei_cl_is_read_fc_cb(struct mei_cl *cl) 143313cf9885SAlexander Usyskin { 143413cf9885SAlexander Usyskin struct mei_device *dev = cl->dev; 143513cf9885SAlexander Usyskin struct mei_cl_cb *cb; 143613cf9885SAlexander Usyskin 143713cf9885SAlexander Usyskin list_for_each_entry(cb, &dev->ctrl_wr_list.list, list) 143813cf9885SAlexander Usyskin if (cb->fop_type == MEI_FOP_READ && cb->cl == cl) 143913cf9885SAlexander Usyskin return true; 144013cf9885SAlexander Usyskin return false; 144113cf9885SAlexander Usyskin } 144213cf9885SAlexander Usyskin 144313cf9885SAlexander Usyskin /** 1444393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 14459ca9050bSTomas Winkler * 144690e0b5f1STomas Winkler * @cl: host client 1447ce23139cSAlexander Usyskin * @length: number of bytes to read 1448bca67d68STomas Winkler * @fp: pointer to file structure 14499ca9050bSTomas Winkler * 1450a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 14519ca9050bSTomas Winkler */ 1452bca67d68STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) 14539ca9050bSTomas Winkler { 145490e0b5f1STomas Winkler struct mei_device *dev; 14559ca9050bSTomas Winkler struct mei_cl_cb *cb; 14569ca9050bSTomas Winkler int rets; 14579ca9050bSTomas Winkler 145890e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 145990e0b5f1STomas Winkler return -ENODEV; 146090e0b5f1STomas Winkler 146190e0b5f1STomas Winkler dev = cl->dev; 146290e0b5f1STomas Winkler 1463b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 14649ca9050bSTomas Winkler return -ENODEV; 14659ca9050bSTomas Winkler 1466a9bed610STomas Winkler /* HW currently supports only one pending read */ 146713cf9885SAlexander Usyskin if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl)) 14689ca9050bSTomas Winkler return -EBUSY; 1469a9bed610STomas Winkler 1470d49ed64aSAlexander Usyskin if (!mei_me_cl_is_active(cl->me_cl)) { 1471d49ed64aSAlexander Usyskin cl_err(dev, cl, "no such me client\n"); 14727ca96aa2SAlexander Usyskin return -ENOTTY; 14739ca9050bSTomas Winkler } 14741df629efSAlexander Usyskin 147579563db9STomas Winkler /* always allocate at least client max message */ 1476d49ed64aSAlexander Usyskin length = max_t(size_t, length, mei_cl_mtu(cl)); 14771df629efSAlexander Usyskin cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); 14781df629efSAlexander Usyskin if (!cb) 14791df629efSAlexander Usyskin return -ENOMEM; 14801df629efSAlexander Usyskin 14811df629efSAlexander Usyskin if (mei_cl_is_fixed_address(cl)) { 14821df629efSAlexander Usyskin list_add_tail(&cb->list, &cl->rd_pending); 14831df629efSAlexander Usyskin return 0; 14841df629efSAlexander Usyskin } 14859ca9050bSTomas Winkler 14862bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 148704bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 14882bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 148904bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 14901df629efSAlexander Usyskin goto nortpm; 149104bb139aSTomas Winkler } 149204bb139aSTomas Winkler 14936aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 149486113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 149586113500SAlexander Usyskin if (rets < 0) 149604bb139aSTomas Winkler goto out; 149704bb139aSTomas Winkler 1498a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_pending); 14999ca9050bSTomas Winkler } else { 15001df629efSAlexander Usyskin rets = 0; 15019ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 15029ca9050bSTomas Winkler } 1503accb884bSChao Bi 150404bb139aSTomas Winkler out: 150504bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 15062bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 15072bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 15081df629efSAlexander Usyskin nortpm: 150904bb139aSTomas Winkler if (rets) 15109ca9050bSTomas Winkler mei_io_cb_free(cb); 151104bb139aSTomas Winkler 15129ca9050bSTomas Winkler return rets; 15139ca9050bSTomas Winkler } 15149ca9050bSTomas Winkler 1515074b4c01STomas Winkler /** 15169d098192STomas Winkler * mei_cl_irq_write - write a message to device 151721767546STomas Winkler * from the interrupt thread context 151821767546STomas Winkler * 151921767546STomas Winkler * @cl: client 152021767546STomas Winkler * @cb: callback block. 152121767546STomas Winkler * @cmpl_list: complete list. 152221767546STomas Winkler * 1523a8605ea2SAlexander Usyskin * Return: 0, OK; otherwise error. 152421767546STomas Winkler */ 15259d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 15269d098192STomas Winkler struct mei_cl_cb *cmpl_list) 152721767546STomas Winkler { 1528136698e5STomas Winkler struct mei_device *dev; 1529136698e5STomas Winkler struct mei_msg_data *buf; 153021767546STomas Winkler struct mei_msg_hdr mei_hdr; 1531136698e5STomas Winkler size_t len; 1532136698e5STomas Winkler u32 msg_slots; 15339d098192STomas Winkler int slots; 15342ebf8c94STomas Winkler int rets; 1535b8b73035SAlexander Usyskin bool first_chunk; 153621767546STomas Winkler 1537136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1538136698e5STomas Winkler return -ENODEV; 1539136698e5STomas Winkler 1540136698e5STomas Winkler dev = cl->dev; 1541136698e5STomas Winkler 15425db7514dSTomas Winkler buf = &cb->buf; 1543136698e5STomas Winkler 1544b8b73035SAlexander Usyskin first_chunk = cb->buf_idx == 0; 1545b8b73035SAlexander Usyskin 1546b8b73035SAlexander Usyskin rets = first_chunk ? mei_cl_flow_ctrl_creds(cl) : 1; 1547136698e5STomas Winkler if (rets < 0) 1548136698e5STomas Winkler return rets; 1549136698e5STomas Winkler 1550136698e5STomas Winkler if (rets == 0) { 1551136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 1552136698e5STomas Winkler return 0; 1553136698e5STomas Winkler } 1554136698e5STomas Winkler 15559d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 1556136698e5STomas Winkler len = buf->size - cb->buf_idx; 1557136698e5STomas Winkler msg_slots = mei_data2slots(len); 1558136698e5STomas Winkler 15591df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1560d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 156121767546STomas Winkler mei_hdr.reserved = 0; 1562479327fcSTomas Winkler mei_hdr.internal = cb->internal; 156321767546STomas Winkler 15649d098192STomas Winkler if (slots >= msg_slots) { 156521767546STomas Winkler mei_hdr.length = len; 156621767546STomas Winkler mei_hdr.msg_complete = 1; 156721767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 15689d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 15699d098192STomas Winkler msg_slots = slots; 15709d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 157121767546STomas Winkler mei_hdr.length = len; 157221767546STomas Winkler mei_hdr.msg_complete = 0; 157321767546STomas Winkler } else { 157421767546STomas Winkler /* wait for next time the host buffer is empty */ 157521767546STomas Winkler return 0; 157621767546STomas Winkler } 157721767546STomas Winkler 1578c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", 15795db7514dSTomas Winkler cb->buf.size, cb->buf_idx); 158021767546STomas Winkler 1581136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 15822ebf8c94STomas Winkler if (rets) { 15832ebf8c94STomas Winkler cl->status = rets; 158421767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 15852ebf8c94STomas Winkler return rets; 158621767546STomas Winkler } 158721767546STomas Winkler 158821767546STomas Winkler cl->status = 0; 15894dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 159021767546STomas Winkler cb->buf_idx += mei_hdr.length; 15918660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 15924dfaa9f7STomas Winkler 1593b8b73035SAlexander Usyskin if (first_chunk) { 159421767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 15952ebf8c94STomas Winkler return -EIO; 159621767546STomas Winkler } 159721767546STomas Winkler 1598b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 1599b8b73035SAlexander Usyskin list_move_tail(&cb->list, &dev->write_waiting_list.list); 1600b8b73035SAlexander Usyskin 160121767546STomas Winkler return 0; 160221767546STomas Winkler } 160321767546STomas Winkler 160421767546STomas Winkler /** 16054234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 1606a8605ea2SAlexander Usyskin * assumes device_lock is locked 16074234a6deSTomas Winkler * 16084234a6deSTomas Winkler * @cl: host client 1609a8605ea2SAlexander Usyskin * @cb: write callback with filled data 1610ce23139cSAlexander Usyskin * @blocking: block until completed 16114234a6deSTomas Winkler * 1612a8605ea2SAlexander Usyskin * Return: number of bytes sent on success, <0 on failure. 16134234a6deSTomas Winkler */ 16144234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 16154234a6deSTomas Winkler { 16164234a6deSTomas Winkler struct mei_device *dev; 16174234a6deSTomas Winkler struct mei_msg_data *buf; 16184234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 161923253c31SAlexander Usyskin int size; 16204234a6deSTomas Winkler int rets; 16214234a6deSTomas Winkler 16224234a6deSTomas Winkler 16234234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 16244234a6deSTomas Winkler return -ENODEV; 16254234a6deSTomas Winkler 16264234a6deSTomas Winkler if (WARN_ON(!cb)) 16274234a6deSTomas Winkler return -EINVAL; 16284234a6deSTomas Winkler 16294234a6deSTomas Winkler dev = cl->dev; 16304234a6deSTomas Winkler 16315db7514dSTomas Winkler buf = &cb->buf; 163223253c31SAlexander Usyskin size = buf->size; 16334234a6deSTomas Winkler 163423253c31SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", size); 16354234a6deSTomas Winkler 16362bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 163704bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 16382bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 163904bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 164004bb139aSTomas Winkler return rets; 164104bb139aSTomas Winkler } 16424234a6deSTomas Winkler 16436aae48ffSTomas Winkler cb->buf_idx = 0; 16446aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 16456aae48ffSTomas Winkler 16461df629efSAlexander Usyskin mei_hdr.host_addr = mei_cl_host_addr(cl); 1647d49ed64aSAlexander Usyskin mei_hdr.me_addr = mei_cl_me_id(cl); 16486aae48ffSTomas Winkler mei_hdr.reserved = 0; 16496aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 16506aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 16514234a6deSTomas Winkler 16524234a6deSTomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 16534234a6deSTomas Winkler if (rets < 0) 16544234a6deSTomas Winkler goto err; 16554234a6deSTomas Winkler 16566aae48ffSTomas Winkler if (rets == 0) { 16576aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 165823253c31SAlexander Usyskin rets = size; 16594234a6deSTomas Winkler goto out; 16604234a6deSTomas Winkler } 16616aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 16626aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 166323253c31SAlexander Usyskin rets = size; 16646aae48ffSTomas Winkler goto out; 16656aae48ffSTomas Winkler } 16664234a6deSTomas Winkler 16674234a6deSTomas Winkler /* Check for a maximum length */ 166823253c31SAlexander Usyskin if (size > mei_hbuf_max_len(dev)) { 16694234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 16704234a6deSTomas Winkler mei_hdr.msg_complete = 0; 16714234a6deSTomas Winkler } else { 167223253c31SAlexander Usyskin mei_hdr.length = size; 16734234a6deSTomas Winkler mei_hdr.msg_complete = 1; 16744234a6deSTomas Winkler } 16754234a6deSTomas Winkler 16762ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 16772ebf8c94STomas Winkler if (rets) 16784234a6deSTomas Winkler goto err; 16794234a6deSTomas Winkler 1680b8b73035SAlexander Usyskin rets = mei_cl_flow_ctrl_reduce(cl); 1681b8b73035SAlexander Usyskin if (rets) 1682b8b73035SAlexander Usyskin goto err; 1683b8b73035SAlexander Usyskin 16844234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 16854234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 16868660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 16874234a6deSTomas Winkler 16884234a6deSTomas Winkler out: 1689b8b73035SAlexander Usyskin if (mei_hdr.msg_complete) 16904234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 1691b8b73035SAlexander Usyskin else 16924234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 16934234a6deSTomas Winkler 169423253c31SAlexander Usyskin cb = NULL; 16954234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 16964234a6deSTomas Winkler 16974234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 16987ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 16997ca96aa2SAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE); 17007ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 17017ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 17027ca96aa2SAlexander Usyskin if (rets) { 17034234a6deSTomas Winkler if (signal_pending(current)) 17044234a6deSTomas Winkler rets = -EINTR; 17057ca96aa2SAlexander Usyskin goto err; 17064234a6deSTomas Winkler } 17074234a6deSTomas Winkler } 17087ca96aa2SAlexander Usyskin 170923253c31SAlexander Usyskin rets = size; 17104234a6deSTomas Winkler err: 171104bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 17122bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 17132bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 171404bb139aSTomas Winkler 17154234a6deSTomas Winkler return rets; 17164234a6deSTomas Winkler } 17174234a6deSTomas Winkler 17184234a6deSTomas Winkler 1719db086fa9STomas Winkler /** 1720db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1721db086fa9STomas Winkler * 1722db086fa9STomas Winkler * @cl: private data of the file object. 1723db086fa9STomas Winkler * @cb: callback block. 1724db086fa9STomas Winkler */ 1725db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1726db086fa9STomas Winkler { 1727a1809d38SAlexander Usyskin struct mei_device *dev = cl->dev; 1728a1809d38SAlexander Usyskin 17293c666182STomas Winkler switch (cb->fop_type) { 17303c666182STomas Winkler case MEI_FOP_WRITE: 1731db086fa9STomas Winkler mei_io_cb_free(cb); 1732db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1733a1809d38SAlexander Usyskin if (waitqueue_active(&cl->tx_wait)) { 1734db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1735a1809d38SAlexander Usyskin } else { 1736a1809d38SAlexander Usyskin pm_runtime_mark_last_busy(dev->dev); 1737a1809d38SAlexander Usyskin pm_request_autosuspend(dev->dev); 1738a1809d38SAlexander Usyskin } 17393c666182STomas Winkler break; 1740db086fa9STomas Winkler 17413c666182STomas Winkler case MEI_FOP_READ: 1742a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_completed); 1743db086fa9STomas Winkler if (waitqueue_active(&cl->rx_wait)) 17441d9013f0STomas Winkler wake_up_interruptible_all(&cl->rx_wait); 1745db086fa9STomas Winkler else 1746db086fa9STomas Winkler mei_cl_bus_rx_event(cl); 17473c666182STomas Winkler break; 1748db086fa9STomas Winkler 17493c666182STomas Winkler case MEI_FOP_CONNECT: 17503c666182STomas Winkler case MEI_FOP_DISCONNECT: 175151678ccbSTomas Winkler case MEI_FOP_NOTIFY_STOP: 175251678ccbSTomas Winkler case MEI_FOP_NOTIFY_START: 17533c666182STomas Winkler if (waitqueue_active(&cl->wait)) 17543c666182STomas Winkler wake_up(&cl->wait); 17553c666182STomas Winkler 17563c666182STomas Winkler break; 17573c666182STomas Winkler default: 17583c666182STomas Winkler BUG_ON(0); 1759db086fa9STomas Winkler } 1760db086fa9STomas Winkler } 1761db086fa9STomas Winkler 17624234a6deSTomas Winkler 17634234a6deSTomas Winkler /** 1764074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1765074b4c01STomas Winkler * 1766a8605ea2SAlexander Usyskin * @dev: mei device 1767074b4c01STomas Winkler */ 1768074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1769074b4c01STomas Winkler { 177031f88f57STomas Winkler struct mei_cl *cl; 1771074b4c01STomas Winkler 17723c666182STomas Winkler list_for_each_entry(cl, &dev->file_list, link) 17733c666182STomas Winkler mei_cl_set_disconnected(cl); 1774074b4c01STomas Winkler } 1775074b4c01STomas Winkler 1776074b4c01STomas Winkler 1777074b4c01STomas Winkler /** 17785290801cSTomas Winkler * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted 1779074b4c01STomas Winkler * 1780a8605ea2SAlexander Usyskin * @dev: mei device 1781074b4c01STomas Winkler */ 17825290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev) 1783074b4c01STomas Winkler { 178431f88f57STomas Winkler struct mei_cl *cl; 178592db1555STomas Winkler 178631f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1787074b4c01STomas Winkler if (waitqueue_active(&cl->rx_wait)) { 1788c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 1789074b4c01STomas Winkler wake_up_interruptible(&cl->rx_wait); 1790074b4c01STomas Winkler } 17915290801cSTomas Winkler if (waitqueue_active(&cl->tx_wait)) { 1792c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 17935290801cSTomas Winkler wake_up_interruptible(&cl->tx_wait); 17945290801cSTomas Winkler } 1795b38a362fSTomas Winkler 1796b38a362fSTomas Winkler /* synchronized under device mutex */ 1797b38a362fSTomas Winkler if (waitqueue_active(&cl->ev_wait)) { 1798b38a362fSTomas Winkler cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); 1799b38a362fSTomas Winkler wake_up_interruptible(&cl->ev_wait); 1800b38a362fSTomas Winkler } 1801074b4c01STomas Winkler } 1802074b4c01STomas Winkler } 1803074b4c01STomas Winkler 1804074b4c01STomas Winkler /** 1805074b4c01STomas Winkler * mei_cl_all_write_clear - clear all pending writes 1806a8605ea2SAlexander Usyskin * 1807a8605ea2SAlexander Usyskin * @dev: mei device 1808074b4c01STomas Winkler */ 1809074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev) 1810074b4c01STomas Winkler { 1811cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_list, NULL); 1812cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_waiting_list, NULL); 1813074b4c01STomas Winkler } 1814074b4c01STomas Winkler 1815074b4c01STomas Winkler 1816