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 /** 86b7d88514STomas Winkler * __mei_me_cl_del - delete me client form 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 99b7d88514STomas Winkler list_del(&me_cl->list); 100b7d88514STomas Winkler mei_me_cl_put(me_cl); 101b7d88514STomas Winkler } 102b7d88514STomas Winkler 103b7d88514STomas Winkler /** 104b7d88514STomas Winkler * mei_me_cl_add - add me client to the list 105b7d88514STomas Winkler * 106b7d88514STomas Winkler * @dev: mei device 107b7d88514STomas Winkler * @me_cl: me client 108b7d88514STomas Winkler */ 109b7d88514STomas Winkler void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl) 110b7d88514STomas Winkler { 111b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 112b7d88514STomas Winkler list_add(&me_cl->list, &dev->me_clients); 113b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 114b7d88514STomas Winkler } 115b7d88514STomas Winkler 116b7d88514STomas Winkler /** 117b7d88514STomas Winkler * __mei_me_cl_by_uuid - locate me client by uuid 118b7d88514STomas Winkler * increases ref count 119b7d88514STomas Winkler * 120b7d88514STomas Winkler * @dev: mei device 121b7d88514STomas Winkler * @uuid: me client uuid 122b7d88514STomas Winkler * 123b7d88514STomas Winkler * Return: me client or NULL if not found 124b7d88514STomas Winkler * 125b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 126b7d88514STomas Winkler */ 127b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev, 128b7d88514STomas Winkler const uuid_le *uuid) 129b7d88514STomas Winkler { 130b7d88514STomas Winkler struct mei_me_client *me_cl; 131b7d88514STomas Winkler const uuid_le *pn; 132b7d88514STomas Winkler 133b7d88514STomas Winkler WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 134b7d88514STomas Winkler 135b7d88514STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) { 136b7d88514STomas Winkler pn = &me_cl->props.protocol_name; 137b7d88514STomas Winkler if (uuid_le_cmp(*uuid, *pn) == 0) 138b7d88514STomas Winkler return mei_me_cl_get(me_cl); 139b7d88514STomas Winkler } 140b7d88514STomas Winkler 141b7d88514STomas Winkler return NULL; 142b7d88514STomas Winkler } 143b7d88514STomas Winkler 144b7d88514STomas Winkler /** 145a8605ea2SAlexander Usyskin * mei_me_cl_by_uuid - locate me client by uuid 14679563db9STomas Winkler * increases ref count 14790e0b5f1STomas Winkler * 14890e0b5f1STomas Winkler * @dev: mei device 149a8605ea2SAlexander Usyskin * @uuid: me client uuid 150a27a76d3SAlexander Usyskin * 151a8605ea2SAlexander Usyskin * Return: me client or NULL if not found 152b7d88514STomas Winkler * 153b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 15490e0b5f1STomas Winkler */ 155b7d88514STomas Winkler struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, 156d320832fSTomas Winkler const uuid_le *uuid) 15790e0b5f1STomas Winkler { 1585ca2d388STomas Winkler struct mei_me_client *me_cl; 15990e0b5f1STomas Winkler 160b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 161b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid(dev, uuid); 162b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 16390e0b5f1STomas Winkler 164b7d88514STomas Winkler return me_cl; 16590e0b5f1STomas Winkler } 16690e0b5f1STomas Winkler 16790e0b5f1STomas Winkler /** 168a8605ea2SAlexander Usyskin * mei_me_cl_by_id - locate me client by client id 16979563db9STomas Winkler * increases ref count 17090e0b5f1STomas Winkler * 17190e0b5f1STomas Winkler * @dev: the device structure 17290e0b5f1STomas Winkler * @client_id: me client id 17390e0b5f1STomas Winkler * 174a8605ea2SAlexander Usyskin * Return: me client or NULL if not found 175b7d88514STomas Winkler * 176b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 17790e0b5f1STomas Winkler */ 178d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 17990e0b5f1STomas Winkler { 180a27a76d3SAlexander Usyskin 181b7d88514STomas Winkler struct mei_me_client *__me_cl, *me_cl = NULL; 182a27a76d3SAlexander Usyskin 183b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 184b7d88514STomas Winkler list_for_each_entry(__me_cl, &dev->me_clients, list) { 185b7d88514STomas Winkler if (__me_cl->client_id == client_id) { 186b7d88514STomas Winkler me_cl = mei_me_cl_get(__me_cl); 187b7d88514STomas Winkler break; 188b7d88514STomas Winkler } 189b7d88514STomas Winkler } 190b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 191b7d88514STomas Winkler 192b7d88514STomas Winkler return me_cl; 193b7d88514STomas Winkler } 194b7d88514STomas Winkler 195b7d88514STomas Winkler /** 196b7d88514STomas Winkler * __mei_me_cl_by_uuid_id - locate me client by client id and uuid 197b7d88514STomas Winkler * increases ref count 198b7d88514STomas Winkler * 199b7d88514STomas Winkler * @dev: the device structure 200b7d88514STomas Winkler * @uuid: me client uuid 201b7d88514STomas Winkler * @client_id: me client id 202b7d88514STomas Winkler * 203b7d88514STomas Winkler * Return: me client or null if not found 204b7d88514STomas Winkler * 205b7d88514STomas Winkler * Locking: dev->me_clients_rwsem 206b7d88514STomas Winkler */ 207b7d88514STomas Winkler static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev, 208b7d88514STomas Winkler const uuid_le *uuid, u8 client_id) 209b7d88514STomas Winkler { 210b7d88514STomas Winkler struct mei_me_client *me_cl; 211b7d88514STomas Winkler const uuid_le *pn; 212b7d88514STomas Winkler 213b7d88514STomas Winkler WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem)); 214b7d88514STomas Winkler 215b7d88514STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) { 216b7d88514STomas Winkler pn = &me_cl->props.protocol_name; 217b7d88514STomas Winkler if (uuid_le_cmp(*uuid, *pn) == 0 && 218b7d88514STomas Winkler me_cl->client_id == client_id) 21979563db9STomas Winkler return mei_me_cl_get(me_cl); 220b7d88514STomas Winkler } 22179563db9STomas Winkler 222d320832fSTomas Winkler return NULL; 22390e0b5f1STomas Winkler } 2249ca9050bSTomas Winkler 225b7d88514STomas Winkler 226a8605ea2SAlexander Usyskin /** 227a8605ea2SAlexander Usyskin * mei_me_cl_by_uuid_id - locate me client by client id and uuid 22879563db9STomas Winkler * increases ref count 229a8605ea2SAlexander Usyskin * 230a8605ea2SAlexander Usyskin * @dev: the device structure 231a8605ea2SAlexander Usyskin * @uuid: me client uuid 232a8605ea2SAlexander Usyskin * @client_id: me client id 233a8605ea2SAlexander Usyskin * 234b7d88514STomas Winkler * Return: me client or null if not found 235a8605ea2SAlexander Usyskin */ 236d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, 237d880f329STomas Winkler const uuid_le *uuid, u8 client_id) 238d880f329STomas Winkler { 239d880f329STomas Winkler struct mei_me_client *me_cl; 240d880f329STomas Winkler 241b7d88514STomas Winkler down_read(&dev->me_clients_rwsem); 242b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id); 243b7d88514STomas Winkler up_read(&dev->me_clients_rwsem); 24479563db9STomas Winkler 245b7d88514STomas Winkler return me_cl; 246d880f329STomas Winkler } 247d880f329STomas Winkler 24825ca6472STomas Winkler /** 24979563db9STomas Winkler * mei_me_cl_rm_by_uuid - remove all me clients matching uuid 25025ca6472STomas Winkler * 25125ca6472STomas Winkler * @dev: the device structure 25225ca6472STomas Winkler * @uuid: me client uuid 25379563db9STomas Winkler * 25479563db9STomas Winkler * Locking: called under "dev->device_lock" lock 25525ca6472STomas Winkler */ 25679563db9STomas Winkler void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) 25725ca6472STomas Winkler { 258b7d88514STomas Winkler struct mei_me_client *me_cl; 25925ca6472STomas Winkler 26079563db9STomas Winkler dev_dbg(dev->dev, "remove %pUl\n", uuid); 261b7d88514STomas Winkler 262b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 263b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid(dev, uuid); 264b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 265b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 26679563db9STomas Winkler } 26779563db9STomas Winkler 26879563db9STomas Winkler /** 26979563db9STomas Winkler * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id 27079563db9STomas Winkler * 27179563db9STomas Winkler * @dev: the device structure 27279563db9STomas Winkler * @uuid: me client uuid 27379563db9STomas Winkler * @id: me client id 27479563db9STomas Winkler * 27579563db9STomas Winkler * Locking: called under "dev->device_lock" lock 27679563db9STomas Winkler */ 27779563db9STomas Winkler void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id) 27879563db9STomas Winkler { 279b7d88514STomas Winkler struct mei_me_client *me_cl; 28079563db9STomas Winkler 28179563db9STomas Winkler dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id); 282b7d88514STomas Winkler 283b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 284b7d88514STomas Winkler me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id); 285b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 286b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 28725ca6472STomas Winkler } 28879563db9STomas Winkler 28979563db9STomas Winkler /** 29079563db9STomas Winkler * mei_me_cl_rm_all - remove all me clients 29179563db9STomas Winkler * 29279563db9STomas Winkler * @dev: the device structure 29379563db9STomas Winkler * 29479563db9STomas Winkler * Locking: called under "dev->device_lock" lock 29579563db9STomas Winkler */ 29679563db9STomas Winkler void mei_me_cl_rm_all(struct mei_device *dev) 29779563db9STomas Winkler { 29879563db9STomas Winkler struct mei_me_client *me_cl, *next; 29979563db9STomas Winkler 300b7d88514STomas Winkler down_write(&dev->me_clients_rwsem); 30179563db9STomas Winkler list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) 302b7d88514STomas Winkler __mei_me_cl_del(dev, me_cl); 303b7d88514STomas Winkler up_write(&dev->me_clients_rwsem); 30425ca6472STomas Winkler } 30525ca6472STomas Winkler 3069ca9050bSTomas Winkler /** 307cc99ecfdSTomas Winkler * mei_cl_cmp_id - tells if the clients are the same 308cc99ecfdSTomas Winkler * 309cc99ecfdSTomas Winkler * @cl1: host client 1 310cc99ecfdSTomas Winkler * @cl2: host client 2 311cc99ecfdSTomas Winkler * 312a8605ea2SAlexander Usyskin * Return: true - if the clients has same host and me ids 313cc99ecfdSTomas Winkler * false - otherwise 314cc99ecfdSTomas Winkler */ 315cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, 316cc99ecfdSTomas Winkler const struct mei_cl *cl2) 317cc99ecfdSTomas Winkler { 318cc99ecfdSTomas Winkler return cl1 && cl2 && 319cc99ecfdSTomas Winkler (cl1->host_client_id == cl2->host_client_id) && 320cc99ecfdSTomas Winkler (cl1->me_client_id == cl2->me_client_id); 321cc99ecfdSTomas Winkler } 322cc99ecfdSTomas Winkler 323cc99ecfdSTomas Winkler /** 3249ca9050bSTomas Winkler * mei_io_cb_free - free mei_cb_private related memory 3259ca9050bSTomas Winkler * 3269ca9050bSTomas Winkler * @cb: mei callback struct 3279ca9050bSTomas Winkler */ 3289ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb) 3299ca9050bSTomas Winkler { 3309ca9050bSTomas Winkler if (cb == NULL) 3319ca9050bSTomas Winkler return; 3329ca9050bSTomas Winkler 333928fa666STomas Winkler list_del(&cb->list); 3345db7514dSTomas Winkler kfree(cb->buf.data); 3359ca9050bSTomas Winkler kfree(cb); 3369ca9050bSTomas Winkler } 3379ca9050bSTomas Winkler 3389ca9050bSTomas Winkler /** 3399ca9050bSTomas Winkler * mei_io_cb_init - allocate and initialize io callback 3409ca9050bSTomas Winkler * 341a8605ea2SAlexander Usyskin * @cl: mei client 342bca67d68STomas Winkler * @type: operation type 343393b148fSMasanari Iida * @fp: pointer to file structure 3449ca9050bSTomas Winkler * 345a8605ea2SAlexander Usyskin * Return: mei_cl_cb pointer or NULL; 3469ca9050bSTomas Winkler */ 347bca67d68STomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, 348bca67d68STomas Winkler struct file *fp) 3499ca9050bSTomas Winkler { 3509ca9050bSTomas Winkler struct mei_cl_cb *cb; 3519ca9050bSTomas Winkler 3529ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 3539ca9050bSTomas Winkler if (!cb) 3549ca9050bSTomas Winkler return NULL; 3559ca9050bSTomas Winkler 356928fa666STomas Winkler INIT_LIST_HEAD(&cb->list); 3579ca9050bSTomas Winkler cb->file_object = fp; 3589ca9050bSTomas Winkler cb->cl = cl; 3599ca9050bSTomas Winkler cb->buf_idx = 0; 360bca67d68STomas Winkler cb->fop_type = type; 3619ca9050bSTomas Winkler return cb; 3629ca9050bSTomas Winkler } 3639ca9050bSTomas Winkler 3649ca9050bSTomas Winkler /** 365928fa666STomas Winkler * __mei_io_list_flush - removes and frees cbs belonging to cl. 366928fa666STomas Winkler * 367928fa666STomas Winkler * @list: an instance of our list structure 368928fa666STomas Winkler * @cl: host client, can be NULL for flushing the whole list 369928fa666STomas Winkler * @free: whether to free the cbs 370928fa666STomas Winkler */ 371928fa666STomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list, 372928fa666STomas Winkler struct mei_cl *cl, bool free) 373928fa666STomas Winkler { 374928fa666STomas Winkler struct mei_cl_cb *cb, *next; 375928fa666STomas Winkler 376928fa666STomas Winkler /* enable removing everything if no cl is specified */ 377928fa666STomas Winkler list_for_each_entry_safe(cb, next, &list->list, list) { 378928fa666STomas Winkler if (!cl || mei_cl_cmp_id(cl, cb->cl)) { 379928fa666STomas Winkler list_del_init(&cb->list); 380928fa666STomas Winkler if (free) 381928fa666STomas Winkler mei_io_cb_free(cb); 382928fa666STomas Winkler } 383928fa666STomas Winkler } 384928fa666STomas Winkler } 385928fa666STomas Winkler 386928fa666STomas Winkler /** 387928fa666STomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 388928fa666STomas Winkler * 389928fa666STomas Winkler * @list: An instance of our list structure 390928fa666STomas Winkler * @cl: host client 391928fa666STomas Winkler */ 392928fa666STomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 393928fa666STomas Winkler { 394928fa666STomas Winkler __mei_io_list_flush(list, cl, false); 395928fa666STomas Winkler } 396928fa666STomas Winkler 397928fa666STomas Winkler /** 398928fa666STomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 399928fa666STomas Winkler * 400928fa666STomas Winkler * @list: An instance of our list structure 401928fa666STomas Winkler * @cl: host client 402928fa666STomas Winkler */ 403928fa666STomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) 404928fa666STomas Winkler { 405928fa666STomas Winkler __mei_io_list_flush(list, cl, true); 406928fa666STomas Winkler } 407928fa666STomas Winkler 408928fa666STomas Winkler /** 4095db7514dSTomas Winkler * mei_io_cb_alloc_buf - allocate callback buffer 4109ca9050bSTomas Winkler * 411393b148fSMasanari Iida * @cb: io callback structure 412393b148fSMasanari Iida * @length: size of the buffer 4139ca9050bSTomas Winkler * 414a8605ea2SAlexander Usyskin * Return: 0 on success 4159ca9050bSTomas Winkler * -EINVAL if cb is NULL 4169ca9050bSTomas Winkler * -ENOMEM if allocation failed 4179ca9050bSTomas Winkler */ 4185db7514dSTomas Winkler int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length) 4199ca9050bSTomas Winkler { 4209ca9050bSTomas Winkler if (!cb) 4219ca9050bSTomas Winkler return -EINVAL; 4229ca9050bSTomas Winkler 4239ca9050bSTomas Winkler if (length == 0) 4249ca9050bSTomas Winkler return 0; 4259ca9050bSTomas Winkler 4265db7514dSTomas Winkler cb->buf.data = kmalloc(length, GFP_KERNEL); 4275db7514dSTomas Winkler if (!cb->buf.data) 4289ca9050bSTomas Winkler return -ENOMEM; 4295db7514dSTomas Winkler cb->buf.size = length; 4309ca9050bSTomas Winkler return 0; 4319ca9050bSTomas Winkler } 4329ca9050bSTomas Winkler 4339ca9050bSTomas Winkler /** 434bca67d68STomas Winkler * mei_cl_alloc_cb - a convenient wrapper for allocating read cb 435bca67d68STomas Winkler * 436bca67d68STomas Winkler * @cl: host client 437bca67d68STomas Winkler * @length: size of the buffer 438bca67d68STomas Winkler * @type: operation type 439bca67d68STomas Winkler * @fp: associated file pointer (might be NULL) 440bca67d68STomas Winkler * 441bca67d68STomas Winkler * Return: cb on success and NULL on failure 442bca67d68STomas Winkler */ 443bca67d68STomas Winkler struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, 444bca67d68STomas Winkler enum mei_cb_file_ops type, struct file *fp) 445bca67d68STomas Winkler { 446bca67d68STomas Winkler struct mei_cl_cb *cb; 447bca67d68STomas Winkler 448bca67d68STomas Winkler cb = mei_io_cb_init(cl, type, fp); 449bca67d68STomas Winkler if (!cb) 450bca67d68STomas Winkler return NULL; 451bca67d68STomas Winkler 452bca67d68STomas Winkler if (mei_io_cb_alloc_buf(cb, length)) { 453bca67d68STomas Winkler mei_io_cb_free(cb); 454bca67d68STomas Winkler return NULL; 455bca67d68STomas Winkler } 456bca67d68STomas Winkler 457bca67d68STomas Winkler return cb; 458bca67d68STomas Winkler } 459bca67d68STomas Winkler 460bca67d68STomas Winkler /** 461a9bed610STomas Winkler * mei_cl_read_cb - find this cl's callback in the read list 462a9bed610STomas Winkler * for a specific file 463a9bed610STomas Winkler * 464a9bed610STomas Winkler * @cl: host client 465a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 466a9bed610STomas Winkler * 467a9bed610STomas Winkler * Return: cb on success, NULL if cb is not found 468a9bed610STomas Winkler */ 469a9bed610STomas Winkler struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) 470a9bed610STomas Winkler { 471a9bed610STomas Winkler struct mei_cl_cb *cb; 472a9bed610STomas Winkler 473a9bed610STomas Winkler list_for_each_entry(cb, &cl->rd_completed, list) 474a9bed610STomas Winkler if (!fp || fp == cb->file_object) 475a9bed610STomas Winkler return cb; 476a9bed610STomas Winkler 477a9bed610STomas Winkler return NULL; 478a9bed610STomas Winkler } 479a9bed610STomas Winkler 480a9bed610STomas Winkler /** 481a9bed610STomas Winkler * mei_cl_read_cb_flush - free client's read pending and completed cbs 482a9bed610STomas Winkler * for a specific file 483a9bed610STomas Winkler * 484a9bed610STomas Winkler * @cl: host client 485a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 486a9bed610STomas Winkler */ 487a9bed610STomas Winkler void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) 488a9bed610STomas Winkler { 489a9bed610STomas Winkler struct mei_cl_cb *cb, *next; 490a9bed610STomas Winkler 491a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_completed, list) 492a9bed610STomas Winkler if (!fp || fp == cb->file_object) 493a9bed610STomas Winkler mei_io_cb_free(cb); 494a9bed610STomas Winkler 495a9bed610STomas Winkler 496a9bed610STomas Winkler list_for_each_entry_safe(cb, next, &cl->rd_pending, list) 497a9bed610STomas Winkler if (!fp || fp == cb->file_object) 498a9bed610STomas Winkler mei_io_cb_free(cb); 499a9bed610STomas Winkler } 500a9bed610STomas Winkler 501a9bed610STomas Winkler /** 5029ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 5039ca9050bSTomas Winkler * 5049ca9050bSTomas Winkler * @cl: host client 505a9bed610STomas Winkler * @fp: file pointer (matching cb file object), may be NULL 506ce23139cSAlexander Usyskin * 507ce23139cSAlexander Usyskin * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. 5089ca9050bSTomas Winkler */ 509a9bed610STomas Winkler int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) 5109ca9050bSTomas Winkler { 511c0abffbdSAlexander Usyskin struct mei_device *dev; 512c0abffbdSAlexander Usyskin 51390e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5149ca9050bSTomas Winkler return -EINVAL; 5159ca9050bSTomas Winkler 516c0abffbdSAlexander Usyskin dev = cl->dev; 517c0abffbdSAlexander Usyskin 518c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 519cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 520cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 5219ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 5229ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 5239ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 5249ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); 525a9bed610STomas Winkler 526a9bed610STomas Winkler mei_cl_read_cb_flush(cl, fp); 527a9bed610STomas Winkler 5289ca9050bSTomas Winkler return 0; 5299ca9050bSTomas Winkler } 5309ca9050bSTomas Winkler 5319ca9050bSTomas Winkler 5329ca9050bSTomas Winkler /** 53383ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 5349ca9050bSTomas Winkler * 5359ca9050bSTomas Winkler * @cl: host client to be initialized 5369ca9050bSTomas Winkler * @dev: mei device 5379ca9050bSTomas Winkler */ 5389ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 5399ca9050bSTomas Winkler { 5409ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 5419ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 5429ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 5439ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 544a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_completed); 545a9bed610STomas Winkler INIT_LIST_HEAD(&cl->rd_pending); 5469ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 547a7b71bc0SSamuel Ortiz INIT_LIST_HEAD(&cl->device_link); 5489ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 5499ca9050bSTomas Winkler cl->dev = dev; 5509ca9050bSTomas Winkler } 5519ca9050bSTomas Winkler 5529ca9050bSTomas Winkler /** 5539ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 5549ca9050bSTomas Winkler * 5559ca9050bSTomas Winkler * @dev: mei device 556a8605ea2SAlexander Usyskin * Return: The allocated file or NULL on failure 5579ca9050bSTomas Winkler */ 5589ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 5599ca9050bSTomas Winkler { 5609ca9050bSTomas Winkler struct mei_cl *cl; 5619ca9050bSTomas Winkler 5629ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 5639ca9050bSTomas Winkler if (!cl) 5649ca9050bSTomas Winkler return NULL; 5659ca9050bSTomas Winkler 5669ca9050bSTomas Winkler mei_cl_init(cl, dev); 5679ca9050bSTomas Winkler 5689ca9050bSTomas Winkler return cl; 5699ca9050bSTomas Winkler } 5709ca9050bSTomas Winkler 57190e0b5f1STomas Winkler /** 5723908be6fSAlexander Usyskin * mei_cl_link - allocate host id in the host map 5739ca9050bSTomas Winkler * 5743908be6fSAlexander Usyskin * @cl: host client 57503b8d341STomas Winkler * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one 576393b148fSMasanari Iida * 577a8605ea2SAlexander Usyskin * Return: 0 on success 5789ca9050bSTomas Winkler * -EINVAL on incorrect values 57903b8d341STomas Winkler * -EMFILE if open count exceeded. 5809ca9050bSTomas Winkler */ 581781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 5829ca9050bSTomas Winkler { 58390e0b5f1STomas Winkler struct mei_device *dev; 58422f96a0eSTomas Winkler long open_handle_count; 5859ca9050bSTomas Winkler 586781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5879ca9050bSTomas Winkler return -EINVAL; 5889ca9050bSTomas Winkler 58990e0b5f1STomas Winkler dev = cl->dev; 59090e0b5f1STomas Winkler 59183ce0741SAlexander Usyskin /* If Id is not assigned get one*/ 592781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 593781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 594781d0d89STomas Winkler MEI_CLIENTS_MAX); 5959ca9050bSTomas Winkler 596781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 5972bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 598e036cc57STomas Winkler return -EMFILE; 599e036cc57STomas Winkler } 600e036cc57STomas Winkler 60122f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 60222f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 6032bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 604e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 605e036cc57STomas Winkler return -EMFILE; 6069ca9050bSTomas Winkler } 607781d0d89STomas Winkler 608781d0d89STomas Winkler dev->open_handle_count++; 609781d0d89STomas Winkler 610781d0d89STomas Winkler cl->host_client_id = id; 611781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 612781d0d89STomas Winkler 613781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 614781d0d89STomas Winkler 615781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 616781d0d89STomas Winkler 617c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 618781d0d89STomas Winkler return 0; 619781d0d89STomas Winkler } 620781d0d89STomas Winkler 6219ca9050bSTomas Winkler /** 62290e0b5f1STomas Winkler * mei_cl_unlink - remove me_cl from the list 6239ca9050bSTomas Winkler * 624393b148fSMasanari Iida * @cl: host client 625ce23139cSAlexander Usyskin * 626ce23139cSAlexander Usyskin * Return: always 0 6279ca9050bSTomas Winkler */ 62890e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 6299ca9050bSTomas Winkler { 63090e0b5f1STomas Winkler struct mei_device *dev; 63190e0b5f1STomas Winkler 632781d0d89STomas Winkler /* don't shout on error exit path */ 633781d0d89STomas Winkler if (!cl) 634781d0d89STomas Winkler return 0; 635781d0d89STomas Winkler 6368e9a4a9aSTomas Winkler /* wd and amthif might not be initialized */ 6378e9a4a9aSTomas Winkler if (!cl->dev) 6388e9a4a9aSTomas Winkler return 0; 63990e0b5f1STomas Winkler 64090e0b5f1STomas Winkler dev = cl->dev; 64190e0b5f1STomas Winkler 642a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 643a14c44d8STomas Winkler 64422f96a0eSTomas Winkler if (dev->open_handle_count > 0) 64522f96a0eSTomas Winkler dev->open_handle_count--; 64622f96a0eSTomas Winkler 64722f96a0eSTomas Winkler /* never clear the 0 bit */ 64822f96a0eSTomas Winkler if (cl->host_client_id) 64922f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 65022f96a0eSTomas Winkler 65122f96a0eSTomas Winkler list_del_init(&cl->link); 65222f96a0eSTomas Winkler 65322f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 65422f96a0eSTomas Winkler 65590e0b5f1STomas Winkler return 0; 6569ca9050bSTomas Winkler } 6579ca9050bSTomas Winkler 6589ca9050bSTomas Winkler 6599ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 6609ca9050bSTomas Winkler { 661b7d88514STomas Winkler struct mei_device *dev = 662b7d88514STomas Winkler container_of(work, struct mei_device, init_work); 6635ca2d388STomas Winkler struct mei_me_client *me_cl; 6649ca9050bSTomas Winkler 6659ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 6669ca9050bSTomas Winkler 6679ca9050bSTomas Winkler 668b7d88514STomas Winkler me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); 669b7d88514STomas Winkler if (me_cl) 6709ca9050bSTomas Winkler mei_amthif_host_init(dev); 671b43baf69STomas Winkler mei_me_cl_put(me_cl); 672b7d88514STomas Winkler 673b7d88514STomas Winkler me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); 674b7d88514STomas Winkler if (me_cl) 6759ca9050bSTomas Winkler mei_wd_host_init(dev); 676b43baf69STomas Winkler mei_me_cl_put(me_cl); 677b7d88514STomas Winkler 678b7d88514STomas Winkler me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); 679b7d88514STomas Winkler if (me_cl) 68059fcd7c6SSamuel Ortiz mei_nfc_host_init(dev); 681b43baf69STomas Winkler mei_me_cl_put(me_cl); 68259fcd7c6SSamuel Ortiz 6839ca9050bSTomas Winkler 6849ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 6856adb8efbSTomas Winkler dev->reset_count = 0; 6869ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 68704bb139aSTomas Winkler 6882bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 6892bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 6902bf94cabSTomas Winkler pm_runtime_autosuspend(dev->dev); 6919ca9050bSTomas Winkler } 6929ca9050bSTomas Winkler 6936aae48ffSTomas Winkler /** 694a8605ea2SAlexander Usyskin * mei_hbuf_acquire - try to acquire host buffer 6956aae48ffSTomas Winkler * 6966aae48ffSTomas Winkler * @dev: the device structure 697a8605ea2SAlexander Usyskin * Return: true if host buffer was acquired 6986aae48ffSTomas Winkler */ 6996aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 7006aae48ffSTomas Winkler { 70104bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 70204bb139aSTomas Winkler dev->pg_event == MEI_PG_EVENT_WAIT) { 7032bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 70404bb139aSTomas Winkler return false; 70504bb139aSTomas Winkler } 70604bb139aSTomas Winkler 7076aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 7082bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 7096aae48ffSTomas Winkler return false; 7106aae48ffSTomas Winkler } 7116aae48ffSTomas Winkler 7126aae48ffSTomas Winkler dev->hbuf_is_ready = false; 7136aae48ffSTomas Winkler 7146aae48ffSTomas Winkler return true; 7156aae48ffSTomas Winkler } 7169ca9050bSTomas Winkler 7179ca9050bSTomas Winkler /** 71883ce0741SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 7199ca9050bSTomas Winkler * 72090e0b5f1STomas Winkler * @cl: host client 7219ca9050bSTomas Winkler * 7229ca9050bSTomas Winkler * Locking: called under "dev->device_lock" lock 7239ca9050bSTomas Winkler * 724a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 7259ca9050bSTomas Winkler */ 72690e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl) 7279ca9050bSTomas Winkler { 72890e0b5f1STomas Winkler struct mei_device *dev; 7299ca9050bSTomas Winkler struct mei_cl_cb *cb; 730fe2f17ebSAlexander Usyskin int rets; 7319ca9050bSTomas Winkler 73290e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 7339ca9050bSTomas Winkler return -ENODEV; 7349ca9050bSTomas Winkler 73590e0b5f1STomas Winkler dev = cl->dev; 73690e0b5f1STomas Winkler 737c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 738c0abffbdSAlexander Usyskin 7399ca9050bSTomas Winkler if (cl->state != MEI_FILE_DISCONNECTING) 7409ca9050bSTomas Winkler return 0; 7419ca9050bSTomas Winkler 7422bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 74304bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 7442bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 74504bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 74604bb139aSTomas Winkler return rets; 74704bb139aSTomas Winkler } 74804bb139aSTomas Winkler 749bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); 750bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 751bca67d68STomas Winkler if (rets) 75204bb139aSTomas Winkler goto free; 7535a8373fbSTomas Winkler 7546aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 7559ca9050bSTomas Winkler if (mei_hbm_cl_disconnect_req(dev, cl)) { 7569ca9050bSTomas Winkler rets = -ENODEV; 757c0abffbdSAlexander Usyskin cl_err(dev, cl, "failed to disconnect.\n"); 7589ca9050bSTomas Winkler goto free; 7599ca9050bSTomas Winkler } 76022b987a3SAlexander Usyskin cl->timer_count = MEI_CONNECT_TIMEOUT; 7619ca9050bSTomas Winkler mdelay(10); /* Wait for hardware disconnection ready */ 7629ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 7639ca9050bSTomas Winkler } else { 764c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 7659ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 7669ca9050bSTomas Winkler 7679ca9050bSTomas Winkler } 7689ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 7699ca9050bSTomas Winkler 77012f45ed4STomas Winkler wait_event_timeout(cl->wait, 7719ca9050bSTomas Winkler MEI_FILE_DISCONNECTED == cl->state, 7729ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 7739ca9050bSTomas Winkler 7749ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 775fe2f17ebSAlexander Usyskin 7769ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED == cl->state) { 7779ca9050bSTomas Winkler rets = 0; 778c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 7799ca9050bSTomas Winkler } else { 780fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 781fe2f17ebSAlexander Usyskin rets = -ETIME; 7829ca9050bSTomas Winkler } 7839ca9050bSTomas Winkler 7849ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 7859ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 7869ca9050bSTomas Winkler free: 78704bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 7882bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 7892bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 79004bb139aSTomas Winkler 7919ca9050bSTomas Winkler mei_io_cb_free(cb); 7929ca9050bSTomas Winkler return rets; 7939ca9050bSTomas Winkler } 7949ca9050bSTomas Winkler 7959ca9050bSTomas Winkler 7969ca9050bSTomas Winkler /** 79790e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 79890e0b5f1STomas Winkler * client with the same me client id is connecting 7999ca9050bSTomas Winkler * 8009ca9050bSTomas Winkler * @cl: private data of the file object 8019ca9050bSTomas Winkler * 802a8605ea2SAlexander Usyskin * Return: true if other client is connected, false - otherwise. 8039ca9050bSTomas Winkler */ 80490e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl) 8059ca9050bSTomas Winkler { 80690e0b5f1STomas Winkler struct mei_device *dev; 80731f88f57STomas Winkler struct mei_cl *ocl; /* the other client */ 8089ca9050bSTomas Winkler 80990e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 81090e0b5f1STomas Winkler return false; 81190e0b5f1STomas Winkler 81290e0b5f1STomas Winkler dev = cl->dev; 81390e0b5f1STomas Winkler 81431f88f57STomas Winkler list_for_each_entry(ocl, &dev->file_list, link) { 81531f88f57STomas Winkler if (ocl->state == MEI_FILE_CONNECTING && 81631f88f57STomas Winkler ocl != cl && 81731f88f57STomas Winkler cl->me_client_id == ocl->me_client_id) 81890e0b5f1STomas Winkler return true; 8199ca9050bSTomas Winkler 8209ca9050bSTomas Winkler } 82190e0b5f1STomas Winkler 82290e0b5f1STomas Winkler return false; 8239ca9050bSTomas Winkler } 8249ca9050bSTomas Winkler 8259ca9050bSTomas Winkler /** 82683ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 8279f81abdaSTomas Winkler * 8289f81abdaSTomas Winkler * @cl: host client 829a8605ea2SAlexander Usyskin * @file: pointer to file structure 8309f81abdaSTomas Winkler * 8319f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 8329f81abdaSTomas Winkler * 833a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 8349f81abdaSTomas Winkler */ 8359f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file) 8369f81abdaSTomas Winkler { 8379f81abdaSTomas Winkler struct mei_device *dev; 8389f81abdaSTomas Winkler struct mei_cl_cb *cb; 8399f81abdaSTomas Winkler int rets; 8409f81abdaSTomas Winkler 8419f81abdaSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 8429f81abdaSTomas Winkler return -ENODEV; 8439f81abdaSTomas Winkler 8449f81abdaSTomas Winkler dev = cl->dev; 8459f81abdaSTomas Winkler 8462bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 84704bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 8482bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 84904bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 85004bb139aSTomas Winkler return rets; 85104bb139aSTomas Winkler } 85204bb139aSTomas Winkler 853bca67d68STomas Winkler cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file); 854bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 855bca67d68STomas Winkler if (rets) 8569f81abdaSTomas Winkler goto out; 8579f81abdaSTomas Winkler 8586aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 8596aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 860e4d8270eSAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 8619f81abdaSTomas Winkler if (mei_hbm_cl_connect_req(dev, cl)) { 8629f81abdaSTomas Winkler rets = -ENODEV; 8639f81abdaSTomas Winkler goto out; 8649f81abdaSTomas Winkler } 8659f81abdaSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 8669f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 8679f81abdaSTomas Winkler } else { 86873ab4232SAlexander Usyskin cl->state = MEI_FILE_INITIALIZING; 8699f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 8709f81abdaSTomas Winkler } 8719f81abdaSTomas Winkler 8729f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 87312f45ed4STomas Winkler wait_event_timeout(cl->wait, 8749f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 8759f81abdaSTomas Winkler cl->state == MEI_FILE_DISCONNECTED), 876206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 8779f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 8789f81abdaSTomas Winkler 8799f81abdaSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) { 8803e37ebb7SAlexander Usyskin cl->state = MEI_FILE_DISCONNECTED; 881285e2996SAlexander Usyskin /* something went really wrong */ 882285e2996SAlexander Usyskin if (!cl->status) 883285e2996SAlexander Usyskin cl->status = -EFAULT; 8849f81abdaSTomas Winkler 8859f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 8869f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 8879f81abdaSTomas Winkler } 8889f81abdaSTomas Winkler 8899f81abdaSTomas Winkler rets = cl->status; 8909f81abdaSTomas Winkler 8919f81abdaSTomas Winkler out: 89204bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 8932bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 8942bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 89504bb139aSTomas Winkler 8969f81abdaSTomas Winkler mei_io_cb_free(cb); 8979f81abdaSTomas Winkler return rets; 8989f81abdaSTomas Winkler } 8999f81abdaSTomas Winkler 9009f81abdaSTomas Winkler /** 90103b8d341STomas Winkler * mei_cl_alloc_linked - allocate and link host client 90203b8d341STomas Winkler * 90303b8d341STomas Winkler * @dev: the device structure 90403b8d341STomas Winkler * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one 90503b8d341STomas Winkler * 90603b8d341STomas Winkler * Return: cl on success ERR_PTR on failure 90703b8d341STomas Winkler */ 90803b8d341STomas Winkler struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id) 90903b8d341STomas Winkler { 91003b8d341STomas Winkler struct mei_cl *cl; 91103b8d341STomas Winkler int ret; 91203b8d341STomas Winkler 91303b8d341STomas Winkler cl = mei_cl_allocate(dev); 91403b8d341STomas Winkler if (!cl) { 91503b8d341STomas Winkler ret = -ENOMEM; 91603b8d341STomas Winkler goto err; 91703b8d341STomas Winkler } 91803b8d341STomas Winkler 91903b8d341STomas Winkler ret = mei_cl_link(cl, id); 92003b8d341STomas Winkler if (ret) 92103b8d341STomas Winkler goto err; 92203b8d341STomas Winkler 92303b8d341STomas Winkler return cl; 92403b8d341STomas Winkler err: 92503b8d341STomas Winkler kfree(cl); 92603b8d341STomas Winkler return ERR_PTR(ret); 92703b8d341STomas Winkler } 92803b8d341STomas Winkler 92903b8d341STomas Winkler 93003b8d341STomas Winkler 93103b8d341STomas Winkler /** 93290e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 9339ca9050bSTomas Winkler * 9349ca9050bSTomas Winkler * @cl: private data of the file object 9359ca9050bSTomas Winkler * 936a8605ea2SAlexander Usyskin * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 9379ca9050bSTomas Winkler * -ENOENT if mei_cl is not present 9389ca9050bSTomas Winkler * -EINVAL if single_recv_buf == 0 9399ca9050bSTomas Winkler */ 94090e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 9419ca9050bSTomas Winkler { 94290e0b5f1STomas Winkler struct mei_device *dev; 94312d00665SAlexander Usyskin struct mei_me_client *me_cl; 94479563db9STomas Winkler int rets = 0; 9459ca9050bSTomas Winkler 94690e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 94790e0b5f1STomas Winkler return -EINVAL; 94890e0b5f1STomas Winkler 94990e0b5f1STomas Winkler dev = cl->dev; 95090e0b5f1STomas Winkler 9519ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 9529ca9050bSTomas Winkler return 1; 9539ca9050bSTomas Winkler 9542e5df413STomas Winkler me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); 955d320832fSTomas Winkler if (!me_cl) { 95612d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 957d320832fSTomas Winkler return -ENOENT; 95812d00665SAlexander Usyskin } 95912d00665SAlexander Usyskin 96079563db9STomas Winkler if (me_cl->mei_flow_ctrl_creds > 0) { 96179563db9STomas Winkler rets = 1; 9629ca9050bSTomas Winkler if (WARN_ON(me_cl->props.single_recv_buf == 0)) 96379563db9STomas Winkler rets = -EINVAL; 96412d00665SAlexander Usyskin } 96579563db9STomas Winkler mei_me_cl_put(me_cl); 96679563db9STomas Winkler return rets; 9679ca9050bSTomas Winkler } 9689ca9050bSTomas Winkler 9699ca9050bSTomas Winkler /** 97090e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 9719ca9050bSTomas Winkler * 9729ca9050bSTomas Winkler * @cl: private data of the file object 973393b148fSMasanari Iida * 974a8605ea2SAlexander Usyskin * Return: 9759ca9050bSTomas Winkler * 0 on success 9769ca9050bSTomas Winkler * -ENOENT when me client is not found 9779ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 9789ca9050bSTomas Winkler */ 97990e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 9809ca9050bSTomas Winkler { 98190e0b5f1STomas Winkler struct mei_device *dev; 98212d00665SAlexander Usyskin struct mei_me_client *me_cl; 98379563db9STomas Winkler int rets; 9849ca9050bSTomas Winkler 98590e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 98690e0b5f1STomas Winkler return -EINVAL; 98790e0b5f1STomas Winkler 98890e0b5f1STomas Winkler dev = cl->dev; 98990e0b5f1STomas Winkler 9902e5df413STomas Winkler me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); 991d320832fSTomas Winkler if (!me_cl) { 99212d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 993d320832fSTomas Winkler return -ENOENT; 99412d00665SAlexander Usyskin } 9959ca9050bSTomas Winkler 996d320832fSTomas Winkler if (me_cl->props.single_recv_buf) { 99779563db9STomas Winkler if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) { 99879563db9STomas Winkler rets = -EINVAL; 99979563db9STomas Winkler goto out; 100079563db9STomas Winkler } 100112d00665SAlexander Usyskin me_cl->mei_flow_ctrl_creds--; 10029ca9050bSTomas Winkler } else { 100379563db9STomas Winkler if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) { 100479563db9STomas Winkler rets = -EINVAL; 100579563db9STomas Winkler goto out; 100679563db9STomas Winkler } 10079ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 10089ca9050bSTomas Winkler } 100979563db9STomas Winkler rets = 0; 101079563db9STomas Winkler out: 101179563db9STomas Winkler mei_me_cl_put(me_cl); 101279563db9STomas Winkler return rets; 10139ca9050bSTomas Winkler } 10149ca9050bSTomas Winkler 10159ca9050bSTomas Winkler /** 1016393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 10179ca9050bSTomas Winkler * 101890e0b5f1STomas Winkler * @cl: host client 1019ce23139cSAlexander Usyskin * @length: number of bytes to read 1020bca67d68STomas Winkler * @fp: pointer to file structure 10219ca9050bSTomas Winkler * 1022a8605ea2SAlexander Usyskin * Return: 0 on success, <0 on failure. 10239ca9050bSTomas Winkler */ 1024bca67d68STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp) 10259ca9050bSTomas Winkler { 102690e0b5f1STomas Winkler struct mei_device *dev; 10279ca9050bSTomas Winkler struct mei_cl_cb *cb; 1028d320832fSTomas Winkler struct mei_me_client *me_cl; 10299ca9050bSTomas Winkler int rets; 10309ca9050bSTomas Winkler 103190e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 103290e0b5f1STomas Winkler return -ENODEV; 103390e0b5f1STomas Winkler 103490e0b5f1STomas Winkler dev = cl->dev; 103590e0b5f1STomas Winkler 1036b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 10379ca9050bSTomas Winkler return -ENODEV; 10389ca9050bSTomas Winkler 1039a9bed610STomas Winkler /* HW currently supports only one pending read */ 1040a9bed610STomas Winkler if (!list_empty(&cl->rd_pending)) 10419ca9050bSTomas Winkler return -EBUSY; 1042a9bed610STomas Winkler 1043d880f329STomas Winkler me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); 1044d320832fSTomas Winkler if (!me_cl) { 1045c0abffbdSAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 10467ca96aa2SAlexander Usyskin return -ENOTTY; 10479ca9050bSTomas Winkler } 104879563db9STomas Winkler /* always allocate at least client max message */ 104979563db9STomas Winkler length = max_t(size_t, length, me_cl->props.max_msg_length); 105079563db9STomas Winkler mei_me_cl_put(me_cl); 10519ca9050bSTomas Winkler 10522bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 105304bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 10542bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 105504bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 105604bb139aSTomas Winkler return rets; 105704bb139aSTomas Winkler } 105804bb139aSTomas Winkler 1059bca67d68STomas Winkler cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp); 1060bca67d68STomas Winkler rets = cb ? 0 : -ENOMEM; 10619ca9050bSTomas Winkler if (rets) 106204bb139aSTomas Winkler goto out; 10639ca9050bSTomas Winkler 10646aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 106586113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 106686113500SAlexander Usyskin if (rets < 0) 106704bb139aSTomas Winkler goto out; 106804bb139aSTomas Winkler 1069a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_pending); 10709ca9050bSTomas Winkler } else { 10719ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 10729ca9050bSTomas Winkler } 1073accb884bSChao Bi 107404bb139aSTomas Winkler out: 107504bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 10762bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 10772bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 107804bb139aSTomas Winkler 107904bb139aSTomas Winkler if (rets) 10809ca9050bSTomas Winkler mei_io_cb_free(cb); 108104bb139aSTomas Winkler 10829ca9050bSTomas Winkler return rets; 10839ca9050bSTomas Winkler } 10849ca9050bSTomas Winkler 1085074b4c01STomas Winkler /** 10869d098192STomas Winkler * mei_cl_irq_write - write a message to device 108721767546STomas Winkler * from the interrupt thread context 108821767546STomas Winkler * 108921767546STomas Winkler * @cl: client 109021767546STomas Winkler * @cb: callback block. 109121767546STomas Winkler * @cmpl_list: complete list. 109221767546STomas Winkler * 1093a8605ea2SAlexander Usyskin * Return: 0, OK; otherwise error. 109421767546STomas Winkler */ 10959d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 10969d098192STomas Winkler struct mei_cl_cb *cmpl_list) 109721767546STomas Winkler { 1098136698e5STomas Winkler struct mei_device *dev; 1099136698e5STomas Winkler struct mei_msg_data *buf; 110021767546STomas Winkler struct mei_msg_hdr mei_hdr; 1101136698e5STomas Winkler size_t len; 1102136698e5STomas Winkler u32 msg_slots; 11039d098192STomas Winkler int slots; 11042ebf8c94STomas Winkler int rets; 110521767546STomas Winkler 1106136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1107136698e5STomas Winkler return -ENODEV; 1108136698e5STomas Winkler 1109136698e5STomas Winkler dev = cl->dev; 1110136698e5STomas Winkler 11115db7514dSTomas Winkler buf = &cb->buf; 1112136698e5STomas Winkler 1113136698e5STomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 1114136698e5STomas Winkler if (rets < 0) 1115136698e5STomas Winkler return rets; 1116136698e5STomas Winkler 1117136698e5STomas Winkler if (rets == 0) { 1118136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 1119136698e5STomas Winkler return 0; 1120136698e5STomas Winkler } 1121136698e5STomas Winkler 11229d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 1123136698e5STomas Winkler len = buf->size - cb->buf_idx; 1124136698e5STomas Winkler msg_slots = mei_data2slots(len); 1125136698e5STomas Winkler 112621767546STomas Winkler mei_hdr.host_addr = cl->host_client_id; 112721767546STomas Winkler mei_hdr.me_addr = cl->me_client_id; 112821767546STomas Winkler mei_hdr.reserved = 0; 1129479327fcSTomas Winkler mei_hdr.internal = cb->internal; 113021767546STomas Winkler 11319d098192STomas Winkler if (slots >= msg_slots) { 113221767546STomas Winkler mei_hdr.length = len; 113321767546STomas Winkler mei_hdr.msg_complete = 1; 113421767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 11359d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 11369d098192STomas Winkler msg_slots = slots; 11379d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 113821767546STomas Winkler mei_hdr.length = len; 113921767546STomas Winkler mei_hdr.msg_complete = 0; 114021767546STomas Winkler } else { 114121767546STomas Winkler /* wait for next time the host buffer is empty */ 114221767546STomas Winkler return 0; 114321767546STomas Winkler } 114421767546STomas Winkler 1145c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", 11465db7514dSTomas Winkler cb->buf.size, cb->buf_idx); 114721767546STomas Winkler 1148136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 11492ebf8c94STomas Winkler if (rets) { 11502ebf8c94STomas Winkler cl->status = rets; 115121767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 11522ebf8c94STomas Winkler return rets; 115321767546STomas Winkler } 115421767546STomas Winkler 115521767546STomas Winkler cl->status = 0; 11564dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 115721767546STomas Winkler cb->buf_idx += mei_hdr.length; 11588660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 11594dfaa9f7STomas Winkler 116021767546STomas Winkler if (mei_hdr.msg_complete) { 116121767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 11622ebf8c94STomas Winkler return -EIO; 116321767546STomas Winkler list_move_tail(&cb->list, &dev->write_waiting_list.list); 116421767546STomas Winkler } 116521767546STomas Winkler 116621767546STomas Winkler return 0; 116721767546STomas Winkler } 116821767546STomas Winkler 116921767546STomas Winkler /** 11704234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 1171a8605ea2SAlexander Usyskin * assumes device_lock is locked 11724234a6deSTomas Winkler * 11734234a6deSTomas Winkler * @cl: host client 1174a8605ea2SAlexander Usyskin * @cb: write callback with filled data 1175ce23139cSAlexander Usyskin * @blocking: block until completed 11764234a6deSTomas Winkler * 1177a8605ea2SAlexander Usyskin * Return: number of bytes sent on success, <0 on failure. 11784234a6deSTomas Winkler */ 11794234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 11804234a6deSTomas Winkler { 11814234a6deSTomas Winkler struct mei_device *dev; 11824234a6deSTomas Winkler struct mei_msg_data *buf; 11834234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 11844234a6deSTomas Winkler int rets; 11854234a6deSTomas Winkler 11864234a6deSTomas Winkler 11874234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 11884234a6deSTomas Winkler return -ENODEV; 11894234a6deSTomas Winkler 11904234a6deSTomas Winkler if (WARN_ON(!cb)) 11914234a6deSTomas Winkler return -EINVAL; 11924234a6deSTomas Winkler 11934234a6deSTomas Winkler dev = cl->dev; 11944234a6deSTomas Winkler 11954234a6deSTomas Winkler 11965db7514dSTomas Winkler buf = &cb->buf; 11974234a6deSTomas Winkler 11980a01e974SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", buf->size); 11994234a6deSTomas Winkler 12002bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 120104bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 12022bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 120304bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 120404bb139aSTomas Winkler return rets; 120504bb139aSTomas Winkler } 12064234a6deSTomas Winkler 12076aae48ffSTomas Winkler cb->buf_idx = 0; 12086aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 12096aae48ffSTomas Winkler 12106aae48ffSTomas Winkler mei_hdr.host_addr = cl->host_client_id; 12116aae48ffSTomas Winkler mei_hdr.me_addr = cl->me_client_id; 12126aae48ffSTomas Winkler mei_hdr.reserved = 0; 12136aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 12146aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 12154234a6deSTomas Winkler 12164234a6deSTomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 12174234a6deSTomas Winkler if (rets < 0) 12184234a6deSTomas Winkler goto err; 12194234a6deSTomas Winkler 12206aae48ffSTomas Winkler if (rets == 0) { 12216aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 12224234a6deSTomas Winkler rets = buf->size; 12234234a6deSTomas Winkler goto out; 12244234a6deSTomas Winkler } 12256aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 12266aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 12276aae48ffSTomas Winkler rets = buf->size; 12286aae48ffSTomas Winkler goto out; 12296aae48ffSTomas Winkler } 12304234a6deSTomas Winkler 12314234a6deSTomas Winkler /* Check for a maximum length */ 12324234a6deSTomas Winkler if (buf->size > mei_hbuf_max_len(dev)) { 12334234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 12344234a6deSTomas Winkler mei_hdr.msg_complete = 0; 12354234a6deSTomas Winkler } else { 12364234a6deSTomas Winkler mei_hdr.length = buf->size; 12374234a6deSTomas Winkler mei_hdr.msg_complete = 1; 12384234a6deSTomas Winkler } 12394234a6deSTomas Winkler 12402ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 12412ebf8c94STomas Winkler if (rets) 12424234a6deSTomas Winkler goto err; 12434234a6deSTomas Winkler 12444234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 12454234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 12468660172eSTomas Winkler cb->completed = mei_hdr.msg_complete == 1; 12474234a6deSTomas Winkler 12484234a6deSTomas Winkler out: 12494234a6deSTomas Winkler if (mei_hdr.msg_complete) { 12507ca96aa2SAlexander Usyskin rets = mei_cl_flow_ctrl_reduce(cl); 12517ca96aa2SAlexander Usyskin if (rets < 0) 12524234a6deSTomas Winkler goto err; 12537ca96aa2SAlexander Usyskin 12544234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 12554234a6deSTomas Winkler } else { 12564234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 12574234a6deSTomas Winkler } 12584234a6deSTomas Winkler 12594234a6deSTomas Winkler 12604234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 12614234a6deSTomas Winkler 12624234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 12637ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 12647ca96aa2SAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE); 12657ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 12667ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 12677ca96aa2SAlexander Usyskin if (rets) { 12684234a6deSTomas Winkler if (signal_pending(current)) 12694234a6deSTomas Winkler rets = -EINTR; 12707ca96aa2SAlexander Usyskin goto err; 12714234a6deSTomas Winkler } 12724234a6deSTomas Winkler } 12737ca96aa2SAlexander Usyskin 12747ca96aa2SAlexander Usyskin rets = buf->size; 12754234a6deSTomas Winkler err: 127604bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 12772bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 12782bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 127904bb139aSTomas Winkler 12804234a6deSTomas Winkler return rets; 12814234a6deSTomas Winkler } 12824234a6deSTomas Winkler 12834234a6deSTomas Winkler 1284db086fa9STomas Winkler /** 1285db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1286db086fa9STomas Winkler * 1287db086fa9STomas Winkler * @cl: private data of the file object. 1288db086fa9STomas Winkler * @cb: callback block. 1289db086fa9STomas Winkler */ 1290db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1291db086fa9STomas Winkler { 1292db086fa9STomas Winkler if (cb->fop_type == MEI_FOP_WRITE) { 1293db086fa9STomas Winkler mei_io_cb_free(cb); 1294db086fa9STomas Winkler cb = NULL; 1295db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1296db086fa9STomas Winkler if (waitqueue_active(&cl->tx_wait)) 1297db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1298db086fa9STomas Winkler 1299a9bed610STomas Winkler } else if (cb->fop_type == MEI_FOP_READ) { 1300a9bed610STomas Winkler list_add_tail(&cb->list, &cl->rd_completed); 1301db086fa9STomas Winkler if (waitqueue_active(&cl->rx_wait)) 13021d9013f0STomas Winkler wake_up_interruptible_all(&cl->rx_wait); 1303db086fa9STomas Winkler else 1304db086fa9STomas Winkler mei_cl_bus_rx_event(cl); 1305db086fa9STomas Winkler 1306db086fa9STomas Winkler } 1307db086fa9STomas Winkler } 1308db086fa9STomas Winkler 13094234a6deSTomas Winkler 13104234a6deSTomas Winkler /** 1311074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1312074b4c01STomas Winkler * 1313a8605ea2SAlexander Usyskin * @dev: mei device 1314074b4c01STomas Winkler */ 1315074b4c01STomas Winkler 1316074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1317074b4c01STomas Winkler { 131831f88f57STomas Winkler struct mei_cl *cl; 1319074b4c01STomas Winkler 132031f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1321074b4c01STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 1322074b4c01STomas Winkler cl->mei_flow_ctrl_creds = 0; 1323074b4c01STomas Winkler cl->timer_count = 0; 1324074b4c01STomas Winkler } 1325074b4c01STomas Winkler } 1326074b4c01STomas Winkler 1327074b4c01STomas Winkler 1328074b4c01STomas Winkler /** 13295290801cSTomas Winkler * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted 1330074b4c01STomas Winkler * 1331a8605ea2SAlexander Usyskin * @dev: mei device 1332074b4c01STomas Winkler */ 13335290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev) 1334074b4c01STomas Winkler { 133531f88f57STomas Winkler struct mei_cl *cl; 133692db1555STomas Winkler 133731f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1338074b4c01STomas Winkler if (waitqueue_active(&cl->rx_wait)) { 1339c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 1340074b4c01STomas Winkler wake_up_interruptible(&cl->rx_wait); 1341074b4c01STomas Winkler } 13425290801cSTomas Winkler if (waitqueue_active(&cl->tx_wait)) { 1343c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 13445290801cSTomas Winkler wake_up_interruptible(&cl->tx_wait); 13455290801cSTomas Winkler } 1346074b4c01STomas Winkler } 1347074b4c01STomas Winkler } 1348074b4c01STomas Winkler 1349074b4c01STomas Winkler /** 1350074b4c01STomas Winkler * mei_cl_all_write_clear - clear all pending writes 1351a8605ea2SAlexander Usyskin * 1352a8605ea2SAlexander Usyskin * @dev: mei device 1353074b4c01STomas Winkler */ 1354074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev) 1355074b4c01STomas Winkler { 1356cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_list, NULL); 1357cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_waiting_list, NULL); 1358074b4c01STomas Winkler } 1359074b4c01STomas Winkler 1360074b4c01STomas Winkler 1361