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 1129ca9050bSTomas Winkler * @file: 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 * 1359ca9050bSTomas Winkler * @cb - io callback structure 1369ca9050bSTomas Winkler * @size: 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 /** 1579ca9050bSTomas Winkler * mei_io_cb_alloc_req_buf - allocate respose buffer 1589ca9050bSTomas Winkler * 1599ca9050bSTomas Winkler * @cb - io callback structure 1609ca9050bSTomas Winkler * @size: 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 * @dev: the device structure 1879ca9050bSTomas Winkler * @cl: host client 1889ca9050bSTomas Winkler */ 1899ca9050bSTomas Winkler int mei_cl_flush_queues(struct mei_cl *cl) 1909ca9050bSTomas Winkler { 19190e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 1929ca9050bSTomas Winkler return -EINVAL; 1939ca9050bSTomas Winkler 1949ca9050bSTomas Winkler dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); 1959ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->read_list, cl); 1969ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->write_list, cl); 1979ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->write_waiting_list, cl); 1989ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 1999ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 2009ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 2019ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); 2029ca9050bSTomas Winkler return 0; 2039ca9050bSTomas Winkler } 2049ca9050bSTomas Winkler 2059ca9050bSTomas Winkler 2069ca9050bSTomas Winkler /** 2079ca9050bSTomas Winkler * mei_cl_init - initializes intialize cl. 2089ca9050bSTomas Winkler * 2099ca9050bSTomas Winkler * @cl: host client to be initialized 2109ca9050bSTomas Winkler * @dev: mei device 2119ca9050bSTomas Winkler */ 2129ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 2139ca9050bSTomas Winkler { 2149ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 2159ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 2169ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 2179ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 2189ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 2199ca9050bSTomas Winkler cl->reading_state = MEI_IDLE; 2209ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 2219ca9050bSTomas Winkler cl->dev = dev; 2229ca9050bSTomas Winkler } 2239ca9050bSTomas Winkler 2249ca9050bSTomas Winkler /** 2259ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 2269ca9050bSTomas Winkler * 2279ca9050bSTomas Winkler * @dev: mei device 2289ca9050bSTomas Winkler * returns The allocated file or NULL on failure 2299ca9050bSTomas Winkler */ 2309ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 2319ca9050bSTomas Winkler { 2329ca9050bSTomas Winkler struct mei_cl *cl; 2339ca9050bSTomas Winkler 2349ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 2359ca9050bSTomas Winkler if (!cl) 2369ca9050bSTomas Winkler return NULL; 2379ca9050bSTomas Winkler 2389ca9050bSTomas Winkler mei_cl_init(cl, dev); 2399ca9050bSTomas Winkler 2409ca9050bSTomas Winkler return cl; 2419ca9050bSTomas Winkler } 2429ca9050bSTomas Winkler 24390e0b5f1STomas Winkler /** 24490e0b5f1STomas Winkler * mei_cl_find_read_cb - find this cl's callback in the read list 24590e0b5f1STomas Winkler * 24690e0b5f1STomas Winkler * @dev: device structure 24790e0b5f1STomas Winkler * returns cb on success, NULL on error 24890e0b5f1STomas Winkler */ 24990e0b5f1STomas Winkler struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) 25090e0b5f1STomas Winkler { 25190e0b5f1STomas Winkler struct mei_device *dev = cl->dev; 25290e0b5f1STomas Winkler struct mei_cl_cb *cb = NULL; 25390e0b5f1STomas Winkler struct mei_cl_cb *next = NULL; 25490e0b5f1STomas Winkler 25590e0b5f1STomas Winkler list_for_each_entry_safe(cb, next, &dev->read_list.list, list) 25690e0b5f1STomas Winkler if (mei_cl_cmp_id(cl, cb->cl)) 25790e0b5f1STomas Winkler return cb; 25890e0b5f1STomas Winkler return NULL; 25990e0b5f1STomas Winkler } 26090e0b5f1STomas Winkler 261781d0d89STomas Winkler /** mei_cl_link: allocte host id in the host map 2629ca9050bSTomas Winkler * 263781d0d89STomas Winkler * @cl - host client 264781d0d89STomas Winkler * @id - fixed host id or -1 for genereting one 265781d0d89STomas Winkler * returns 0 on success 2669ca9050bSTomas Winkler * -EINVAL on incorrect values 2679ca9050bSTomas Winkler * -ENONET if client not found 2689ca9050bSTomas Winkler */ 269781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 2709ca9050bSTomas Winkler { 27190e0b5f1STomas Winkler struct mei_device *dev; 2729ca9050bSTomas Winkler 273781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 2749ca9050bSTomas Winkler return -EINVAL; 2759ca9050bSTomas Winkler 27690e0b5f1STomas Winkler dev = cl->dev; 27790e0b5f1STomas Winkler 278781d0d89STomas Winkler /* If Id is not asigned get one*/ 279781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 280781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 281781d0d89STomas Winkler MEI_CLIENTS_MAX); 2829ca9050bSTomas Winkler 283781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 284781d0d89STomas Winkler dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ; 2859ca9050bSTomas Winkler return -ENOENT; 2869ca9050bSTomas Winkler } 287781d0d89STomas Winkler 288781d0d89STomas Winkler dev->open_handle_count++; 289781d0d89STomas Winkler 290781d0d89STomas Winkler cl->host_client_id = id; 291781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 292781d0d89STomas Winkler 293781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 294781d0d89STomas Winkler 295781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 296781d0d89STomas Winkler 297781d0d89STomas Winkler dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id); 298781d0d89STomas Winkler return 0; 299781d0d89STomas Winkler } 300781d0d89STomas Winkler 3019ca9050bSTomas Winkler /** 30290e0b5f1STomas Winkler * mei_cl_unlink - remove me_cl from the list 3039ca9050bSTomas Winkler * 3049ca9050bSTomas Winkler * @dev: the device structure 3059ca9050bSTomas Winkler */ 30690e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 3079ca9050bSTomas Winkler { 30890e0b5f1STomas Winkler struct mei_device *dev; 3099ca9050bSTomas Winkler struct mei_cl *pos, *next; 31090e0b5f1STomas Winkler 311781d0d89STomas Winkler /* don't shout on error exit path */ 312781d0d89STomas Winkler if (!cl) 313781d0d89STomas Winkler return 0; 314781d0d89STomas Winkler 315781d0d89STomas Winkler if (WARN_ON(!cl->dev)) 31690e0b5f1STomas Winkler return -EINVAL; 31790e0b5f1STomas Winkler 31890e0b5f1STomas Winkler dev = cl->dev; 31990e0b5f1STomas Winkler 3209ca9050bSTomas Winkler list_for_each_entry_safe(pos, next, &dev->file_list, link) { 3219ca9050bSTomas Winkler if (cl->host_client_id == pos->host_client_id) { 3229ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", 3239ca9050bSTomas Winkler pos->host_client_id, pos->me_client_id); 3249ca9050bSTomas Winkler list_del_init(&pos->link); 3259ca9050bSTomas Winkler break; 3269ca9050bSTomas Winkler } 3279ca9050bSTomas Winkler } 32890e0b5f1STomas Winkler return 0; 3299ca9050bSTomas Winkler } 3309ca9050bSTomas Winkler 3319ca9050bSTomas Winkler 3329ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 3339ca9050bSTomas Winkler { 3349ca9050bSTomas Winkler struct mei_device *dev = container_of(work, 3359ca9050bSTomas Winkler struct mei_device, init_work); 3369ca9050bSTomas Winkler struct mei_client_properties *client_props; 3379ca9050bSTomas Winkler int i; 3389ca9050bSTomas Winkler 3399ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 3409ca9050bSTomas Winkler 3419ca9050bSTomas Winkler bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); 3429ca9050bSTomas Winkler dev->open_handle_count = 0; 3439ca9050bSTomas Winkler 3449ca9050bSTomas Winkler /* 3459ca9050bSTomas Winkler * Reserving the first three client IDs 3469ca9050bSTomas Winkler * 0: Reserved for MEI Bus Message communications 3479ca9050bSTomas Winkler * 1: Reserved for Watchdog 3489ca9050bSTomas Winkler * 2: Reserved for AMTHI 3499ca9050bSTomas Winkler */ 3509ca9050bSTomas Winkler bitmap_set(dev->host_clients_map, 0, 3); 3519ca9050bSTomas Winkler 3529ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 3539ca9050bSTomas Winkler client_props = &dev->me_clients[i].props; 3549ca9050bSTomas Winkler 3551a1aca42STomas Winkler if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) 3569ca9050bSTomas Winkler mei_amthif_host_init(dev); 3579ca9050bSTomas Winkler else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) 3589ca9050bSTomas Winkler mei_wd_host_init(dev); 3599ca9050bSTomas Winkler } 3609ca9050bSTomas Winkler 3619ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 3629ca9050bSTomas Winkler 3639ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 3649ca9050bSTomas Winkler } 3659ca9050bSTomas Winkler 3669ca9050bSTomas Winkler 3679ca9050bSTomas Winkler /** 36890e0b5f1STomas Winkler * mei_cl_disconnect - disconnect host clinet form the me one 3699ca9050bSTomas Winkler * 37090e0b5f1STomas Winkler * @cl: host client 3719ca9050bSTomas Winkler * 3729ca9050bSTomas Winkler * Locking: called under "dev->device_lock" lock 3739ca9050bSTomas Winkler * 3749ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 3759ca9050bSTomas Winkler */ 37690e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl) 3779ca9050bSTomas Winkler { 37890e0b5f1STomas Winkler struct mei_device *dev; 3799ca9050bSTomas Winkler struct mei_cl_cb *cb; 3809ca9050bSTomas Winkler int rets, err; 3819ca9050bSTomas Winkler 38290e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 3839ca9050bSTomas Winkler return -ENODEV; 3849ca9050bSTomas Winkler 38590e0b5f1STomas Winkler dev = cl->dev; 38690e0b5f1STomas Winkler 3879ca9050bSTomas Winkler if (cl->state != MEI_FILE_DISCONNECTING) 3889ca9050bSTomas Winkler return 0; 3899ca9050bSTomas Winkler 3909ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 3919ca9050bSTomas Winkler if (!cb) 3929ca9050bSTomas Winkler return -ENOMEM; 3939ca9050bSTomas Winkler 3949ca9050bSTomas Winkler cb->fop_type = MEI_FOP_CLOSE; 3959ca9050bSTomas Winkler if (dev->mei_host_buffer_is_empty) { 3969ca9050bSTomas Winkler dev->mei_host_buffer_is_empty = false; 3979ca9050bSTomas Winkler if (mei_hbm_cl_disconnect_req(dev, cl)) { 3989ca9050bSTomas Winkler rets = -ENODEV; 3999ca9050bSTomas Winkler dev_err(&dev->pdev->dev, "failed to disconnect.\n"); 4009ca9050bSTomas Winkler goto free; 4019ca9050bSTomas Winkler } 4029ca9050bSTomas Winkler mdelay(10); /* Wait for hardware disconnection ready */ 4039ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 4049ca9050bSTomas Winkler } else { 4059ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); 4069ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 4079ca9050bSTomas Winkler 4089ca9050bSTomas Winkler } 4099ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 4109ca9050bSTomas Winkler 4119ca9050bSTomas Winkler err = wait_event_timeout(dev->wait_recvd_msg, 4129ca9050bSTomas Winkler MEI_FILE_DISCONNECTED == cl->state, 4139ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 4149ca9050bSTomas Winkler 4159ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 4169ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED == cl->state) { 4179ca9050bSTomas Winkler rets = 0; 4189ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); 4199ca9050bSTomas Winkler } else { 4209ca9050bSTomas Winkler rets = -ENODEV; 4219ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED != cl->state) 4229ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); 4239ca9050bSTomas Winkler 4249ca9050bSTomas Winkler if (err) 4259ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, 4269ca9050bSTomas Winkler "wait failed disconnect err=%08x\n", 4279ca9050bSTomas Winkler err); 4289ca9050bSTomas Winkler 4299ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); 4309ca9050bSTomas Winkler } 4319ca9050bSTomas Winkler 4329ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 4339ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 4349ca9050bSTomas Winkler free: 4359ca9050bSTomas Winkler mei_io_cb_free(cb); 4369ca9050bSTomas Winkler return rets; 4379ca9050bSTomas Winkler } 4389ca9050bSTomas Winkler 4399ca9050bSTomas Winkler 4409ca9050bSTomas Winkler /** 44190e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 44290e0b5f1STomas Winkler * client with the same me client id is connecting 4439ca9050bSTomas Winkler * 4449ca9050bSTomas Winkler * @cl: private data of the file object 4459ca9050bSTomas Winkler * 44690e0b5f1STomas Winkler * returns ture if other client is connected, 0 - otherwise. 4479ca9050bSTomas Winkler */ 44890e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl) 4499ca9050bSTomas Winkler { 45090e0b5f1STomas Winkler struct mei_device *dev; 45190e0b5f1STomas Winkler struct mei_cl *pos; 45290e0b5f1STomas Winkler struct mei_cl *next; 4539ca9050bSTomas Winkler 45490e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 45590e0b5f1STomas Winkler return false; 45690e0b5f1STomas Winkler 45790e0b5f1STomas Winkler dev = cl->dev; 45890e0b5f1STomas Winkler 45990e0b5f1STomas Winkler list_for_each_entry_safe(pos, next, &dev->file_list, link) { 46090e0b5f1STomas Winkler if ((pos->state == MEI_FILE_CONNECTING) && 46190e0b5f1STomas Winkler (pos != cl) && cl->me_client_id == pos->me_client_id) 46290e0b5f1STomas Winkler return true; 4639ca9050bSTomas Winkler 4649ca9050bSTomas Winkler } 46590e0b5f1STomas Winkler 46690e0b5f1STomas Winkler return false; 4679ca9050bSTomas Winkler } 4689ca9050bSTomas Winkler 4699ca9050bSTomas Winkler /** 4709f81abdaSTomas Winkler * mei_cl_connect - connect host clinet to the me one 4719f81abdaSTomas Winkler * 4729f81abdaSTomas Winkler * @cl: host client 4739f81abdaSTomas Winkler * 4749f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 4759f81abdaSTomas Winkler * 4769f81abdaSTomas Winkler * returns 0 on success, <0 on failure. 4779f81abdaSTomas Winkler */ 4789f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file) 4799f81abdaSTomas Winkler { 4809f81abdaSTomas Winkler struct mei_device *dev; 4819f81abdaSTomas Winkler struct mei_cl_cb *cb; 4829f81abdaSTomas Winkler long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT); 4839f81abdaSTomas Winkler int rets; 4849f81abdaSTomas Winkler 4859f81abdaSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 4869f81abdaSTomas Winkler return -ENODEV; 4879f81abdaSTomas Winkler 4889f81abdaSTomas Winkler dev = cl->dev; 4899f81abdaSTomas Winkler 4909f81abdaSTomas Winkler cb = mei_io_cb_init(cl, file); 4919f81abdaSTomas Winkler if (!cb) { 4929f81abdaSTomas Winkler rets = -ENOMEM; 4939f81abdaSTomas Winkler goto out; 4949f81abdaSTomas Winkler } 4959f81abdaSTomas Winkler 4969f81abdaSTomas Winkler cb->fop_type = MEI_FOP_IOCTL; 4979f81abdaSTomas Winkler 4989f81abdaSTomas Winkler if (dev->mei_host_buffer_is_empty && 4999f81abdaSTomas Winkler !mei_cl_is_other_connecting(cl)) { 5009f81abdaSTomas Winkler dev->mei_host_buffer_is_empty = false; 5019f81abdaSTomas Winkler 5029f81abdaSTomas Winkler if (mei_hbm_cl_connect_req(dev, cl)) { 5039f81abdaSTomas Winkler rets = -ENODEV; 5049f81abdaSTomas Winkler goto out; 5059f81abdaSTomas Winkler } 5069f81abdaSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 5079f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 5089f81abdaSTomas Winkler } else { 5099f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 5109f81abdaSTomas Winkler } 5119f81abdaSTomas Winkler 5129f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 5139f81abdaSTomas Winkler rets = wait_event_timeout(dev->wait_recvd_msg, 5149f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 5159f81abdaSTomas Winkler cl->state == MEI_FILE_DISCONNECTED), 5169f81abdaSTomas Winkler timeout * HZ); 5179f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 5189f81abdaSTomas Winkler 5199f81abdaSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) { 5209f81abdaSTomas Winkler rets = -EFAULT; 5219f81abdaSTomas Winkler 5229f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 5239f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 5249f81abdaSTomas Winkler goto out; 5259f81abdaSTomas Winkler } 5269f81abdaSTomas Winkler 5279f81abdaSTomas Winkler rets = cl->status; 5289f81abdaSTomas Winkler 5299f81abdaSTomas Winkler out: 5309f81abdaSTomas Winkler mei_io_cb_free(cb); 5319f81abdaSTomas Winkler return rets; 5329f81abdaSTomas Winkler } 5339f81abdaSTomas Winkler 5349f81abdaSTomas Winkler /** 53590e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 5369ca9050bSTomas Winkler * 5379ca9050bSTomas Winkler * @dev: the device structure 5389ca9050bSTomas Winkler * @cl: private data of the file object 5399ca9050bSTomas Winkler * 5409ca9050bSTomas Winkler * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 5419ca9050bSTomas Winkler * -ENOENT if mei_cl is not present 5429ca9050bSTomas Winkler * -EINVAL if single_recv_buf == 0 5439ca9050bSTomas Winkler */ 54490e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 5459ca9050bSTomas Winkler { 54690e0b5f1STomas Winkler struct mei_device *dev; 5479ca9050bSTomas Winkler int i; 5489ca9050bSTomas Winkler 54990e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 55090e0b5f1STomas Winkler return -EINVAL; 55190e0b5f1STomas Winkler 55290e0b5f1STomas Winkler dev = cl->dev; 55390e0b5f1STomas Winkler 5549ca9050bSTomas Winkler if (!dev->me_clients_num) 5559ca9050bSTomas Winkler return 0; 5569ca9050bSTomas Winkler 5579ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 5589ca9050bSTomas Winkler return 1; 5599ca9050bSTomas Winkler 5609ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 5619ca9050bSTomas Winkler struct mei_me_client *me_cl = &dev->me_clients[i]; 5629ca9050bSTomas Winkler if (me_cl->client_id == cl->me_client_id) { 5639ca9050bSTomas Winkler if (me_cl->mei_flow_ctrl_creds) { 5649ca9050bSTomas Winkler if (WARN_ON(me_cl->props.single_recv_buf == 0)) 5659ca9050bSTomas Winkler return -EINVAL; 5669ca9050bSTomas Winkler return 1; 5679ca9050bSTomas Winkler } else { 5689ca9050bSTomas Winkler return 0; 5699ca9050bSTomas Winkler } 5709ca9050bSTomas Winkler } 5719ca9050bSTomas Winkler } 5729ca9050bSTomas Winkler return -ENOENT; 5739ca9050bSTomas Winkler } 5749ca9050bSTomas Winkler 5759ca9050bSTomas Winkler /** 57690e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 5779ca9050bSTomas Winkler * 5789ca9050bSTomas Winkler * @dev: the device structure 5799ca9050bSTomas Winkler * @cl: private data of the file object 5809ca9050bSTomas Winkler * @returns 5819ca9050bSTomas Winkler * 0 on success 5829ca9050bSTomas Winkler * -ENOENT when me client is not found 5839ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 5849ca9050bSTomas Winkler */ 58590e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 5869ca9050bSTomas Winkler { 58790e0b5f1STomas Winkler struct mei_device *dev; 5889ca9050bSTomas Winkler int i; 5899ca9050bSTomas Winkler 59090e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 59190e0b5f1STomas Winkler return -EINVAL; 59290e0b5f1STomas Winkler 59390e0b5f1STomas Winkler dev = cl->dev; 59490e0b5f1STomas Winkler 5959ca9050bSTomas Winkler if (!dev->me_clients_num) 5969ca9050bSTomas Winkler return -ENOENT; 5979ca9050bSTomas Winkler 5989ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 5999ca9050bSTomas Winkler struct mei_me_client *me_cl = &dev->me_clients[i]; 6009ca9050bSTomas Winkler if (me_cl->client_id == cl->me_client_id) { 6019ca9050bSTomas Winkler if (me_cl->props.single_recv_buf != 0) { 6029ca9050bSTomas Winkler if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) 6039ca9050bSTomas Winkler return -EINVAL; 6049ca9050bSTomas Winkler dev->me_clients[i].mei_flow_ctrl_creds--; 6059ca9050bSTomas Winkler } else { 6069ca9050bSTomas Winkler if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 6079ca9050bSTomas Winkler return -EINVAL; 6089ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 6099ca9050bSTomas Winkler } 6109ca9050bSTomas Winkler return 0; 6119ca9050bSTomas Winkler } 6129ca9050bSTomas Winkler } 6139ca9050bSTomas Winkler return -ENOENT; 6149ca9050bSTomas Winkler } 6159ca9050bSTomas Winkler 6169ca9050bSTomas Winkler /** 61790e0b5f1STomas Winkler * mei_cl_start_read - the start read client message function. 6189ca9050bSTomas Winkler * 61990e0b5f1STomas Winkler * @cl: host client 6209ca9050bSTomas Winkler * 6219ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 6229ca9050bSTomas Winkler */ 62390e0b5f1STomas Winkler int mei_cl_read_start(struct mei_cl *cl) 6249ca9050bSTomas Winkler { 62590e0b5f1STomas Winkler struct mei_device *dev; 6269ca9050bSTomas Winkler struct mei_cl_cb *cb; 6279ca9050bSTomas Winkler int rets; 6289ca9050bSTomas Winkler int i; 6299ca9050bSTomas Winkler 63090e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 63190e0b5f1STomas Winkler return -ENODEV; 63290e0b5f1STomas Winkler 63390e0b5f1STomas Winkler dev = cl->dev; 63490e0b5f1STomas Winkler 6359ca9050bSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) 6369ca9050bSTomas Winkler return -ENODEV; 6379ca9050bSTomas Winkler 6389ca9050bSTomas Winkler if (dev->dev_state != MEI_DEV_ENABLED) 6399ca9050bSTomas Winkler return -ENODEV; 6409ca9050bSTomas Winkler 641d91aaed3STomas Winkler if (cl->read_cb) { 6429ca9050bSTomas Winkler dev_dbg(&dev->pdev->dev, "read is pending.\n"); 6439ca9050bSTomas Winkler return -EBUSY; 6449ca9050bSTomas Winkler } 6459ca9050bSTomas Winkler i = mei_me_cl_by_id(dev, cl->me_client_id); 6469ca9050bSTomas Winkler if (i < 0) { 6479ca9050bSTomas Winkler dev_err(&dev->pdev->dev, "no such me client %d\n", 6489ca9050bSTomas Winkler cl->me_client_id); 6499ca9050bSTomas Winkler return -ENODEV; 6509ca9050bSTomas Winkler } 6519ca9050bSTomas Winkler 6529ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 6539ca9050bSTomas Winkler if (!cb) 6549ca9050bSTomas Winkler return -ENOMEM; 6559ca9050bSTomas Winkler 6569ca9050bSTomas Winkler rets = mei_io_cb_alloc_resp_buf(cb, 6579ca9050bSTomas Winkler dev->me_clients[i].props.max_msg_length); 6589ca9050bSTomas Winkler if (rets) 6599ca9050bSTomas Winkler goto err; 6609ca9050bSTomas Winkler 6619ca9050bSTomas Winkler cb->fop_type = MEI_FOP_READ; 6629ca9050bSTomas Winkler cl->read_cb = cb; 6639ca9050bSTomas Winkler if (dev->mei_host_buffer_is_empty) { 6649ca9050bSTomas Winkler dev->mei_host_buffer_is_empty = false; 6659ca9050bSTomas Winkler if (mei_hbm_cl_flow_control_req(dev, cl)) { 6669ca9050bSTomas Winkler rets = -ENODEV; 6679ca9050bSTomas Winkler goto err; 6689ca9050bSTomas Winkler } 6699ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->read_list.list); 6709ca9050bSTomas Winkler } else { 6719ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 6729ca9050bSTomas Winkler } 6739ca9050bSTomas Winkler return rets; 6749ca9050bSTomas Winkler err: 6759ca9050bSTomas Winkler mei_io_cb_free(cb); 6769ca9050bSTomas Winkler return rets; 6779ca9050bSTomas Winkler } 6789ca9050bSTomas Winkler 679