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> 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 /** 3090e0b5f1STomas Winkler * mei_me_cl_by_uuid - locate index of me client 3190e0b5f1STomas Winkler * 3290e0b5f1STomas Winkler * @dev: mei device 33a27a76d3SAlexander Usyskin * 34a27a76d3SAlexander Usyskin * Locking: called under "dev->device_lock" lock 35a27a76d3SAlexander Usyskin * 3690e0b5f1STomas Winkler * returns me client index or -ENOENT if not found 3790e0b5f1STomas Winkler */ 3890e0b5f1STomas Winkler int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) 3990e0b5f1STomas Winkler { 40a27a76d3SAlexander Usyskin int i; 4190e0b5f1STomas Winkler 4290e0b5f1STomas Winkler for (i = 0; i < dev->me_clients_num; ++i) 4390e0b5f1STomas Winkler if (uuid_le_cmp(*uuid, 44a27a76d3SAlexander Usyskin dev->me_clients[i].props.protocol_name) == 0) 45a27a76d3SAlexander Usyskin return i; 4690e0b5f1STomas Winkler 47a27a76d3SAlexander Usyskin return -ENOENT; 4890e0b5f1STomas Winkler } 4990e0b5f1STomas Winkler 5090e0b5f1STomas Winkler 5190e0b5f1STomas Winkler /** 5290e0b5f1STomas Winkler * mei_me_cl_by_id return index to me_clients for client_id 5390e0b5f1STomas Winkler * 5490e0b5f1STomas Winkler * @dev: the device structure 5590e0b5f1STomas Winkler * @client_id: me client id 5690e0b5f1STomas Winkler * 5790e0b5f1STomas Winkler * Locking: called under "dev->device_lock" lock 5890e0b5f1STomas Winkler * 5990e0b5f1STomas Winkler * returns index on success, -ENOENT on failure. 6090e0b5f1STomas Winkler */ 6190e0b5f1STomas Winkler 6290e0b5f1STomas Winkler int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 6390e0b5f1STomas Winkler { 6490e0b5f1STomas Winkler int i; 65a27a76d3SAlexander Usyskin 6690e0b5f1STomas Winkler for (i = 0; i < dev->me_clients_num; i++) 6790e0b5f1STomas Winkler if (dev->me_clients[i].client_id == client_id) 6890e0b5f1STomas Winkler return i; 69a27a76d3SAlexander Usyskin 70a27a76d3SAlexander Usyskin return -ENOENT; 7190e0b5f1STomas Winkler } 729ca9050bSTomas Winkler 739ca9050bSTomas Winkler 749ca9050bSTomas Winkler /** 75cc99ecfdSTomas Winkler * mei_cl_cmp_id - tells if the clients are the same 76cc99ecfdSTomas Winkler * 77cc99ecfdSTomas Winkler * @cl1: host client 1 78cc99ecfdSTomas Winkler * @cl2: host client 2 79cc99ecfdSTomas Winkler * 80cc99ecfdSTomas Winkler * returns true - if the clients has same host and me ids 81cc99ecfdSTomas Winkler * false - otherwise 82cc99ecfdSTomas Winkler */ 83cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, 84cc99ecfdSTomas Winkler const struct mei_cl *cl2) 85cc99ecfdSTomas Winkler { 86cc99ecfdSTomas Winkler return cl1 && cl2 && 87cc99ecfdSTomas Winkler (cl1->host_client_id == cl2->host_client_id) && 88cc99ecfdSTomas Winkler (cl1->me_client_id == cl2->me_client_id); 89cc99ecfdSTomas Winkler } 90cc99ecfdSTomas Winkler 91cc99ecfdSTomas Winkler /** 92cc99ecfdSTomas Winkler * mei_io_list_flush - removes cbs belonging to cl. 93cc99ecfdSTomas Winkler * 94cc99ecfdSTomas Winkler * @list: an instance of our list structure 95cc99ecfdSTomas Winkler * @cl: host client, can be NULL for flushing the whole list 96cc99ecfdSTomas Winkler * @free: whether to free the cbs 97cc99ecfdSTomas Winkler */ 98cc99ecfdSTomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list, 99cc99ecfdSTomas Winkler struct mei_cl *cl, bool free) 100cc99ecfdSTomas Winkler { 101cc99ecfdSTomas Winkler struct mei_cl_cb *cb; 102cc99ecfdSTomas Winkler struct mei_cl_cb *next; 103cc99ecfdSTomas Winkler 104cc99ecfdSTomas Winkler /* enable removing everything if no cl is specified */ 105cc99ecfdSTomas Winkler list_for_each_entry_safe(cb, next, &list->list, list) { 106cc99ecfdSTomas Winkler if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) { 107cc99ecfdSTomas Winkler list_del(&cb->list); 108cc99ecfdSTomas Winkler if (free) 109cc99ecfdSTomas Winkler mei_io_cb_free(cb); 110cc99ecfdSTomas Winkler } 111cc99ecfdSTomas Winkler } 112cc99ecfdSTomas Winkler } 113cc99ecfdSTomas Winkler 114cc99ecfdSTomas Winkler /** 1159ca9050bSTomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 1169ca9050bSTomas Winkler * 1179ca9050bSTomas Winkler * @list: An instance of our list structure 1189ca9050bSTomas Winkler * @cl: host client 1199ca9050bSTomas Winkler */ 1205456796bSAlexander Usyskin void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 1219ca9050bSTomas Winkler { 122cc99ecfdSTomas Winkler __mei_io_list_flush(list, cl, false); 1239ca9050bSTomas Winkler } 124cc99ecfdSTomas Winkler 125cc99ecfdSTomas Winkler 126cc99ecfdSTomas Winkler /** 127cc99ecfdSTomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 128cc99ecfdSTomas Winkler * 129cc99ecfdSTomas Winkler * @list: An instance of our list structure 130cc99ecfdSTomas Winkler * @cl: host client 131cc99ecfdSTomas Winkler */ 132cc99ecfdSTomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) 133cc99ecfdSTomas Winkler { 134cc99ecfdSTomas Winkler __mei_io_list_flush(list, cl, true); 1359ca9050bSTomas Winkler } 1369ca9050bSTomas Winkler 1379ca9050bSTomas Winkler /** 1389ca9050bSTomas Winkler * mei_io_cb_free - free mei_cb_private related memory 1399ca9050bSTomas Winkler * 1409ca9050bSTomas Winkler * @cb: mei callback struct 1419ca9050bSTomas Winkler */ 1429ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb) 1439ca9050bSTomas Winkler { 1449ca9050bSTomas Winkler if (cb == NULL) 1459ca9050bSTomas Winkler return; 1469ca9050bSTomas Winkler 1479ca9050bSTomas Winkler kfree(cb->request_buffer.data); 1489ca9050bSTomas Winkler kfree(cb->response_buffer.data); 1499ca9050bSTomas Winkler kfree(cb); 1509ca9050bSTomas Winkler } 1519ca9050bSTomas Winkler 1529ca9050bSTomas Winkler /** 1539ca9050bSTomas Winkler * mei_io_cb_init - allocate and initialize io callback 1549ca9050bSTomas Winkler * 1559ca9050bSTomas Winkler * @cl - mei client 156393b148fSMasanari Iida * @fp: pointer to file structure 1579ca9050bSTomas Winkler * 1589ca9050bSTomas Winkler * returns mei_cl_cb pointer or NULL; 1599ca9050bSTomas Winkler */ 1609ca9050bSTomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) 1619ca9050bSTomas Winkler { 1629ca9050bSTomas Winkler struct mei_cl_cb *cb; 1639ca9050bSTomas Winkler 1649ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 1659ca9050bSTomas Winkler if (!cb) 1669ca9050bSTomas Winkler return NULL; 1679ca9050bSTomas Winkler 1689ca9050bSTomas Winkler mei_io_list_init(cb); 1699ca9050bSTomas Winkler 1709ca9050bSTomas Winkler cb->file_object = fp; 1719ca9050bSTomas Winkler cb->cl = cl; 1729ca9050bSTomas Winkler cb->buf_idx = 0; 1739ca9050bSTomas Winkler return cb; 1749ca9050bSTomas Winkler } 1759ca9050bSTomas Winkler 1769ca9050bSTomas Winkler /** 1779ca9050bSTomas Winkler * mei_io_cb_alloc_req_buf - allocate request buffer 1789ca9050bSTomas Winkler * 179393b148fSMasanari Iida * @cb: io callback structure 180393b148fSMasanari Iida * @length: size of the buffer 1819ca9050bSTomas Winkler * 1829ca9050bSTomas Winkler * returns 0 on success 1839ca9050bSTomas Winkler * -EINVAL if cb is NULL 1849ca9050bSTomas Winkler * -ENOMEM if allocation failed 1859ca9050bSTomas Winkler */ 1869ca9050bSTomas Winkler int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) 1879ca9050bSTomas Winkler { 1889ca9050bSTomas Winkler if (!cb) 1899ca9050bSTomas Winkler return -EINVAL; 1909ca9050bSTomas Winkler 1919ca9050bSTomas Winkler if (length == 0) 1929ca9050bSTomas Winkler return 0; 1939ca9050bSTomas Winkler 1949ca9050bSTomas Winkler cb->request_buffer.data = kmalloc(length, GFP_KERNEL); 1959ca9050bSTomas Winkler if (!cb->request_buffer.data) 1969ca9050bSTomas Winkler return -ENOMEM; 1979ca9050bSTomas Winkler cb->request_buffer.size = length; 1989ca9050bSTomas Winkler return 0; 1999ca9050bSTomas Winkler } 2009ca9050bSTomas Winkler /** 20183ce0741SAlexander Usyskin * mei_io_cb_alloc_resp_buf - allocate response buffer 2029ca9050bSTomas Winkler * 203393b148fSMasanari Iida * @cb: io callback structure 204393b148fSMasanari Iida * @length: size of the buffer 2059ca9050bSTomas Winkler * 2069ca9050bSTomas Winkler * returns 0 on success 2079ca9050bSTomas Winkler * -EINVAL if cb is NULL 2089ca9050bSTomas Winkler * -ENOMEM if allocation failed 2099ca9050bSTomas Winkler */ 2109ca9050bSTomas Winkler int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) 2119ca9050bSTomas Winkler { 2129ca9050bSTomas Winkler if (!cb) 2139ca9050bSTomas Winkler return -EINVAL; 2149ca9050bSTomas Winkler 2159ca9050bSTomas Winkler if (length == 0) 2169ca9050bSTomas Winkler return 0; 2179ca9050bSTomas Winkler 2189ca9050bSTomas Winkler cb->response_buffer.data = kmalloc(length, GFP_KERNEL); 2199ca9050bSTomas Winkler if (!cb->response_buffer.data) 2209ca9050bSTomas Winkler return -ENOMEM; 2219ca9050bSTomas Winkler cb->response_buffer.size = length; 2229ca9050bSTomas Winkler return 0; 2239ca9050bSTomas Winkler } 2249ca9050bSTomas Winkler 2259ca9050bSTomas Winkler 2269ca9050bSTomas Winkler 2279ca9050bSTomas Winkler /** 2289ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 2299ca9050bSTomas Winkler * 2309ca9050bSTomas Winkler * @cl: host client 2319ca9050bSTomas Winkler */ 2329ca9050bSTomas Winkler int mei_cl_flush_queues(struct mei_cl *cl) 2339ca9050bSTomas Winkler { 234c0abffbdSAlexander Usyskin struct mei_device *dev; 235c0abffbdSAlexander Usyskin 23690e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 2379ca9050bSTomas Winkler return -EINVAL; 2389ca9050bSTomas Winkler 239c0abffbdSAlexander Usyskin dev = cl->dev; 240c0abffbdSAlexander Usyskin 241c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 2429ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->read_list, cl); 243cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 244cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 2459ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 2469ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 2479ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 2489ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); 2499ca9050bSTomas Winkler return 0; 2509ca9050bSTomas Winkler } 2519ca9050bSTomas Winkler 2529ca9050bSTomas Winkler 2539ca9050bSTomas Winkler /** 25483ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 2559ca9050bSTomas Winkler * 2569ca9050bSTomas Winkler * @cl: host client to be initialized 2579ca9050bSTomas Winkler * @dev: mei device 2589ca9050bSTomas Winkler */ 2599ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 2609ca9050bSTomas Winkler { 2619ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 2629ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 2639ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 2649ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 2659ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 266a7b71bc0SSamuel Ortiz INIT_LIST_HEAD(&cl->device_link); 2679ca9050bSTomas Winkler cl->reading_state = MEI_IDLE; 2689ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 2699ca9050bSTomas Winkler cl->dev = dev; 2709ca9050bSTomas Winkler } 2719ca9050bSTomas Winkler 2729ca9050bSTomas Winkler /** 2739ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 2749ca9050bSTomas Winkler * 2759ca9050bSTomas Winkler * @dev: mei device 2769ca9050bSTomas Winkler * returns The allocated file or NULL on failure 2779ca9050bSTomas Winkler */ 2789ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 2799ca9050bSTomas Winkler { 2809ca9050bSTomas Winkler struct mei_cl *cl; 2819ca9050bSTomas Winkler 2829ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 2839ca9050bSTomas Winkler if (!cl) 2849ca9050bSTomas Winkler return NULL; 2859ca9050bSTomas Winkler 2869ca9050bSTomas Winkler mei_cl_init(cl, dev); 2879ca9050bSTomas Winkler 2889ca9050bSTomas Winkler return cl; 2899ca9050bSTomas Winkler } 2909ca9050bSTomas Winkler 29190e0b5f1STomas Winkler /** 29290e0b5f1STomas Winkler * mei_cl_find_read_cb - find this cl's callback in the read list 29390e0b5f1STomas Winkler * 294393b148fSMasanari Iida * @cl: host client 295393b148fSMasanari Iida * 29690e0b5f1STomas Winkler * returns cb on success, NULL on error 29790e0b5f1STomas Winkler */ 29890e0b5f1STomas Winkler struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) 29990e0b5f1STomas Winkler { 30090e0b5f1STomas Winkler struct mei_device *dev = cl->dev; 30131f88f57STomas Winkler struct mei_cl_cb *cb; 30290e0b5f1STomas Winkler 30331f88f57STomas Winkler list_for_each_entry(cb, &dev->read_list.list, list) 30490e0b5f1STomas Winkler if (mei_cl_cmp_id(cl, cb->cl)) 30590e0b5f1STomas Winkler return cb; 30690e0b5f1STomas Winkler return NULL; 30790e0b5f1STomas Winkler } 30890e0b5f1STomas Winkler 30983ce0741SAlexander Usyskin /** mei_cl_link: allocate host id in the host map 3109ca9050bSTomas Winkler * 311781d0d89STomas Winkler * @cl - host client 31283ce0741SAlexander Usyskin * @id - fixed host id or -1 for generic one 313393b148fSMasanari Iida * 314781d0d89STomas Winkler * returns 0 on success 3159ca9050bSTomas Winkler * -EINVAL on incorrect values 3169ca9050bSTomas Winkler * -ENONET if client not found 3179ca9050bSTomas Winkler */ 318781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 3199ca9050bSTomas Winkler { 32090e0b5f1STomas Winkler struct mei_device *dev; 32122f96a0eSTomas Winkler long open_handle_count; 3229ca9050bSTomas Winkler 323781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 3249ca9050bSTomas Winkler return -EINVAL; 3259ca9050bSTomas Winkler 32690e0b5f1STomas Winkler dev = cl->dev; 32790e0b5f1STomas Winkler 32883ce0741SAlexander Usyskin /* If Id is not assigned get one*/ 329781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 330781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 331781d0d89STomas Winkler MEI_CLIENTS_MAX); 3329ca9050bSTomas Winkler 333781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 33483ce0741SAlexander Usyskin dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 335e036cc57STomas Winkler return -EMFILE; 336e036cc57STomas Winkler } 337e036cc57STomas Winkler 33822f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 33922f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 34083ce0741SAlexander Usyskin dev_err(&dev->pdev->dev, "open_handle_count exceeded %d", 341e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 342e036cc57STomas Winkler return -EMFILE; 3439ca9050bSTomas Winkler } 344781d0d89STomas Winkler 345781d0d89STomas Winkler dev->open_handle_count++; 346781d0d89STomas Winkler 347781d0d89STomas Winkler cl->host_client_id = id; 348781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 349781d0d89STomas Winkler 350781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 351781d0d89STomas Winkler 352781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 353781d0d89STomas Winkler 354c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 355781d0d89STomas Winkler return 0; 356781d0d89STomas Winkler } 357781d0d89STomas Winkler 3589ca9050bSTomas Winkler /** 35990e0b5f1STomas Winkler * mei_cl_unlink - remove me_cl from the list 3609ca9050bSTomas Winkler * 361393b148fSMasanari Iida * @cl: host client 3629ca9050bSTomas Winkler */ 36390e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 3649ca9050bSTomas Winkler { 36590e0b5f1STomas Winkler struct mei_device *dev; 36690e0b5f1STomas Winkler 367781d0d89STomas Winkler /* don't shout on error exit path */ 368781d0d89STomas Winkler if (!cl) 369781d0d89STomas Winkler return 0; 370781d0d89STomas Winkler 3718e9a4a9aSTomas Winkler /* wd and amthif might not be initialized */ 3728e9a4a9aSTomas Winkler if (!cl->dev) 3738e9a4a9aSTomas Winkler return 0; 37490e0b5f1STomas Winkler 37590e0b5f1STomas Winkler dev = cl->dev; 37690e0b5f1STomas Winkler 377a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 378a14c44d8STomas Winkler 37922f96a0eSTomas Winkler if (dev->open_handle_count > 0) 38022f96a0eSTomas Winkler dev->open_handle_count--; 38122f96a0eSTomas Winkler 38222f96a0eSTomas Winkler /* never clear the 0 bit */ 38322f96a0eSTomas Winkler if (cl->host_client_id) 38422f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 38522f96a0eSTomas Winkler 38622f96a0eSTomas Winkler list_del_init(&cl->link); 38722f96a0eSTomas Winkler 38822f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 38922f96a0eSTomas Winkler 39090e0b5f1STomas Winkler return 0; 3919ca9050bSTomas Winkler } 3929ca9050bSTomas Winkler 3939ca9050bSTomas Winkler 3949ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 3959ca9050bSTomas Winkler { 3969ca9050bSTomas Winkler struct mei_device *dev = container_of(work, 3979ca9050bSTomas Winkler struct mei_device, init_work); 3989ca9050bSTomas Winkler struct mei_client_properties *client_props; 3999ca9050bSTomas Winkler int i; 4009ca9050bSTomas Winkler 4019ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 4029ca9050bSTomas Winkler 4039ca9050bSTomas Winkler for (i = 0; i < dev->me_clients_num; i++) { 4049ca9050bSTomas Winkler client_props = &dev->me_clients[i].props; 4059ca9050bSTomas Winkler 4061a1aca42STomas Winkler if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) 4079ca9050bSTomas Winkler mei_amthif_host_init(dev); 4089ca9050bSTomas Winkler else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) 4099ca9050bSTomas Winkler mei_wd_host_init(dev); 41059fcd7c6SSamuel Ortiz else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) 41159fcd7c6SSamuel Ortiz mei_nfc_host_init(dev); 41259fcd7c6SSamuel Ortiz 4139ca9050bSTomas Winkler } 4149ca9050bSTomas Winkler 4159ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 4166adb8efbSTomas Winkler dev->reset_count = 0; 4179ca9050bSTomas Winkler 4189ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 41904bb139aSTomas Winkler 42004bb139aSTomas Winkler pm_runtime_mark_last_busy(&dev->pdev->dev); 42104bb139aSTomas Winkler dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n"); 42204bb139aSTomas Winkler pm_runtime_autosuspend(&dev->pdev->dev); 4239ca9050bSTomas Winkler } 4249ca9050bSTomas Winkler 4256aae48ffSTomas Winkler /** 4266aae48ffSTomas Winkler * mei_hbuf_acquire: try to acquire host buffer 4276aae48ffSTomas Winkler * 4286aae48ffSTomas Winkler * @dev: the device structure 4296aae48ffSTomas Winkler * returns true if host buffer was acquired 4306aae48ffSTomas Winkler */ 4316aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 4326aae48ffSTomas Winkler { 43304bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 43404bb139aSTomas Winkler dev->pg_event == MEI_PG_EVENT_WAIT) { 43504bb139aSTomas Winkler dev_dbg(&dev->pdev->dev, "device is in pg\n"); 43604bb139aSTomas Winkler return false; 43704bb139aSTomas Winkler } 43804bb139aSTomas Winkler 4396aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 4406aae48ffSTomas Winkler dev_dbg(&dev->pdev->dev, "hbuf is not ready\n"); 4416aae48ffSTomas Winkler return false; 4426aae48ffSTomas Winkler } 4436aae48ffSTomas Winkler 4446aae48ffSTomas Winkler dev->hbuf_is_ready = false; 4456aae48ffSTomas Winkler 4466aae48ffSTomas Winkler return true; 4476aae48ffSTomas Winkler } 4489ca9050bSTomas Winkler 4499ca9050bSTomas Winkler /** 45083ce0741SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 4519ca9050bSTomas Winkler * 45290e0b5f1STomas Winkler * @cl: host client 4539ca9050bSTomas Winkler * 4549ca9050bSTomas Winkler * Locking: called under "dev->device_lock" lock 4559ca9050bSTomas Winkler * 4569ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 4579ca9050bSTomas Winkler */ 45890e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl) 4599ca9050bSTomas Winkler { 46090e0b5f1STomas Winkler struct mei_device *dev; 4619ca9050bSTomas Winkler struct mei_cl_cb *cb; 462fe2f17ebSAlexander Usyskin int rets; 4639ca9050bSTomas Winkler 46490e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 4659ca9050bSTomas Winkler return -ENODEV; 4669ca9050bSTomas Winkler 46790e0b5f1STomas Winkler dev = cl->dev; 46890e0b5f1STomas Winkler 469c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 470c0abffbdSAlexander Usyskin 4719ca9050bSTomas Winkler if (cl->state != MEI_FILE_DISCONNECTING) 4729ca9050bSTomas Winkler return 0; 4739ca9050bSTomas Winkler 47404bb139aSTomas Winkler rets = pm_runtime_get(&dev->pdev->dev); 47504bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 47604bb139aSTomas Winkler pm_runtime_put_noidle(&dev->pdev->dev); 47704bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 47804bb139aSTomas Winkler return rets; 47904bb139aSTomas Winkler } 48004bb139aSTomas Winkler 4819ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 48204bb139aSTomas Winkler if (!cb) { 48304bb139aSTomas Winkler rets = -ENOMEM; 48404bb139aSTomas Winkler goto free; 48504bb139aSTomas Winkler } 4869ca9050bSTomas Winkler 4879ca9050bSTomas Winkler cb->fop_type = MEI_FOP_CLOSE; 4886aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 4899ca9050bSTomas Winkler if (mei_hbm_cl_disconnect_req(dev, cl)) { 4909ca9050bSTomas Winkler rets = -ENODEV; 491c0abffbdSAlexander Usyskin cl_err(dev, cl, "failed to disconnect.\n"); 4929ca9050bSTomas Winkler goto free; 4939ca9050bSTomas Winkler } 49422b987a3SAlexander Usyskin cl->timer_count = MEI_CONNECT_TIMEOUT; 4959ca9050bSTomas Winkler mdelay(10); /* Wait for hardware disconnection ready */ 4969ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 4979ca9050bSTomas Winkler } else { 498c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 4999ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 5009ca9050bSTomas Winkler 5019ca9050bSTomas Winkler } 5029ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 5039ca9050bSTomas Winkler 504fe2f17ebSAlexander Usyskin wait_event_timeout(dev->wait_recvd_msg, 5059ca9050bSTomas Winkler MEI_FILE_DISCONNECTED == cl->state, 5069ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 5079ca9050bSTomas Winkler 5089ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 509fe2f17ebSAlexander Usyskin 5109ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED == cl->state) { 5119ca9050bSTomas Winkler rets = 0; 512c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 5139ca9050bSTomas Winkler } else { 514fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 515fe2f17ebSAlexander Usyskin rets = -ETIME; 5169ca9050bSTomas Winkler } 5179ca9050bSTomas Winkler 5189ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 5199ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 5209ca9050bSTomas Winkler free: 52104bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 52204bb139aSTomas Winkler pm_runtime_mark_last_busy(&dev->pdev->dev); 52304bb139aSTomas Winkler pm_runtime_put_autosuspend(&dev->pdev->dev); 52404bb139aSTomas Winkler 5259ca9050bSTomas Winkler mei_io_cb_free(cb); 5269ca9050bSTomas Winkler return rets; 5279ca9050bSTomas Winkler } 5289ca9050bSTomas Winkler 5299ca9050bSTomas Winkler 5309ca9050bSTomas Winkler /** 53190e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 53290e0b5f1STomas Winkler * client with the same me client id is connecting 5339ca9050bSTomas Winkler * 5349ca9050bSTomas Winkler * @cl: private data of the file object 5359ca9050bSTomas Winkler * 53683ce0741SAlexander Usyskin * returns true if other client is connected, false - otherwise. 5379ca9050bSTomas Winkler */ 53890e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl) 5399ca9050bSTomas Winkler { 54090e0b5f1STomas Winkler struct mei_device *dev; 54131f88f57STomas Winkler struct mei_cl *ocl; /* the other client */ 5429ca9050bSTomas Winkler 54390e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 54490e0b5f1STomas Winkler return false; 54590e0b5f1STomas Winkler 54690e0b5f1STomas Winkler dev = cl->dev; 54790e0b5f1STomas Winkler 54831f88f57STomas Winkler list_for_each_entry(ocl, &dev->file_list, link) { 54931f88f57STomas Winkler if (ocl->state == MEI_FILE_CONNECTING && 55031f88f57STomas Winkler ocl != cl && 55131f88f57STomas Winkler cl->me_client_id == ocl->me_client_id) 55290e0b5f1STomas Winkler return true; 5539ca9050bSTomas Winkler 5549ca9050bSTomas Winkler } 55590e0b5f1STomas Winkler 55690e0b5f1STomas Winkler return false; 5579ca9050bSTomas Winkler } 5589ca9050bSTomas Winkler 5599ca9050bSTomas Winkler /** 56083ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 5619f81abdaSTomas Winkler * 5629f81abdaSTomas Winkler * @cl: host client 5639f81abdaSTomas Winkler * 5649f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 5659f81abdaSTomas Winkler * 5669f81abdaSTomas Winkler * returns 0 on success, <0 on failure. 5679f81abdaSTomas Winkler */ 5689f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file) 5699f81abdaSTomas Winkler { 5709f81abdaSTomas Winkler struct mei_device *dev; 5719f81abdaSTomas Winkler struct mei_cl_cb *cb; 5729f81abdaSTomas Winkler int rets; 5739f81abdaSTomas Winkler 5749f81abdaSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 5759f81abdaSTomas Winkler return -ENODEV; 5769f81abdaSTomas Winkler 5779f81abdaSTomas Winkler dev = cl->dev; 5789f81abdaSTomas Winkler 57904bb139aSTomas Winkler rets = pm_runtime_get(&dev->pdev->dev); 58004bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 58104bb139aSTomas Winkler pm_runtime_put_noidle(&dev->pdev->dev); 58204bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 58304bb139aSTomas Winkler return rets; 58404bb139aSTomas Winkler } 58504bb139aSTomas Winkler 5869f81abdaSTomas Winkler cb = mei_io_cb_init(cl, file); 5879f81abdaSTomas Winkler if (!cb) { 5889f81abdaSTomas Winkler rets = -ENOMEM; 5899f81abdaSTomas Winkler goto out; 5909f81abdaSTomas Winkler } 5919f81abdaSTomas Winkler 59202a7eeccSTomas Winkler cb->fop_type = MEI_FOP_CONNECT; 5939f81abdaSTomas Winkler 5946aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 5956aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 596e4d8270eSAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 5979f81abdaSTomas Winkler if (mei_hbm_cl_connect_req(dev, cl)) { 5989f81abdaSTomas Winkler rets = -ENODEV; 5999f81abdaSTomas Winkler goto out; 6009f81abdaSTomas Winkler } 6019f81abdaSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 6029f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 6039f81abdaSTomas Winkler } else { 60473ab4232SAlexander Usyskin cl->state = MEI_FILE_INITIALIZING; 6059f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 6069f81abdaSTomas Winkler } 6079f81abdaSTomas Winkler 6089f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 609285e2996SAlexander Usyskin wait_event_timeout(dev->wait_recvd_msg, 6109f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 6119f81abdaSTomas Winkler cl->state == MEI_FILE_DISCONNECTED), 612206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 6139f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 6149f81abdaSTomas Winkler 6159f81abdaSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) { 6163e37ebb7SAlexander Usyskin cl->state = MEI_FILE_DISCONNECTED; 617285e2996SAlexander Usyskin /* something went really wrong */ 618285e2996SAlexander Usyskin if (!cl->status) 619285e2996SAlexander Usyskin cl->status = -EFAULT; 6209f81abdaSTomas Winkler 6219f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 6229f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 6239f81abdaSTomas Winkler } 6249f81abdaSTomas Winkler 6259f81abdaSTomas Winkler rets = cl->status; 6269f81abdaSTomas Winkler 6279f81abdaSTomas Winkler out: 62804bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 62904bb139aSTomas Winkler pm_runtime_mark_last_busy(&dev->pdev->dev); 63004bb139aSTomas Winkler pm_runtime_put_autosuspend(&dev->pdev->dev); 63104bb139aSTomas Winkler 6329f81abdaSTomas Winkler mei_io_cb_free(cb); 6339f81abdaSTomas Winkler return rets; 6349f81abdaSTomas Winkler } 6359f81abdaSTomas Winkler 6369f81abdaSTomas Winkler /** 63790e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 6389ca9050bSTomas Winkler * 6399ca9050bSTomas Winkler * @cl: private data of the file object 6409ca9050bSTomas Winkler * 6419ca9050bSTomas Winkler * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 6429ca9050bSTomas Winkler * -ENOENT if mei_cl is not present 6439ca9050bSTomas Winkler * -EINVAL if single_recv_buf == 0 6449ca9050bSTomas Winkler */ 64590e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 6469ca9050bSTomas Winkler { 64790e0b5f1STomas Winkler struct mei_device *dev; 64812d00665SAlexander Usyskin struct mei_me_client *me_cl; 64912d00665SAlexander Usyskin int id; 6509ca9050bSTomas Winkler 65190e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 65290e0b5f1STomas Winkler return -EINVAL; 65390e0b5f1STomas Winkler 65490e0b5f1STomas Winkler dev = cl->dev; 65590e0b5f1STomas Winkler 6569ca9050bSTomas Winkler if (!dev->me_clients_num) 6579ca9050bSTomas Winkler return 0; 6589ca9050bSTomas Winkler 6599ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 6609ca9050bSTomas Winkler return 1; 6619ca9050bSTomas Winkler 66212d00665SAlexander Usyskin id = mei_me_cl_by_id(dev, cl->me_client_id); 66312d00665SAlexander Usyskin if (id < 0) { 66412d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 66512d00665SAlexander Usyskin return id; 66612d00665SAlexander Usyskin } 66712d00665SAlexander Usyskin 66812d00665SAlexander Usyskin me_cl = &dev->me_clients[id]; 6699ca9050bSTomas Winkler if (me_cl->mei_flow_ctrl_creds) { 6709ca9050bSTomas Winkler if (WARN_ON(me_cl->props.single_recv_buf == 0)) 6719ca9050bSTomas Winkler return -EINVAL; 6729ca9050bSTomas Winkler return 1; 67312d00665SAlexander Usyskin } 6749ca9050bSTomas Winkler return 0; 6759ca9050bSTomas Winkler } 6769ca9050bSTomas Winkler 6779ca9050bSTomas Winkler /** 67890e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 6799ca9050bSTomas Winkler * 6809ca9050bSTomas Winkler * @cl: private data of the file object 681393b148fSMasanari Iida * 6829ca9050bSTomas Winkler * @returns 6839ca9050bSTomas Winkler * 0 on success 6849ca9050bSTomas Winkler * -ENOENT when me client is not found 6859ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 6869ca9050bSTomas Winkler */ 68790e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 6889ca9050bSTomas Winkler { 68990e0b5f1STomas Winkler struct mei_device *dev; 69012d00665SAlexander Usyskin struct mei_me_client *me_cl; 69112d00665SAlexander Usyskin int id; 6929ca9050bSTomas Winkler 69390e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 69490e0b5f1STomas Winkler return -EINVAL; 69590e0b5f1STomas Winkler 69690e0b5f1STomas Winkler dev = cl->dev; 69790e0b5f1STomas Winkler 69812d00665SAlexander Usyskin id = mei_me_cl_by_id(dev, cl->me_client_id); 69912d00665SAlexander Usyskin if (id < 0) { 70012d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 70112d00665SAlexander Usyskin return id; 70212d00665SAlexander Usyskin } 7039ca9050bSTomas Winkler 70412d00665SAlexander Usyskin me_cl = &dev->me_clients[id]; 7059ca9050bSTomas Winkler if (me_cl->props.single_recv_buf != 0) { 7069ca9050bSTomas Winkler if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) 7079ca9050bSTomas Winkler return -EINVAL; 70812d00665SAlexander Usyskin me_cl->mei_flow_ctrl_creds--; 7099ca9050bSTomas Winkler } else { 7109ca9050bSTomas Winkler if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 7119ca9050bSTomas Winkler return -EINVAL; 7129ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 7139ca9050bSTomas Winkler } 7149ca9050bSTomas Winkler return 0; 7159ca9050bSTomas Winkler } 7169ca9050bSTomas Winkler 7179ca9050bSTomas Winkler /** 718393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 7199ca9050bSTomas Winkler * 72090e0b5f1STomas Winkler * @cl: host client 7219ca9050bSTomas Winkler * 7229ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 7239ca9050bSTomas Winkler */ 724fcb136e1STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length) 7259ca9050bSTomas Winkler { 72690e0b5f1STomas Winkler struct mei_device *dev; 7279ca9050bSTomas Winkler struct mei_cl_cb *cb; 7289ca9050bSTomas Winkler int rets; 7299ca9050bSTomas Winkler int i; 7309ca9050bSTomas Winkler 73190e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 73290e0b5f1STomas Winkler return -ENODEV; 73390e0b5f1STomas Winkler 73490e0b5f1STomas Winkler dev = cl->dev; 73590e0b5f1STomas Winkler 736b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 7379ca9050bSTomas Winkler return -ENODEV; 7389ca9050bSTomas Winkler 739d91aaed3STomas Winkler if (cl->read_cb) { 740c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "read is pending.\n"); 7419ca9050bSTomas Winkler return -EBUSY; 7429ca9050bSTomas Winkler } 7439ca9050bSTomas Winkler i = mei_me_cl_by_id(dev, cl->me_client_id); 7449ca9050bSTomas Winkler if (i < 0) { 745c0abffbdSAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 7467ca96aa2SAlexander Usyskin return -ENOTTY; 7479ca9050bSTomas Winkler } 7489ca9050bSTomas Winkler 74904bb139aSTomas Winkler rets = pm_runtime_get(&dev->pdev->dev); 75004bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 75104bb139aSTomas Winkler pm_runtime_put_noidle(&dev->pdev->dev); 75204bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 75304bb139aSTomas Winkler return rets; 75404bb139aSTomas Winkler } 75504bb139aSTomas Winkler 7569ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 75704bb139aSTomas Winkler if (!cb) { 75804bb139aSTomas Winkler rets = -ENOMEM; 75904bb139aSTomas Winkler goto out; 76004bb139aSTomas Winkler } 7619ca9050bSTomas Winkler 762fcb136e1STomas Winkler /* always allocate at least client max message */ 763fcb136e1STomas Winkler length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); 764fcb136e1STomas Winkler rets = mei_io_cb_alloc_resp_buf(cb, length); 7659ca9050bSTomas Winkler if (rets) 76604bb139aSTomas Winkler goto out; 7679ca9050bSTomas Winkler 7689ca9050bSTomas Winkler cb->fop_type = MEI_FOP_READ; 7696aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 77086113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 77186113500SAlexander Usyskin if (rets < 0) 77204bb139aSTomas Winkler goto out; 77304bb139aSTomas Winkler 7749ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->read_list.list); 7759ca9050bSTomas Winkler } else { 7769ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 7779ca9050bSTomas Winkler } 778accb884bSChao Bi 779accb884bSChao Bi cl->read_cb = cb; 780accb884bSChao Bi 78104bb139aSTomas Winkler out: 78204bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 78304bb139aSTomas Winkler pm_runtime_mark_last_busy(&dev->pdev->dev); 78404bb139aSTomas Winkler pm_runtime_put_autosuspend(&dev->pdev->dev); 78504bb139aSTomas Winkler 78604bb139aSTomas Winkler if (rets) 7879ca9050bSTomas Winkler mei_io_cb_free(cb); 78804bb139aSTomas Winkler 7899ca9050bSTomas Winkler return rets; 7909ca9050bSTomas Winkler } 7919ca9050bSTomas Winkler 792074b4c01STomas Winkler /** 7939d098192STomas Winkler * mei_cl_irq_write - write a message to device 79421767546STomas Winkler * from the interrupt thread context 79521767546STomas Winkler * 79621767546STomas Winkler * @cl: client 79721767546STomas Winkler * @cb: callback block. 79821767546STomas Winkler * @cmpl_list: complete list. 79921767546STomas Winkler * 80021767546STomas Winkler * returns 0, OK; otherwise error. 80121767546STomas Winkler */ 8029d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 8039d098192STomas Winkler struct mei_cl_cb *cmpl_list) 80421767546STomas Winkler { 805136698e5STomas Winkler struct mei_device *dev; 806136698e5STomas Winkler struct mei_msg_data *buf; 80721767546STomas Winkler struct mei_msg_hdr mei_hdr; 808136698e5STomas Winkler size_t len; 809136698e5STomas Winkler u32 msg_slots; 8109d098192STomas Winkler int slots; 8112ebf8c94STomas Winkler int rets; 81221767546STomas Winkler 813136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 814136698e5STomas Winkler return -ENODEV; 815136698e5STomas Winkler 816136698e5STomas Winkler dev = cl->dev; 817136698e5STomas Winkler 818136698e5STomas Winkler buf = &cb->request_buffer; 819136698e5STomas Winkler 820136698e5STomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 821136698e5STomas Winkler if (rets < 0) 822136698e5STomas Winkler return rets; 823136698e5STomas Winkler 824136698e5STomas Winkler if (rets == 0) { 825136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 826136698e5STomas Winkler return 0; 827136698e5STomas Winkler } 828136698e5STomas Winkler 8299d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 830136698e5STomas Winkler len = buf->size - cb->buf_idx; 831136698e5STomas Winkler msg_slots = mei_data2slots(len); 832136698e5STomas Winkler 83321767546STomas Winkler mei_hdr.host_addr = cl->host_client_id; 83421767546STomas Winkler mei_hdr.me_addr = cl->me_client_id; 83521767546STomas Winkler mei_hdr.reserved = 0; 836479327fcSTomas Winkler mei_hdr.internal = cb->internal; 83721767546STomas Winkler 8389d098192STomas Winkler if (slots >= msg_slots) { 83921767546STomas Winkler mei_hdr.length = len; 84021767546STomas Winkler mei_hdr.msg_complete = 1; 84121767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 8429d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 8439d098192STomas Winkler msg_slots = slots; 8449d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 84521767546STomas Winkler mei_hdr.length = len; 84621767546STomas Winkler mei_hdr.msg_complete = 0; 84721767546STomas Winkler } else { 84821767546STomas Winkler /* wait for next time the host buffer is empty */ 84921767546STomas Winkler return 0; 85021767546STomas Winkler } 85121767546STomas Winkler 852c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", 85321767546STomas Winkler cb->request_buffer.size, cb->buf_idx); 85421767546STomas Winkler 855136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 8562ebf8c94STomas Winkler if (rets) { 8572ebf8c94STomas Winkler cl->status = rets; 85821767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 8592ebf8c94STomas Winkler return rets; 86021767546STomas Winkler } 86121767546STomas Winkler 86221767546STomas Winkler cl->status = 0; 8634dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 86421767546STomas Winkler cb->buf_idx += mei_hdr.length; 8654dfaa9f7STomas Winkler 86621767546STomas Winkler if (mei_hdr.msg_complete) { 86721767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 8682ebf8c94STomas Winkler return -EIO; 86921767546STomas Winkler list_move_tail(&cb->list, &dev->write_waiting_list.list); 87021767546STomas Winkler } 87121767546STomas Winkler 87221767546STomas Winkler return 0; 87321767546STomas Winkler } 87421767546STomas Winkler 87521767546STomas Winkler /** 8764234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 8774234a6deSTomas Winkler assumes device_lock is locked 8784234a6deSTomas Winkler * 8794234a6deSTomas Winkler * @cl: host client 8804234a6deSTomas Winkler * @cl: write callback with filled data 8814234a6deSTomas Winkler * 88283ce0741SAlexander Usyskin * returns number of bytes sent on success, <0 on failure. 8834234a6deSTomas Winkler */ 8844234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 8854234a6deSTomas Winkler { 8864234a6deSTomas Winkler struct mei_device *dev; 8874234a6deSTomas Winkler struct mei_msg_data *buf; 8884234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 8894234a6deSTomas Winkler int rets; 8904234a6deSTomas Winkler 8914234a6deSTomas Winkler 8924234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 8934234a6deSTomas Winkler return -ENODEV; 8944234a6deSTomas Winkler 8954234a6deSTomas Winkler if (WARN_ON(!cb)) 8964234a6deSTomas Winkler return -EINVAL; 8974234a6deSTomas Winkler 8984234a6deSTomas Winkler dev = cl->dev; 8994234a6deSTomas Winkler 9004234a6deSTomas Winkler 9014234a6deSTomas Winkler buf = &cb->request_buffer; 9024234a6deSTomas Winkler 903c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); 9044234a6deSTomas Winkler 90504bb139aSTomas Winkler rets = pm_runtime_get(&dev->pdev->dev); 90604bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 90704bb139aSTomas Winkler pm_runtime_put_noidle(&dev->pdev->dev); 90804bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 90904bb139aSTomas Winkler return rets; 91004bb139aSTomas Winkler } 9114234a6deSTomas Winkler 9124234a6deSTomas Winkler cb->fop_type = MEI_FOP_WRITE; 9136aae48ffSTomas Winkler cb->buf_idx = 0; 9146aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 9156aae48ffSTomas Winkler 9166aae48ffSTomas Winkler mei_hdr.host_addr = cl->host_client_id; 9176aae48ffSTomas Winkler mei_hdr.me_addr = cl->me_client_id; 9186aae48ffSTomas Winkler mei_hdr.reserved = 0; 9196aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 9206aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 9214234a6deSTomas Winkler 9224234a6deSTomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 9234234a6deSTomas Winkler if (rets < 0) 9244234a6deSTomas Winkler goto err; 9254234a6deSTomas Winkler 9266aae48ffSTomas Winkler if (rets == 0) { 9276aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 9284234a6deSTomas Winkler rets = buf->size; 9294234a6deSTomas Winkler goto out; 9304234a6deSTomas Winkler } 9316aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 9326aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 9336aae48ffSTomas Winkler rets = buf->size; 9346aae48ffSTomas Winkler goto out; 9356aae48ffSTomas Winkler } 9364234a6deSTomas Winkler 9374234a6deSTomas Winkler /* Check for a maximum length */ 9384234a6deSTomas Winkler if (buf->size > mei_hbuf_max_len(dev)) { 9394234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 9404234a6deSTomas Winkler mei_hdr.msg_complete = 0; 9414234a6deSTomas Winkler } else { 9424234a6deSTomas Winkler mei_hdr.length = buf->size; 9434234a6deSTomas Winkler mei_hdr.msg_complete = 1; 9444234a6deSTomas Winkler } 9454234a6deSTomas Winkler 9462ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 9472ebf8c94STomas Winkler if (rets) 9484234a6deSTomas Winkler goto err; 9494234a6deSTomas Winkler 9504234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 9514234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 9524234a6deSTomas Winkler 9534234a6deSTomas Winkler out: 9544234a6deSTomas Winkler if (mei_hdr.msg_complete) { 9557ca96aa2SAlexander Usyskin rets = mei_cl_flow_ctrl_reduce(cl); 9567ca96aa2SAlexander Usyskin if (rets < 0) 9574234a6deSTomas Winkler goto err; 9587ca96aa2SAlexander Usyskin 9594234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 9604234a6deSTomas Winkler } else { 9614234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 9624234a6deSTomas Winkler } 9634234a6deSTomas Winkler 9644234a6deSTomas Winkler 9654234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 9664234a6deSTomas Winkler 9674234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 9687ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 9697ca96aa2SAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE); 9707ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 9717ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 9727ca96aa2SAlexander Usyskin if (rets) { 9734234a6deSTomas Winkler if (signal_pending(current)) 9744234a6deSTomas Winkler rets = -EINTR; 9757ca96aa2SAlexander Usyskin goto err; 9764234a6deSTomas Winkler } 9774234a6deSTomas Winkler } 9787ca96aa2SAlexander Usyskin 9797ca96aa2SAlexander Usyskin rets = buf->size; 9804234a6deSTomas Winkler err: 98104bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 98204bb139aSTomas Winkler pm_runtime_mark_last_busy(&dev->pdev->dev); 98304bb139aSTomas Winkler pm_runtime_put_autosuspend(&dev->pdev->dev); 98404bb139aSTomas Winkler 9854234a6deSTomas Winkler return rets; 9864234a6deSTomas Winkler } 9874234a6deSTomas Winkler 9884234a6deSTomas Winkler 989db086fa9STomas Winkler /** 990db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 991db086fa9STomas Winkler * 992db086fa9STomas Winkler * @cl: private data of the file object. 993db086fa9STomas Winkler * @cb: callback block. 994db086fa9STomas Winkler */ 995db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 996db086fa9STomas Winkler { 997db086fa9STomas Winkler if (cb->fop_type == MEI_FOP_WRITE) { 998db086fa9STomas Winkler mei_io_cb_free(cb); 999db086fa9STomas Winkler cb = NULL; 1000db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1001db086fa9STomas Winkler if (waitqueue_active(&cl->tx_wait)) 1002db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1003db086fa9STomas Winkler 1004db086fa9STomas Winkler } else if (cb->fop_type == MEI_FOP_READ && 1005db086fa9STomas Winkler MEI_READING == cl->reading_state) { 1006db086fa9STomas Winkler cl->reading_state = MEI_READ_COMPLETE; 1007db086fa9STomas Winkler if (waitqueue_active(&cl->rx_wait)) 1008db086fa9STomas Winkler wake_up_interruptible(&cl->rx_wait); 1009db086fa9STomas Winkler else 1010db086fa9STomas Winkler mei_cl_bus_rx_event(cl); 1011db086fa9STomas Winkler 1012db086fa9STomas Winkler } 1013db086fa9STomas Winkler } 1014db086fa9STomas Winkler 10154234a6deSTomas Winkler 10164234a6deSTomas Winkler /** 1017074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1018074b4c01STomas Winkler * 1019074b4c01STomas Winkler * @dev - mei device 1020074b4c01STomas Winkler */ 1021074b4c01STomas Winkler 1022074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1023074b4c01STomas Winkler { 102431f88f57STomas Winkler struct mei_cl *cl; 1025074b4c01STomas Winkler 102631f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1027074b4c01STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 1028074b4c01STomas Winkler cl->mei_flow_ctrl_creds = 0; 1029074b4c01STomas Winkler cl->timer_count = 0; 1030074b4c01STomas Winkler } 1031074b4c01STomas Winkler } 1032074b4c01STomas Winkler 1033074b4c01STomas Winkler 1034074b4c01STomas Winkler /** 10355290801cSTomas Winkler * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted 1036074b4c01STomas Winkler * 1037074b4c01STomas Winkler * @dev - mei device 1038074b4c01STomas Winkler */ 10395290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev) 1040074b4c01STomas Winkler { 104131f88f57STomas Winkler struct mei_cl *cl; 104231f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1043074b4c01STomas Winkler if (waitqueue_active(&cl->rx_wait)) { 1044c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 1045074b4c01STomas Winkler wake_up_interruptible(&cl->rx_wait); 1046074b4c01STomas Winkler } 10475290801cSTomas Winkler if (waitqueue_active(&cl->tx_wait)) { 1048c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 10495290801cSTomas Winkler wake_up_interruptible(&cl->tx_wait); 10505290801cSTomas Winkler } 1051074b4c01STomas Winkler } 1052074b4c01STomas Winkler } 1053074b4c01STomas Winkler 1054074b4c01STomas Winkler /** 1055074b4c01STomas Winkler * mei_cl_all_write_clear - clear all pending writes 1056074b4c01STomas Winkler 1057074b4c01STomas Winkler * @dev - mei device 1058074b4c01STomas Winkler */ 1059074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev) 1060074b4c01STomas Winkler { 1061cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_list, NULL); 1062cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_waiting_list, NULL); 1063074b4c01STomas Winkler } 1064074b4c01STomas Winkler 1065074b4c01STomas Winkler 1066