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/pci.h> 189ca9050bSTomas Winkler #include <linux/sched.h> 199ca9050bSTomas Winkler #include <linux/wait.h> 209ca9050bSTomas Winkler #include <linux/delay.h> 219ca9050bSTomas Winkler 229ca9050bSTomas Winkler #include <linux/mei.h> 239ca9050bSTomas Winkler 249ca9050bSTomas Winkler #include "mei_dev.h" 259ca9050bSTomas Winkler #include "hbm.h" 2690e0b5f1STomas Winkler #include "client.h" 2790e0b5f1STomas Winkler 2890e0b5f1STomas Winkler /** 2990e0b5f1STomas Winkler * mei_me_cl_by_uuid - locate index of me client 3090e0b5f1STomas Winkler * 3190e0b5f1STomas Winkler * @dev: mei device 3290e0b5f1STomas Winkler * returns me client index or -ENOENT if not found 3390e0b5f1STomas Winkler */ 3490e0b5f1STomas Winkler int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) 3590e0b5f1STomas Winkler { 3690e0b5f1STomas Winkler int i, res = -ENOENT; 3790e0b5f1STomas Winkler 3890e0b5f1STomas Winkler for (i = 0; i < dev->me_clients_num; ++i) 3990e0b5f1STomas Winkler if (uuid_le_cmp(*uuid, 4090e0b5f1STomas Winkler dev->me_clients[i].props.protocol_name) == 0) { 4190e0b5f1STomas Winkler res = i; 4290e0b5f1STomas Winkler break; 4390e0b5f1STomas Winkler } 4490e0b5f1STomas Winkler 4590e0b5f1STomas Winkler return res; 4690e0b5f1STomas Winkler } 4790e0b5f1STomas Winkler 4890e0b5f1STomas Winkler 4990e0b5f1STomas Winkler /** 5090e0b5f1STomas Winkler * mei_me_cl_by_id return index to me_clients for client_id 5190e0b5f1STomas Winkler * 5290e0b5f1STomas Winkler * @dev: the device structure 5390e0b5f1STomas Winkler * @client_id: me client id 5490e0b5f1STomas Winkler * 5590e0b5f1STomas Winkler * Locking: called under "dev->device_lock" lock 5690e0b5f1STomas Winkler * 5790e0b5f1STomas Winkler * returns index on success, -ENOENT on failure. 5890e0b5f1STomas Winkler */ 5990e0b5f1STomas Winkler 6090e0b5f1STomas Winkler int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 6190e0b5f1STomas Winkler { 6290e0b5f1STomas Winkler int i; 6390e0b5f1STomas Winkler for (i = 0; i < dev->me_clients_num; i++) 6490e0b5f1STomas Winkler if (dev->me_clients[i].client_id == client_id) 6590e0b5f1STomas Winkler break; 6690e0b5f1STomas Winkler if (WARN_ON(dev->me_clients[i].client_id != client_id)) 6790e0b5f1STomas Winkler return -ENOENT; 6890e0b5f1STomas Winkler 6990e0b5f1STomas Winkler if (i == dev->me_clients_num) 7090e0b5f1STomas Winkler return -ENOENT; 7190e0b5f1STomas Winkler 7290e0b5f1STomas Winkler return i; 7390e0b5f1STomas Winkler } 749ca9050bSTomas Winkler 759ca9050bSTomas Winkler 769ca9050bSTomas Winkler /** 779ca9050bSTomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 789ca9050bSTomas Winkler * 799ca9050bSTomas Winkler * @list: An instance of our list structure 809ca9050bSTomas Winkler * @cl: host client 819ca9050bSTomas Winkler */ 829ca9050bSTomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 839ca9050bSTomas Winkler { 849ca9050bSTomas Winkler struct mei_cl_cb *cb; 859ca9050bSTomas Winkler struct mei_cl_cb *next; 869ca9050bSTomas Winkler 879ca9050bSTomas Winkler list_for_each_entry_safe(cb, next, &list->list, list) { 889ca9050bSTomas Winkler if (cb->cl && mei_cl_cmp_id(cl, cb->cl)) 899ca9050bSTomas Winkler list_del(&cb->list); 909ca9050bSTomas Winkler } 919ca9050bSTomas Winkler } 929ca9050bSTomas Winkler 939ca9050bSTomas Winkler /** 949ca9050bSTomas Winkler * mei_io_cb_free - free mei_cb_private related memory 959ca9050bSTomas Winkler * 969ca9050bSTomas Winkler * @cb: mei callback struct 979ca9050bSTomas Winkler */ 989ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb) 999ca9050bSTomas Winkler { 1009ca9050bSTomas Winkler if (cb == NULL) 1019ca9050bSTomas Winkler return; 1029ca9050bSTomas Winkler 1039ca9050bSTomas Winkler kfree(cb->request_buffer.data); 1049ca9050bSTomas Winkler kfree(cb->response_buffer.data); 1059ca9050bSTomas Winkler kfree(cb); 1069ca9050bSTomas Winkler } 1079ca9050bSTomas Winkler 1089ca9050bSTomas Winkler /** 1099ca9050bSTomas Winkler * mei_io_cb_init - allocate and initialize io callback 1109ca9050bSTomas Winkler * 1119ca9050bSTomas Winkler * @cl - mei client 112393b148fSMasanari Iida * @fp: pointer to file structure 1139ca9050bSTomas Winkler * 1149ca9050bSTomas Winkler * returns mei_cl_cb pointer or NULL; 1159ca9050bSTomas Winkler */ 1169ca9050bSTomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) 1179ca9050bSTomas Winkler { 1189ca9050bSTomas Winkler struct mei_cl_cb *cb; 1199ca9050bSTomas Winkler 1209ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 1219ca9050bSTomas Winkler if (!cb) 1229ca9050bSTomas Winkler return NULL; 1239ca9050bSTomas Winkler 1249ca9050bSTomas Winkler mei_io_list_init(cb); 1259ca9050bSTomas Winkler 1269ca9050bSTomas Winkler cb->file_object = fp; 1279ca9050bSTomas Winkler cb->cl = cl; 1289ca9050bSTomas Winkler cb->buf_idx = 0; 1299ca9050bSTomas Winkler return cb; 1309ca9050bSTomas Winkler } 1319ca9050bSTomas Winkler 1329ca9050bSTomas Winkler /** 1339ca9050bSTomas Winkler * mei_io_cb_alloc_req_buf - allocate request buffer 1349ca9050bSTomas Winkler * 135393b148fSMasanari Iida * @cb: io callback structure 136393b148fSMasanari Iida * @length: size of the buffer 1379ca9050bSTomas Winkler * 1389ca9050bSTomas Winkler * returns 0 on success 1399ca9050bSTomas Winkler * -EINVAL if cb is NULL 1409ca9050bSTomas Winkler * -ENOMEM if allocation failed 1419ca9050bSTomas Winkler */ 1429ca9050bSTomas Winkler int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) 1439ca9050bSTomas Winkler { 1449ca9050bSTomas Winkler if (!cb) 1459ca9050bSTomas Winkler return -EINVAL; 1469ca9050bSTomas Winkler 1479ca9050bSTomas Winkler if (length == 0) 1489ca9050bSTomas Winkler return 0; 1499ca9050bSTomas Winkler 1509ca9050bSTomas Winkler cb->request_buffer.data = kmalloc(length, GFP_KERNEL); 1519ca9050bSTomas Winkler if (!cb->request_buffer.data) 1529ca9050bSTomas Winkler return -ENOMEM; 1539ca9050bSTomas Winkler cb->request_buffer.size = length; 1549ca9050bSTomas Winkler return 0; 1559ca9050bSTomas Winkler } 1569ca9050bSTomas Winkler /** 15783ce0741SAlexander Usyskin * mei_io_cb_alloc_resp_buf - allocate response buffer 1589ca9050bSTomas Winkler * 159393b148fSMasanari Iida * @cb: io callback structure 160393b148fSMasanari Iida * @length: size of the buffer 1619ca9050bSTomas Winkler * 1629ca9050bSTomas Winkler * returns 0 on success 1639ca9050bSTomas Winkler * -EINVAL if cb is NULL 1649ca9050bSTomas Winkler * -ENOMEM if allocation failed 1659ca9050bSTomas Winkler */ 1669ca9050bSTomas Winkler int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) 1679ca9050bSTomas Winkler { 1689ca9050bSTomas Winkler if (!cb) 1699ca9050bSTomas Winkler return -EINVAL; 1709ca9050bSTomas Winkler 1719ca9050bSTomas Winkler if (length == 0) 1729ca9050bSTomas Winkler return 0; 1739ca9050bSTomas Winkler 1749ca9050bSTomas Winkler cb->response_buffer.data = kmalloc(length, GFP_KERNEL); 1759ca9050bSTomas Winkler if (!cb->response_buffer.data) 1769ca9050bSTomas Winkler return -ENOMEM; 1779ca9050bSTomas Winkler cb->response_buffer.size = length; 1789ca9050bSTomas Winkler return 0; 1799ca9050bSTomas Winkler } 1809ca9050bSTomas Winkler 1819ca9050bSTomas Winkler 1829ca9050bSTomas Winkler 1839ca9050bSTomas Winkler /** 1849ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 1859ca9050bSTomas Winkler * 1869ca9050bSTomas Winkler * @cl: host client 1879ca9050bSTomas Winkler */ 1889ca9050bSTomas Winkler int mei_cl_flush_queues(struct mei_cl *cl) 1899ca9050bSTomas Winkler { 190c0abffbdSAlexander Usyskin struct mei_device *dev; 191c0abffbdSAlexander Usyskin 19290e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1939ca9050bSTomas Winkler return -EINVAL; 1949ca9050bSTomas Winkler 195c0abffbdSAlexander Usyskin dev = cl->dev; 196c0abffbdSAlexander Usyskin 197c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 1989ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->read_list, cl); 1999ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->write_list, cl); 2009ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->write_waiting_list, cl); 2019ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 2029ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 2039ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 2049ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); 2059ca9050bSTomas Winkler return 0; 2069ca9050bSTomas Winkler } 2079ca9050bSTomas Winkler 2089ca9050bSTomas Winkler 2099ca9050bSTomas Winkler /** 21083ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 2119ca9050bSTomas Winkler * 2129ca9050bSTomas Winkler * @cl: host client to be initialized 2139ca9050bSTomas Winkler * @dev: mei device 2149ca9050bSTomas Winkler */ 2159ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 2169ca9050bSTomas Winkler { 2179ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 2189ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 2199ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 2209ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 2219ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 222a7b71bc0SSamuel Ortiz INIT_LIST_HEAD(&cl->device_link); 2239ca9050bSTomas Winkler cl->reading_state = MEI_IDLE; 2249ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 2259ca9050bSTomas Winkler cl->dev = dev; 2269ca9050bSTomas Winkler } 2279ca9050bSTomas Winkler 2289ca9050bSTomas Winkler /** 2299ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 2309ca9050bSTomas Winkler * 2319ca9050bSTomas Winkler * @dev: mei device 2329ca9050bSTomas Winkler * returns The allocated file or NULL on failure 2339ca9050bSTomas Winkler */ 2349ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 2359ca9050bSTomas Winkler { 2369ca9050bSTomas Winkler struct mei_cl *cl; 2379ca9050bSTomas Winkler 2389ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 2399ca9050bSTomas Winkler if (!cl) 2409ca9050bSTomas Winkler return NULL; 2419ca9050bSTomas Winkler 2429ca9050bSTomas Winkler mei_cl_init(cl, dev); 2439ca9050bSTomas Winkler 2449ca9050bSTomas Winkler return cl; 2459ca9050bSTomas Winkler } 2469ca9050bSTomas Winkler 24790e0b5f1STomas Winkler /** 24890e0b5f1STomas Winkler * mei_cl_find_read_cb - find this cl's callback in the read list 24990e0b5f1STomas Winkler * 250393b148fSMasanari Iida * @cl: host client 251393b148fSMasanari Iida * 25290e0b5f1STomas Winkler * returns cb on success, NULL on error 25390e0b5f1STomas Winkler */ 25490e0b5f1STomas Winkler struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) 25590e0b5f1STomas Winkler { 25690e0b5f1STomas Winkler struct mei_device *dev = cl->dev; 25790e0b5f1STomas Winkler struct mei_cl_cb *cb = NULL; 25890e0b5f1STomas Winkler struct mei_cl_cb *next = NULL; 25990e0b5f1STomas Winkler 26090e0b5f1STomas Winkler list_for_each_entry_safe(cb, next, &dev->read_list.list, list) 26190e0b5f1STomas Winkler if (mei_cl_cmp_id(cl, cb->cl)) 26290e0b5f1STomas Winkler return cb; 26390e0b5f1STomas Winkler return NULL; 26490e0b5f1STomas Winkler } 26590e0b5f1STomas Winkler 26683ce0741SAlexander Usyskin /** mei_cl_link: allocate host id in the host map 2679ca9050bSTomas Winkler * 268781d0d89STomas Winkler * @cl - host client 26983ce0741SAlexander Usyskin * @id - fixed host id or -1 for generic one 270393b148fSMasanari Iida * 271781d0d89STomas Winkler * returns 0 on success 2729ca9050bSTomas Winkler * -EINVAL on incorrect values 2739ca9050bSTomas Winkler * -ENONET if client not found 2749ca9050bSTomas Winkler */ 275781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 2769ca9050bSTomas Winkler { 27790e0b5f1STomas Winkler struct mei_device *dev; 27822f96a0eSTomas Winkler long open_handle_count; 2799ca9050bSTomas Winkler 280781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 2819ca9050bSTomas Winkler return -EINVAL; 2829ca9050bSTomas Winkler 28390e0b5f1STomas Winkler dev = cl->dev; 28490e0b5f1STomas Winkler 28583ce0741SAlexander Usyskin /* If Id is not assigned get one*/ 286781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 287781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 288781d0d89STomas Winkler MEI_CLIENTS_MAX); 2899ca9050bSTomas Winkler 290781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 29183ce0741SAlexander Usyskin dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 292e036cc57STomas Winkler return -EMFILE; 293e036cc57STomas Winkler } 294e036cc57STomas Winkler 29522f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 29622f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 29783ce0741SAlexander Usyskin dev_err(&dev->pdev->dev, "open_handle_count exceeded %d", 298e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 299e036cc57STomas Winkler return -EMFILE; 3009ca9050bSTomas Winkler } 301781d0d89STomas Winkler 302781d0d89STomas Winkler dev->open_handle_count++; 303781d0d89STomas Winkler 304781d0d89STomas Winkler cl->host_client_id = id; 305781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 306781d0d89STomas Winkler 307781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 308781d0d89STomas Winkler 309781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 310781d0d89STomas Winkler 311c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 312781d0d89STomas Winkler return 0; 313781d0d89STomas Winkler } 314781d0d89STomas Winkler 3159ca9050bSTomas Winkler /** 31690e0b5f1STomas Winkler * mei_cl_unlink - remove me_cl from the list 3179ca9050bSTomas Winkler * 318393b148fSMasanari Iida * @cl: host client 3199ca9050bSTomas Winkler */ 32090e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 3219ca9050bSTomas Winkler { 32290e0b5f1STomas Winkler struct mei_device *dev; 32390e0b5f1STomas Winkler 324781d0d89STomas Winkler /* don't shout on error exit path */ 325781d0d89STomas Winkler if (!cl) 326781d0d89STomas Winkler return 0; 327781d0d89STomas Winkler 3288e9a4a9aSTomas Winkler /* wd and amthif might not be initialized */ 3298e9a4a9aSTomas Winkler if (!cl->dev) 3308e9a4a9aSTomas Winkler return 0; 33190e0b5f1STomas Winkler 33290e0b5f1STomas Winkler dev = cl->dev; 33390e0b5f1STomas Winkler 334a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 335a14c44d8STomas Winkler 33622f96a0eSTomas Winkler if (dev->open_handle_count > 0) 33722f96a0eSTomas Winkler dev->open_handle_count--; 33822f96a0eSTomas Winkler 33922f96a0eSTomas Winkler /* never clear the 0 bit */ 34022f96a0eSTomas Winkler if (cl->host_client_id) 34122f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 34222f96a0eSTomas Winkler 34322f96a0eSTomas Winkler list_del_init(&cl->link); 34422f96a0eSTomas Winkler 34522f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 34622f96a0eSTomas Winkler 34790e0b5f1STomas Winkler return 0; 3489ca9050bSTomas Winkler } 3499ca9050bSTomas Winkler 3509ca9050bSTomas Winkler 3519ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 3529ca9050bSTomas Winkler { 3539ca9050bSTomas Winkler struct mei_device *dev = container_of(work, 3549ca9050bSTomas Winkler struct mei_device, init_work); 3559ca9050bSTomas Winkler struct mei_client_properties *client_props; 3569ca9050bSTomas Winkler int i; 3579ca9050bSTomas Winkler 3589ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 3599ca9050bSTomas Winkler 3609ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 3619ca9050bSTomas Winkler client_props = &dev->me_clients[i].props; 3629ca9050bSTomas Winkler 3631a1aca42STomas Winkler if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) 3649ca9050bSTomas Winkler mei_amthif_host_init(dev); 3659ca9050bSTomas Winkler else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) 3669ca9050bSTomas Winkler mei_wd_host_init(dev); 36759fcd7c6SSamuel Ortiz else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) 36859fcd7c6SSamuel Ortiz mei_nfc_host_init(dev); 36959fcd7c6SSamuel Ortiz 3709ca9050bSTomas Winkler } 3719ca9050bSTomas Winkler 3729ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 3736adb8efbSTomas Winkler dev->reset_count = 0; 3749ca9050bSTomas Winkler 3759ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 3769ca9050bSTomas Winkler } 3779ca9050bSTomas Winkler 3789ca9050bSTomas Winkler 3799ca9050bSTomas Winkler /** 38083ce0741SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 3819ca9050bSTomas Winkler * 38290e0b5f1STomas Winkler * @cl: host client 3839ca9050bSTomas Winkler * 3849ca9050bSTomas Winkler * Locking: called under "dev->device_lock" lock 3859ca9050bSTomas Winkler * 3869ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 3879ca9050bSTomas Winkler */ 38890e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl) 3899ca9050bSTomas Winkler { 39090e0b5f1STomas Winkler struct mei_device *dev; 3919ca9050bSTomas Winkler struct mei_cl_cb *cb; 3929ca9050bSTomas Winkler int rets, err; 3939ca9050bSTomas Winkler 39490e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 3959ca9050bSTomas Winkler return -ENODEV; 3969ca9050bSTomas Winkler 39790e0b5f1STomas Winkler dev = cl->dev; 39890e0b5f1STomas Winkler 399c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 400c0abffbdSAlexander Usyskin 4019ca9050bSTomas Winkler if (cl->state != MEI_FILE_DISCONNECTING) 4029ca9050bSTomas Winkler return 0; 4039ca9050bSTomas Winkler 4049ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 4059ca9050bSTomas Winkler if (!cb) 4069ca9050bSTomas Winkler return -ENOMEM; 4079ca9050bSTomas Winkler 4089ca9050bSTomas Winkler cb->fop_type = MEI_FOP_CLOSE; 409330dd7daSTomas Winkler if (dev->hbuf_is_ready) { 410330dd7daSTomas Winkler dev->hbuf_is_ready = false; 4119ca9050bSTomas Winkler if (mei_hbm_cl_disconnect_req(dev, cl)) { 4129ca9050bSTomas Winkler rets = -ENODEV; 413c0abffbdSAlexander Usyskin cl_err(dev, cl, "failed to disconnect.\n"); 4149ca9050bSTomas Winkler goto free; 4159ca9050bSTomas Winkler } 4169ca9050bSTomas Winkler mdelay(10); /* Wait for hardware disconnection ready */ 4179ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 4189ca9050bSTomas Winkler } else { 419c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 4209ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 4219ca9050bSTomas Winkler 4229ca9050bSTomas Winkler } 4239ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 4249ca9050bSTomas Winkler 4259ca9050bSTomas Winkler err = wait_event_timeout(dev->wait_recvd_msg, 4269ca9050bSTomas Winkler MEI_FILE_DISCONNECTED == cl->state, 4279ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 4289ca9050bSTomas Winkler 4299ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 4309ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED == cl->state) { 4319ca9050bSTomas Winkler rets = 0; 432c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 4339ca9050bSTomas Winkler } else { 4349ca9050bSTomas Winkler rets = -ENODEV; 4359ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED != cl->state) 436c0abffbdSAlexander Usyskin cl_err(dev, cl, "wrong status client disconnect.\n"); 4379ca9050bSTomas Winkler 4389ca9050bSTomas Winkler if (err) 439c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", 4409ca9050bSTomas Winkler err); 4419ca9050bSTomas Winkler 442c0abffbdSAlexander Usyskin cl_err(dev, cl, "failed to disconnect from FW client.\n"); 4439ca9050bSTomas Winkler } 4449ca9050bSTomas Winkler 4459ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 4469ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 4479ca9050bSTomas Winkler free: 4489ca9050bSTomas Winkler mei_io_cb_free(cb); 4499ca9050bSTomas Winkler return rets; 4509ca9050bSTomas Winkler } 4519ca9050bSTomas Winkler 4529ca9050bSTomas Winkler 4539ca9050bSTomas Winkler /** 45490e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 45590e0b5f1STomas Winkler * client with the same me client id is connecting 4569ca9050bSTomas Winkler * 4579ca9050bSTomas Winkler * @cl: private data of the file object 4589ca9050bSTomas Winkler * 45983ce0741SAlexander Usyskin * returns true if other client is connected, false - otherwise. 4609ca9050bSTomas Winkler */ 46190e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl) 4629ca9050bSTomas Winkler { 46390e0b5f1STomas Winkler struct mei_device *dev; 46490e0b5f1STomas Winkler struct mei_cl *pos; 46590e0b5f1STomas Winkler struct mei_cl *next; 4669ca9050bSTomas Winkler 46790e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 46890e0b5f1STomas Winkler return false; 46990e0b5f1STomas Winkler 47090e0b5f1STomas Winkler dev = cl->dev; 47190e0b5f1STomas Winkler 47290e0b5f1STomas Winkler list_for_each_entry_safe(pos, next, &dev->file_list, link) { 47390e0b5f1STomas Winkler if ((pos->state == MEI_FILE_CONNECTING) && 47490e0b5f1STomas Winkler (pos != cl) && cl->me_client_id == pos->me_client_id) 47590e0b5f1STomas Winkler return true; 4769ca9050bSTomas Winkler 4779ca9050bSTomas Winkler } 47890e0b5f1STomas Winkler 47990e0b5f1STomas Winkler return false; 4809ca9050bSTomas Winkler } 4819ca9050bSTomas Winkler 4829ca9050bSTomas Winkler /** 48383ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 4849f81abdaSTomas Winkler * 4859f81abdaSTomas Winkler * @cl: host client 4869f81abdaSTomas Winkler * 4879f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 4889f81abdaSTomas Winkler * 4899f81abdaSTomas Winkler * returns 0 on success, <0 on failure. 4909f81abdaSTomas Winkler */ 4919f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file) 4929f81abdaSTomas Winkler { 4939f81abdaSTomas Winkler struct mei_device *dev; 4949f81abdaSTomas Winkler struct mei_cl_cb *cb; 4959f81abdaSTomas Winkler int rets; 4969f81abdaSTomas Winkler 4979f81abdaSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 4989f81abdaSTomas Winkler return -ENODEV; 4999f81abdaSTomas Winkler 5009f81abdaSTomas Winkler dev = cl->dev; 5019f81abdaSTomas Winkler 5029f81abdaSTomas Winkler cb = mei_io_cb_init(cl, file); 5039f81abdaSTomas Winkler if (!cb) { 5049f81abdaSTomas Winkler rets = -ENOMEM; 5059f81abdaSTomas Winkler goto out; 5069f81abdaSTomas Winkler } 5079f81abdaSTomas Winkler 50802a7eeccSTomas Winkler cb->fop_type = MEI_FOP_CONNECT; 5099f81abdaSTomas Winkler 510330dd7daSTomas Winkler if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) { 511330dd7daSTomas Winkler dev->hbuf_is_ready = false; 5129f81abdaSTomas Winkler 5139f81abdaSTomas Winkler if (mei_hbm_cl_connect_req(dev, cl)) { 5149f81abdaSTomas Winkler rets = -ENODEV; 5159f81abdaSTomas Winkler goto out; 5169f81abdaSTomas Winkler } 5179f81abdaSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 5189f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 5199f81abdaSTomas Winkler } else { 5209f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 5219f81abdaSTomas Winkler } 5229f81abdaSTomas Winkler 5239f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 5249f81abdaSTomas Winkler rets = wait_event_timeout(dev->wait_recvd_msg, 5259f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 5269f81abdaSTomas Winkler cl->state == MEI_FILE_DISCONNECTED), 527206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 5289f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 5299f81abdaSTomas Winkler 5309f81abdaSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) { 5319f81abdaSTomas Winkler rets = -EFAULT; 5329f81abdaSTomas Winkler 5339f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 5349f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 5359f81abdaSTomas Winkler goto out; 5369f81abdaSTomas Winkler } 5379f81abdaSTomas Winkler 5389f81abdaSTomas Winkler rets = cl->status; 5399f81abdaSTomas Winkler 5409f81abdaSTomas Winkler out: 5419f81abdaSTomas Winkler mei_io_cb_free(cb); 5429f81abdaSTomas Winkler return rets; 5439f81abdaSTomas Winkler } 5449f81abdaSTomas Winkler 5459f81abdaSTomas Winkler /** 54690e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 5479ca9050bSTomas Winkler * 5489ca9050bSTomas Winkler * @cl: private data of the file object 5499ca9050bSTomas Winkler * 5509ca9050bSTomas Winkler * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 5519ca9050bSTomas Winkler * -ENOENT if mei_cl is not present 5529ca9050bSTomas Winkler * -EINVAL if single_recv_buf == 0 5539ca9050bSTomas Winkler */ 55490e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 5559ca9050bSTomas Winkler { 55690e0b5f1STomas Winkler struct mei_device *dev; 5579ca9050bSTomas Winkler int i; 5589ca9050bSTomas Winkler 55990e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 56090e0b5f1STomas Winkler return -EINVAL; 56190e0b5f1STomas Winkler 56290e0b5f1STomas Winkler dev = cl->dev; 56390e0b5f1STomas Winkler 5649ca9050bSTomas Winkler if (!dev->me_clients_num) 5659ca9050bSTomas Winkler return 0; 5669ca9050bSTomas Winkler 5679ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 5689ca9050bSTomas Winkler return 1; 5699ca9050bSTomas Winkler 5709ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 5719ca9050bSTomas Winkler struct mei_me_client *me_cl = &dev->me_clients[i]; 5729ca9050bSTomas Winkler if (me_cl->client_id == cl->me_client_id) { 5739ca9050bSTomas Winkler if (me_cl->mei_flow_ctrl_creds) { 5749ca9050bSTomas Winkler if (WARN_ON(me_cl->props.single_recv_buf == 0)) 5759ca9050bSTomas Winkler return -EINVAL; 5769ca9050bSTomas Winkler return 1; 5779ca9050bSTomas Winkler } else { 5789ca9050bSTomas Winkler return 0; 5799ca9050bSTomas Winkler } 5809ca9050bSTomas Winkler } 5819ca9050bSTomas Winkler } 5829ca9050bSTomas Winkler return -ENOENT; 5839ca9050bSTomas Winkler } 5849ca9050bSTomas Winkler 5859ca9050bSTomas Winkler /** 58690e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 5879ca9050bSTomas Winkler * 5889ca9050bSTomas Winkler * @cl: private data of the file object 589393b148fSMasanari Iida * 5909ca9050bSTomas Winkler * @returns 5919ca9050bSTomas Winkler * 0 on success 5929ca9050bSTomas Winkler * -ENOENT when me client is not found 5939ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 5949ca9050bSTomas Winkler */ 59590e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 5969ca9050bSTomas Winkler { 59790e0b5f1STomas Winkler struct mei_device *dev; 5989ca9050bSTomas Winkler int i; 5999ca9050bSTomas Winkler 60090e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 60190e0b5f1STomas Winkler return -EINVAL; 60290e0b5f1STomas Winkler 60390e0b5f1STomas Winkler dev = cl->dev; 60490e0b5f1STomas Winkler 6059ca9050bSTomas Winkler if (!dev->me_clients_num) 6069ca9050bSTomas Winkler return -ENOENT; 6079ca9050bSTomas Winkler 6089ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 6099ca9050bSTomas Winkler struct mei_me_client *me_cl = &dev->me_clients[i]; 6109ca9050bSTomas Winkler if (me_cl->client_id == cl->me_client_id) { 6119ca9050bSTomas Winkler if (me_cl->props.single_recv_buf != 0) { 6129ca9050bSTomas Winkler if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) 6139ca9050bSTomas Winkler return -EINVAL; 6149ca9050bSTomas Winkler dev->me_clients[i].mei_flow_ctrl_creds--; 6159ca9050bSTomas Winkler } else { 6169ca9050bSTomas Winkler if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 6179ca9050bSTomas Winkler return -EINVAL; 6189ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 6199ca9050bSTomas Winkler } 6209ca9050bSTomas Winkler return 0; 6219ca9050bSTomas Winkler } 6229ca9050bSTomas Winkler } 6239ca9050bSTomas Winkler return -ENOENT; 6249ca9050bSTomas Winkler } 6259ca9050bSTomas Winkler 6269ca9050bSTomas Winkler /** 627393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 6289ca9050bSTomas Winkler * 62990e0b5f1STomas Winkler * @cl: host client 6309ca9050bSTomas Winkler * 6319ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 6329ca9050bSTomas Winkler */ 633fcb136e1STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length) 6349ca9050bSTomas Winkler { 63590e0b5f1STomas Winkler struct mei_device *dev; 6369ca9050bSTomas Winkler struct mei_cl_cb *cb; 6379ca9050bSTomas Winkler int rets; 6389ca9050bSTomas Winkler int i; 6399ca9050bSTomas Winkler 64090e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 64190e0b5f1STomas Winkler return -ENODEV; 64290e0b5f1STomas Winkler 64390e0b5f1STomas Winkler dev = cl->dev; 64490e0b5f1STomas Winkler 645b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 6469ca9050bSTomas Winkler return -ENODEV; 6479ca9050bSTomas Winkler 648d91aaed3STomas Winkler if (cl->read_cb) { 649c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "read is pending.\n"); 6509ca9050bSTomas Winkler return -EBUSY; 6519ca9050bSTomas Winkler } 6529ca9050bSTomas Winkler i = mei_me_cl_by_id(dev, cl->me_client_id); 6539ca9050bSTomas Winkler if (i < 0) { 654c0abffbdSAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 6559ca9050bSTomas Winkler return -ENODEV; 6569ca9050bSTomas Winkler } 6579ca9050bSTomas Winkler 6589ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 6599ca9050bSTomas Winkler if (!cb) 6609ca9050bSTomas Winkler return -ENOMEM; 6619ca9050bSTomas Winkler 662fcb136e1STomas Winkler /* always allocate at least client max message */ 663fcb136e1STomas Winkler length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); 664fcb136e1STomas Winkler rets = mei_io_cb_alloc_resp_buf(cb, length); 6659ca9050bSTomas Winkler if (rets) 6669ca9050bSTomas Winkler goto err; 6679ca9050bSTomas Winkler 6689ca9050bSTomas Winkler cb->fop_type = MEI_FOP_READ; 6699ca9050bSTomas Winkler cl->read_cb = cb; 670330dd7daSTomas Winkler if (dev->hbuf_is_ready) { 671330dd7daSTomas Winkler dev->hbuf_is_ready = false; 6729ca9050bSTomas Winkler if (mei_hbm_cl_flow_control_req(dev, cl)) { 673c0abffbdSAlexander Usyskin cl_err(dev, cl, "flow control send failed\n"); 6749ca9050bSTomas Winkler rets = -ENODEV; 6759ca9050bSTomas Winkler goto err; 6769ca9050bSTomas Winkler } 6779ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->read_list.list); 6789ca9050bSTomas Winkler } else { 6799ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 6809ca9050bSTomas Winkler } 6819ca9050bSTomas Winkler return rets; 6829ca9050bSTomas Winkler err: 6839ca9050bSTomas Winkler mei_io_cb_free(cb); 6849ca9050bSTomas Winkler return rets; 6859ca9050bSTomas Winkler } 6869ca9050bSTomas Winkler 687074b4c01STomas Winkler /** 68821767546STomas Winkler * mei_cl_irq_write_complete - write a message to device 68921767546STomas Winkler * from the interrupt thread context 69021767546STomas Winkler * 69121767546STomas Winkler * @cl: client 69221767546STomas Winkler * @cb: callback block. 69321767546STomas Winkler * @slots: free slots. 69421767546STomas Winkler * @cmpl_list: complete list. 69521767546STomas Winkler * 69621767546STomas Winkler * returns 0, OK; otherwise error. 69721767546STomas Winkler */ 69821767546STomas Winkler int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, 69921767546STomas Winkler s32 *slots, struct mei_cl_cb *cmpl_list) 70021767546STomas Winkler { 701136698e5STomas Winkler struct mei_device *dev; 702136698e5STomas Winkler struct mei_msg_data *buf; 70321767546STomas Winkler struct mei_msg_hdr mei_hdr; 704136698e5STomas Winkler size_t len; 705136698e5STomas Winkler u32 msg_slots; 7062ebf8c94STomas Winkler int rets; 70721767546STomas Winkler 708136698e5STomas Winkler 709136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 710136698e5STomas Winkler return -ENODEV; 711136698e5STomas Winkler 712136698e5STomas Winkler dev = cl->dev; 713136698e5STomas Winkler 714136698e5STomas Winkler buf = &cb->request_buffer; 715136698e5STomas Winkler 716136698e5STomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 717136698e5STomas Winkler if (rets < 0) 718136698e5STomas Winkler return rets; 719136698e5STomas Winkler 720136698e5STomas Winkler if (rets == 0) { 721136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 722136698e5STomas Winkler return 0; 723136698e5STomas Winkler } 724136698e5STomas Winkler 725136698e5STomas Winkler len = buf->size - cb->buf_idx; 726136698e5STomas Winkler msg_slots = mei_data2slots(len); 727136698e5STomas Winkler 72821767546STomas Winkler mei_hdr.host_addr = cl->host_client_id; 72921767546STomas Winkler mei_hdr.me_addr = cl->me_client_id; 73021767546STomas Winkler mei_hdr.reserved = 0; 731479327fcSTomas Winkler mei_hdr.internal = cb->internal; 73221767546STomas Winkler 73321767546STomas Winkler if (*slots >= msg_slots) { 73421767546STomas Winkler mei_hdr.length = len; 73521767546STomas Winkler mei_hdr.msg_complete = 1; 73621767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 73721767546STomas Winkler } else if (*slots == dev->hbuf_depth) { 73821767546STomas Winkler msg_slots = *slots; 73921767546STomas Winkler len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 74021767546STomas Winkler mei_hdr.length = len; 74121767546STomas Winkler mei_hdr.msg_complete = 0; 74221767546STomas Winkler } else { 74321767546STomas Winkler /* wait for next time the host buffer is empty */ 74421767546STomas Winkler return 0; 74521767546STomas Winkler } 74621767546STomas Winkler 747c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", 74821767546STomas Winkler cb->request_buffer.size, cb->buf_idx); 74921767546STomas Winkler 75021767546STomas Winkler *slots -= msg_slots; 751136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 7522ebf8c94STomas Winkler if (rets) { 7532ebf8c94STomas Winkler cl->status = rets; 75421767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 7552ebf8c94STomas Winkler return rets; 75621767546STomas Winkler } 75721767546STomas Winkler 75821767546STomas Winkler cl->status = 0; 7594dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 76021767546STomas Winkler cb->buf_idx += mei_hdr.length; 7614dfaa9f7STomas Winkler 76221767546STomas Winkler if (mei_hdr.msg_complete) { 76321767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 7642ebf8c94STomas Winkler return -EIO; 76521767546STomas Winkler list_move_tail(&cb->list, &dev->write_waiting_list.list); 76621767546STomas Winkler } 76721767546STomas Winkler 76821767546STomas Winkler return 0; 76921767546STomas Winkler } 77021767546STomas Winkler 77121767546STomas Winkler /** 7724234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 7734234a6deSTomas Winkler assumes device_lock is locked 7744234a6deSTomas Winkler * 7754234a6deSTomas Winkler * @cl: host client 7764234a6deSTomas Winkler * @cl: write callback with filled data 7774234a6deSTomas Winkler * 77883ce0741SAlexander Usyskin * returns number of bytes sent on success, <0 on failure. 7794234a6deSTomas Winkler */ 7804234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 7814234a6deSTomas Winkler { 7824234a6deSTomas Winkler struct mei_device *dev; 7834234a6deSTomas Winkler struct mei_msg_data *buf; 7844234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 7854234a6deSTomas Winkler int rets; 7864234a6deSTomas Winkler 7874234a6deSTomas Winkler 7884234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 7894234a6deSTomas Winkler return -ENODEV; 7904234a6deSTomas Winkler 7914234a6deSTomas Winkler if (WARN_ON(!cb)) 7924234a6deSTomas Winkler return -EINVAL; 7934234a6deSTomas Winkler 7944234a6deSTomas Winkler dev = cl->dev; 7954234a6deSTomas Winkler 7964234a6deSTomas Winkler 7974234a6deSTomas Winkler buf = &cb->request_buffer; 7984234a6deSTomas Winkler 799c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); 8004234a6deSTomas Winkler 8014234a6deSTomas Winkler 8024234a6deSTomas Winkler cb->fop_type = MEI_FOP_WRITE; 8034234a6deSTomas Winkler 8044234a6deSTomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 8054234a6deSTomas Winkler if (rets < 0) 8064234a6deSTomas Winkler goto err; 8074234a6deSTomas Winkler 8084234a6deSTomas Winkler /* Host buffer is not ready, we queue the request */ 8094234a6deSTomas Winkler if (rets == 0 || !dev->hbuf_is_ready) { 8104234a6deSTomas Winkler cb->buf_idx = 0; 8114234a6deSTomas Winkler /* unseting complete will enqueue the cb for write */ 8124234a6deSTomas Winkler mei_hdr.msg_complete = 0; 8134234a6deSTomas Winkler rets = buf->size; 8144234a6deSTomas Winkler goto out; 8154234a6deSTomas Winkler } 8164234a6deSTomas Winkler 8174234a6deSTomas Winkler dev->hbuf_is_ready = false; 8184234a6deSTomas Winkler 8194234a6deSTomas Winkler /* Check for a maximum length */ 8204234a6deSTomas Winkler if (buf->size > mei_hbuf_max_len(dev)) { 8214234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 8224234a6deSTomas Winkler mei_hdr.msg_complete = 0; 8234234a6deSTomas Winkler } else { 8244234a6deSTomas Winkler mei_hdr.length = buf->size; 8254234a6deSTomas Winkler mei_hdr.msg_complete = 1; 8264234a6deSTomas Winkler } 8274234a6deSTomas Winkler 8284234a6deSTomas Winkler mei_hdr.host_addr = cl->host_client_id; 8294234a6deSTomas Winkler mei_hdr.me_addr = cl->me_client_id; 8304234a6deSTomas Winkler mei_hdr.reserved = 0; 831479327fcSTomas Winkler mei_hdr.internal = cb->internal; 8324234a6deSTomas Winkler 8334234a6deSTomas Winkler 8342ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 8352ebf8c94STomas Winkler if (rets) 8364234a6deSTomas Winkler goto err; 8374234a6deSTomas Winkler 8384234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 8394234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 8404234a6deSTomas Winkler 8414234a6deSTomas Winkler rets = buf->size; 8424234a6deSTomas Winkler out: 8434234a6deSTomas Winkler if (mei_hdr.msg_complete) { 8444234a6deSTomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) { 8454234a6deSTomas Winkler rets = -ENODEV; 8464234a6deSTomas Winkler goto err; 8474234a6deSTomas Winkler } 8484234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 8494234a6deSTomas Winkler } else { 8504234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 8514234a6deSTomas Winkler } 8524234a6deSTomas Winkler 8534234a6deSTomas Winkler 8544234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 8554234a6deSTomas Winkler 8564234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 8574234a6deSTomas Winkler if (wait_event_interruptible(cl->tx_wait, 8584234a6deSTomas Winkler cl->writing_state == MEI_WRITE_COMPLETE)) { 8594234a6deSTomas Winkler if (signal_pending(current)) 8604234a6deSTomas Winkler rets = -EINTR; 8614234a6deSTomas Winkler else 8624234a6deSTomas Winkler rets = -ERESTARTSYS; 8634234a6deSTomas Winkler } 8644234a6deSTomas Winkler mutex_lock(&dev->device_lock); 8654234a6deSTomas Winkler } 8664234a6deSTomas Winkler err: 8674234a6deSTomas Winkler return rets; 8684234a6deSTomas Winkler } 8694234a6deSTomas Winkler 8704234a6deSTomas Winkler 871db086fa9STomas Winkler /** 872db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 873db086fa9STomas Winkler * 874db086fa9STomas Winkler * @cl: private data of the file object. 875db086fa9STomas Winkler * @cb: callback block. 876db086fa9STomas Winkler */ 877db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 878db086fa9STomas Winkler { 879db086fa9STomas Winkler if (cb->fop_type == MEI_FOP_WRITE) { 880db086fa9STomas Winkler mei_io_cb_free(cb); 881db086fa9STomas Winkler cb = NULL; 882db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 883db086fa9STomas Winkler if (waitqueue_active(&cl->tx_wait)) 884db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 885db086fa9STomas Winkler 886db086fa9STomas Winkler } else if (cb->fop_type == MEI_FOP_READ && 887db086fa9STomas Winkler MEI_READING == cl->reading_state) { 888db086fa9STomas Winkler cl->reading_state = MEI_READ_COMPLETE; 889db086fa9STomas Winkler if (waitqueue_active(&cl->rx_wait)) 890db086fa9STomas Winkler wake_up_interruptible(&cl->rx_wait); 891db086fa9STomas Winkler else 892db086fa9STomas Winkler mei_cl_bus_rx_event(cl); 893db086fa9STomas Winkler 894db086fa9STomas Winkler } 895db086fa9STomas Winkler } 896db086fa9STomas Winkler 8974234a6deSTomas Winkler 8984234a6deSTomas Winkler /** 899074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 900074b4c01STomas Winkler * 901074b4c01STomas Winkler * @dev - mei device 902074b4c01STomas Winkler */ 903074b4c01STomas Winkler 904074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 905074b4c01STomas Winkler { 906074b4c01STomas Winkler struct mei_cl *cl, *next; 907074b4c01STomas Winkler 908074b4c01STomas Winkler list_for_each_entry_safe(cl, next, &dev->file_list, link) { 909074b4c01STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 910074b4c01STomas Winkler cl->mei_flow_ctrl_creds = 0; 911074b4c01STomas Winkler cl->read_cb = NULL; 912074b4c01STomas Winkler cl->timer_count = 0; 913074b4c01STomas Winkler } 914074b4c01STomas Winkler } 915074b4c01STomas Winkler 916074b4c01STomas Winkler 917074b4c01STomas Winkler /** 9185290801cSTomas Winkler * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted 919074b4c01STomas Winkler * 920074b4c01STomas Winkler * @dev - mei device 921074b4c01STomas Winkler */ 9225290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev) 923074b4c01STomas Winkler { 924074b4c01STomas Winkler struct mei_cl *cl, *next; 925074b4c01STomas Winkler list_for_each_entry_safe(cl, next, &dev->file_list, link) { 926074b4c01STomas Winkler if (waitqueue_active(&cl->rx_wait)) { 927c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 928074b4c01STomas Winkler wake_up_interruptible(&cl->rx_wait); 929074b4c01STomas Winkler } 9305290801cSTomas Winkler if (waitqueue_active(&cl->tx_wait)) { 931c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 9325290801cSTomas Winkler wake_up_interruptible(&cl->tx_wait); 9335290801cSTomas Winkler } 934074b4c01STomas Winkler } 935074b4c01STomas Winkler } 936074b4c01STomas Winkler 937074b4c01STomas Winkler /** 938074b4c01STomas Winkler * mei_cl_all_write_clear - clear all pending writes 939074b4c01STomas Winkler 940074b4c01STomas Winkler * @dev - mei device 941074b4c01STomas Winkler */ 942074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev) 943074b4c01STomas Winkler { 944074b4c01STomas Winkler struct mei_cl_cb *cb, *next; 945074b4c01STomas Winkler 946074b4c01STomas Winkler list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { 947074b4c01STomas Winkler list_del(&cb->list); 948074b4c01STomas Winkler mei_io_cb_free(cb); 949074b4c01STomas Winkler } 950074b4c01STomas Winkler } 951074b4c01STomas Winkler 952074b4c01STomas Winkler 953