19ca9050bSTomas Winkler /* 29ca9050bSTomas Winkler * 39ca9050bSTomas Winkler * Intel Management Engine Interface (Intel MEI) Linux driver 49ca9050bSTomas Winkler * Copyright (c) 2003-2012, Intel Corporation. 59ca9050bSTomas Winkler * 69ca9050bSTomas Winkler * This program is free software; you can redistribute it and/or modify it 79ca9050bSTomas Winkler * under the terms and conditions of the GNU General Public License, 89ca9050bSTomas Winkler * version 2, as published by the Free Software Foundation. 99ca9050bSTomas Winkler * 109ca9050bSTomas Winkler * This program is distributed in the hope it will be useful, but WITHOUT 119ca9050bSTomas Winkler * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 129ca9050bSTomas Winkler * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 139ca9050bSTomas Winkler * more details. 149ca9050bSTomas Winkler * 159ca9050bSTomas Winkler */ 169ca9050bSTomas Winkler 179ca9050bSTomas Winkler #include <linux/sched.h> 189ca9050bSTomas Winkler #include <linux/wait.h> 199ca9050bSTomas Winkler #include <linux/delay.h> 201f180359STomas Winkler #include <linux/slab.h> 2104bb139aSTomas Winkler #include <linux/pm_runtime.h> 229ca9050bSTomas Winkler 239ca9050bSTomas Winkler #include <linux/mei.h> 249ca9050bSTomas Winkler 259ca9050bSTomas Winkler #include "mei_dev.h" 269ca9050bSTomas Winkler #include "hbm.h" 2790e0b5f1STomas Winkler #include "client.h" 2890e0b5f1STomas Winkler 2990e0b5f1STomas Winkler /** 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 * 36d320832fSTomas Winkler * returns me client or NULL if not found 3790e0b5f1STomas Winkler */ 38d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, 39d320832fSTomas Winkler const uuid_le *uuid) 4090e0b5f1STomas Winkler { 415ca2d388STomas Winkler struct mei_me_client *me_cl; 4290e0b5f1STomas Winkler 435ca2d388STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) 445ca2d388STomas Winkler if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) 455ca2d388STomas Winkler return me_cl; 4690e0b5f1STomas Winkler 47d320832fSTomas Winkler return NULL; 4890e0b5f1STomas Winkler } 4990e0b5f1STomas Winkler 5090e0b5f1STomas Winkler /** 5190e0b5f1STomas Winkler * mei_me_cl_by_id return index to me_clients for client_id 5290e0b5f1STomas Winkler * 5390e0b5f1STomas Winkler * @dev: the device structure 5490e0b5f1STomas Winkler * @client_id: me client id 5590e0b5f1STomas Winkler * 5690e0b5f1STomas Winkler * Locking: called under "dev->device_lock" lock 5790e0b5f1STomas Winkler * 58d320832fSTomas Winkler * returns me client or NULL if not found 5990e0b5f1STomas Winkler */ 6090e0b5f1STomas Winkler 61d320832fSTomas Winkler struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) 6290e0b5f1STomas Winkler { 63a27a76d3SAlexander Usyskin 645ca2d388STomas Winkler struct mei_me_client *me_cl; 65a27a76d3SAlexander Usyskin 665ca2d388STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) 675ca2d388STomas Winkler if (me_cl->client_id == client_id) 685ca2d388STomas Winkler return me_cl; 69d320832fSTomas Winkler return NULL; 7090e0b5f1STomas Winkler } 719ca9050bSTomas Winkler 72d880f329STomas Winkler struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, 73d880f329STomas Winkler const uuid_le *uuid, u8 client_id) 74d880f329STomas Winkler { 75d880f329STomas Winkler struct mei_me_client *me_cl; 76d880f329STomas Winkler 77d880f329STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) 78d880f329STomas Winkler if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && 79d880f329STomas Winkler me_cl->client_id == client_id) 80d880f329STomas Winkler return me_cl; 81d880f329STomas Winkler return NULL; 82d880f329STomas Winkler } 83d880f329STomas Winkler 8425ca6472STomas Winkler /** 8525ca6472STomas Winkler * mei_me_cl_remove - remove me client matching uuid and client_id 8625ca6472STomas Winkler * 8725ca6472STomas Winkler * @dev: the device structure 8825ca6472STomas Winkler * @uuid: me client uuid 8925ca6472STomas Winkler * @client_id: me client address 9025ca6472STomas Winkler */ 9125ca6472STomas Winkler void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id) 9225ca6472STomas Winkler { 9325ca6472STomas Winkler struct mei_me_client *me_cl, *next; 9425ca6472STomas Winkler 9525ca6472STomas Winkler list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { 9625ca6472STomas Winkler if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && 9725ca6472STomas Winkler me_cl->client_id == client_id) { 9825ca6472STomas Winkler list_del(&me_cl->list); 9925ca6472STomas Winkler kfree(me_cl); 10025ca6472STomas Winkler break; 10125ca6472STomas Winkler } 10225ca6472STomas Winkler } 10325ca6472STomas Winkler } 10425ca6472STomas Winkler 1059ca9050bSTomas Winkler 1069ca9050bSTomas Winkler /** 107cc99ecfdSTomas Winkler * mei_cl_cmp_id - tells if the clients are the same 108cc99ecfdSTomas Winkler * 109cc99ecfdSTomas Winkler * @cl1: host client 1 110cc99ecfdSTomas Winkler * @cl2: host client 2 111cc99ecfdSTomas Winkler * 112cc99ecfdSTomas Winkler * returns true - if the clients has same host and me ids 113cc99ecfdSTomas Winkler * false - otherwise 114cc99ecfdSTomas Winkler */ 115cc99ecfdSTomas Winkler static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, 116cc99ecfdSTomas Winkler const struct mei_cl *cl2) 117cc99ecfdSTomas Winkler { 118cc99ecfdSTomas Winkler return cl1 && cl2 && 119cc99ecfdSTomas Winkler (cl1->host_client_id == cl2->host_client_id) && 120cc99ecfdSTomas Winkler (cl1->me_client_id == cl2->me_client_id); 121cc99ecfdSTomas Winkler } 122cc99ecfdSTomas Winkler 123cc99ecfdSTomas Winkler /** 124cc99ecfdSTomas Winkler * mei_io_list_flush - removes cbs belonging to cl. 125cc99ecfdSTomas Winkler * 126cc99ecfdSTomas Winkler * @list: an instance of our list structure 127cc99ecfdSTomas Winkler * @cl: host client, can be NULL for flushing the whole list 128cc99ecfdSTomas Winkler * @free: whether to free the cbs 129cc99ecfdSTomas Winkler */ 130cc99ecfdSTomas Winkler static void __mei_io_list_flush(struct mei_cl_cb *list, 131cc99ecfdSTomas Winkler struct mei_cl *cl, bool free) 132cc99ecfdSTomas Winkler { 133cc99ecfdSTomas Winkler struct mei_cl_cb *cb; 134cc99ecfdSTomas Winkler struct mei_cl_cb *next; 135cc99ecfdSTomas Winkler 136cc99ecfdSTomas Winkler /* enable removing everything if no cl is specified */ 137cc99ecfdSTomas Winkler list_for_each_entry_safe(cb, next, &list->list, list) { 138cc99ecfdSTomas Winkler if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) { 139cc99ecfdSTomas Winkler list_del(&cb->list); 140cc99ecfdSTomas Winkler if (free) 141cc99ecfdSTomas Winkler mei_io_cb_free(cb); 142cc99ecfdSTomas Winkler } 143cc99ecfdSTomas Winkler } 144cc99ecfdSTomas Winkler } 145cc99ecfdSTomas Winkler 146cc99ecfdSTomas Winkler /** 1479ca9050bSTomas Winkler * mei_io_list_flush - removes list entry belonging to cl. 1489ca9050bSTomas Winkler * 1499ca9050bSTomas Winkler * @list: An instance of our list structure 1509ca9050bSTomas Winkler * @cl: host client 1519ca9050bSTomas Winkler */ 1525456796bSAlexander Usyskin void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) 1539ca9050bSTomas Winkler { 154cc99ecfdSTomas Winkler __mei_io_list_flush(list, cl, false); 1559ca9050bSTomas Winkler } 156cc99ecfdSTomas Winkler 157cc99ecfdSTomas Winkler 158cc99ecfdSTomas Winkler /** 159cc99ecfdSTomas Winkler * mei_io_list_free - removes cb belonging to cl and free them 160cc99ecfdSTomas Winkler * 161cc99ecfdSTomas Winkler * @list: An instance of our list structure 162cc99ecfdSTomas Winkler * @cl: host client 163cc99ecfdSTomas Winkler */ 164cc99ecfdSTomas Winkler static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) 165cc99ecfdSTomas Winkler { 166cc99ecfdSTomas Winkler __mei_io_list_flush(list, cl, true); 1679ca9050bSTomas Winkler } 1689ca9050bSTomas Winkler 1699ca9050bSTomas Winkler /** 1709ca9050bSTomas Winkler * mei_io_cb_free - free mei_cb_private related memory 1719ca9050bSTomas Winkler * 1729ca9050bSTomas Winkler * @cb: mei callback struct 1739ca9050bSTomas Winkler */ 1749ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb) 1759ca9050bSTomas Winkler { 1769ca9050bSTomas Winkler if (cb == NULL) 1779ca9050bSTomas Winkler return; 1789ca9050bSTomas Winkler 1799ca9050bSTomas Winkler kfree(cb->request_buffer.data); 1809ca9050bSTomas Winkler kfree(cb->response_buffer.data); 1819ca9050bSTomas Winkler kfree(cb); 1829ca9050bSTomas Winkler } 1839ca9050bSTomas Winkler 1849ca9050bSTomas Winkler /** 1859ca9050bSTomas Winkler * mei_io_cb_init - allocate and initialize io callback 1869ca9050bSTomas Winkler * 1879ca9050bSTomas Winkler * @cl - mei client 188393b148fSMasanari Iida * @fp: pointer to file structure 1899ca9050bSTomas Winkler * 1909ca9050bSTomas Winkler * returns mei_cl_cb pointer or NULL; 1919ca9050bSTomas Winkler */ 1929ca9050bSTomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) 1939ca9050bSTomas Winkler { 1949ca9050bSTomas Winkler struct mei_cl_cb *cb; 1959ca9050bSTomas Winkler 1969ca9050bSTomas Winkler cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); 1979ca9050bSTomas Winkler if (!cb) 1989ca9050bSTomas Winkler return NULL; 1999ca9050bSTomas Winkler 2009ca9050bSTomas Winkler mei_io_list_init(cb); 2019ca9050bSTomas Winkler 2029ca9050bSTomas Winkler cb->file_object = fp; 2039ca9050bSTomas Winkler cb->cl = cl; 2049ca9050bSTomas Winkler cb->buf_idx = 0; 2059ca9050bSTomas Winkler return cb; 2069ca9050bSTomas Winkler } 2079ca9050bSTomas Winkler 2089ca9050bSTomas Winkler /** 2099ca9050bSTomas Winkler * mei_io_cb_alloc_req_buf - allocate request buffer 2109ca9050bSTomas Winkler * 211393b148fSMasanari Iida * @cb: io callback structure 212393b148fSMasanari Iida * @length: size of the buffer 2139ca9050bSTomas Winkler * 2149ca9050bSTomas Winkler * returns 0 on success 2159ca9050bSTomas Winkler * -EINVAL if cb is NULL 2169ca9050bSTomas Winkler * -ENOMEM if allocation failed 2179ca9050bSTomas Winkler */ 2189ca9050bSTomas Winkler int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) 2199ca9050bSTomas Winkler { 2209ca9050bSTomas Winkler if (!cb) 2219ca9050bSTomas Winkler return -EINVAL; 2229ca9050bSTomas Winkler 2239ca9050bSTomas Winkler if (length == 0) 2249ca9050bSTomas Winkler return 0; 2259ca9050bSTomas Winkler 2269ca9050bSTomas Winkler cb->request_buffer.data = kmalloc(length, GFP_KERNEL); 2279ca9050bSTomas Winkler if (!cb->request_buffer.data) 2289ca9050bSTomas Winkler return -ENOMEM; 2299ca9050bSTomas Winkler cb->request_buffer.size = length; 2309ca9050bSTomas Winkler return 0; 2319ca9050bSTomas Winkler } 2329ca9050bSTomas Winkler /** 23383ce0741SAlexander Usyskin * mei_io_cb_alloc_resp_buf - allocate response buffer 2349ca9050bSTomas Winkler * 235393b148fSMasanari Iida * @cb: io callback structure 236393b148fSMasanari Iida * @length: size of the buffer 2379ca9050bSTomas Winkler * 2389ca9050bSTomas Winkler * returns 0 on success 2399ca9050bSTomas Winkler * -EINVAL if cb is NULL 2409ca9050bSTomas Winkler * -ENOMEM if allocation failed 2419ca9050bSTomas Winkler */ 2429ca9050bSTomas Winkler int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) 2439ca9050bSTomas Winkler { 2449ca9050bSTomas Winkler if (!cb) 2459ca9050bSTomas Winkler return -EINVAL; 2469ca9050bSTomas Winkler 2479ca9050bSTomas Winkler if (length == 0) 2489ca9050bSTomas Winkler return 0; 2499ca9050bSTomas Winkler 2509ca9050bSTomas Winkler cb->response_buffer.data = kmalloc(length, GFP_KERNEL); 2519ca9050bSTomas Winkler if (!cb->response_buffer.data) 2529ca9050bSTomas Winkler return -ENOMEM; 2539ca9050bSTomas Winkler cb->response_buffer.size = length; 2549ca9050bSTomas Winkler return 0; 2559ca9050bSTomas Winkler } 2569ca9050bSTomas Winkler 2579ca9050bSTomas Winkler 2589ca9050bSTomas Winkler 2599ca9050bSTomas Winkler /** 2609ca9050bSTomas Winkler * mei_cl_flush_queues - flushes queue lists belonging to cl. 2619ca9050bSTomas Winkler * 2629ca9050bSTomas Winkler * @cl: host client 2639ca9050bSTomas Winkler */ 2649ca9050bSTomas Winkler int mei_cl_flush_queues(struct mei_cl *cl) 2659ca9050bSTomas Winkler { 266c0abffbdSAlexander Usyskin struct mei_device *dev; 267c0abffbdSAlexander Usyskin 26890e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 2699ca9050bSTomas Winkler return -EINVAL; 2709ca9050bSTomas Winkler 271c0abffbdSAlexander Usyskin dev = cl->dev; 272c0abffbdSAlexander Usyskin 273c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "remove list entry belonging to cl\n"); 2749ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->read_list, cl); 275cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_list, cl); 276cc99ecfdSTomas Winkler mei_io_list_free(&cl->dev->write_waiting_list, cl); 2779ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); 2789ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); 2799ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); 2809ca9050bSTomas Winkler mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); 2819ca9050bSTomas Winkler return 0; 2829ca9050bSTomas Winkler } 2839ca9050bSTomas Winkler 2849ca9050bSTomas Winkler 2859ca9050bSTomas Winkler /** 28683ce0741SAlexander Usyskin * mei_cl_init - initializes cl. 2879ca9050bSTomas Winkler * 2889ca9050bSTomas Winkler * @cl: host client to be initialized 2899ca9050bSTomas Winkler * @dev: mei device 2909ca9050bSTomas Winkler */ 2919ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) 2929ca9050bSTomas Winkler { 2939ca9050bSTomas Winkler memset(cl, 0, sizeof(struct mei_cl)); 2949ca9050bSTomas Winkler init_waitqueue_head(&cl->wait); 2959ca9050bSTomas Winkler init_waitqueue_head(&cl->rx_wait); 2969ca9050bSTomas Winkler init_waitqueue_head(&cl->tx_wait); 2979ca9050bSTomas Winkler INIT_LIST_HEAD(&cl->link); 298a7b71bc0SSamuel Ortiz INIT_LIST_HEAD(&cl->device_link); 2999ca9050bSTomas Winkler cl->reading_state = MEI_IDLE; 3009ca9050bSTomas Winkler cl->writing_state = MEI_IDLE; 3019ca9050bSTomas Winkler cl->dev = dev; 3029ca9050bSTomas Winkler } 3039ca9050bSTomas Winkler 3049ca9050bSTomas Winkler /** 3059ca9050bSTomas Winkler * mei_cl_allocate - allocates cl structure and sets it up. 3069ca9050bSTomas Winkler * 3079ca9050bSTomas Winkler * @dev: mei device 3089ca9050bSTomas Winkler * returns The allocated file or NULL on failure 3099ca9050bSTomas Winkler */ 3109ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev) 3119ca9050bSTomas Winkler { 3129ca9050bSTomas Winkler struct mei_cl *cl; 3139ca9050bSTomas Winkler 3149ca9050bSTomas Winkler cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); 3159ca9050bSTomas Winkler if (!cl) 3169ca9050bSTomas Winkler return NULL; 3179ca9050bSTomas Winkler 3189ca9050bSTomas Winkler mei_cl_init(cl, dev); 3199ca9050bSTomas Winkler 3209ca9050bSTomas Winkler return cl; 3219ca9050bSTomas Winkler } 3229ca9050bSTomas Winkler 32390e0b5f1STomas Winkler /** 32490e0b5f1STomas Winkler * mei_cl_find_read_cb - find this cl's callback in the read list 32590e0b5f1STomas Winkler * 326393b148fSMasanari Iida * @cl: host client 327393b148fSMasanari Iida * 32890e0b5f1STomas Winkler * returns cb on success, NULL on error 32990e0b5f1STomas Winkler */ 33090e0b5f1STomas Winkler struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) 33190e0b5f1STomas Winkler { 33290e0b5f1STomas Winkler struct mei_device *dev = cl->dev; 33331f88f57STomas Winkler struct mei_cl_cb *cb; 33490e0b5f1STomas Winkler 33531f88f57STomas Winkler list_for_each_entry(cb, &dev->read_list.list, list) 33690e0b5f1STomas Winkler if (mei_cl_cmp_id(cl, cb->cl)) 33790e0b5f1STomas Winkler return cb; 33890e0b5f1STomas Winkler return NULL; 33990e0b5f1STomas Winkler } 34090e0b5f1STomas Winkler 34183ce0741SAlexander Usyskin /** mei_cl_link: allocate host id in the host map 3429ca9050bSTomas Winkler * 343781d0d89STomas Winkler * @cl - host client 34483ce0741SAlexander Usyskin * @id - fixed host id or -1 for generic one 345393b148fSMasanari Iida * 346781d0d89STomas Winkler * returns 0 on success 3479ca9050bSTomas Winkler * -EINVAL on incorrect values 3489ca9050bSTomas Winkler * -ENONET if client not found 3499ca9050bSTomas Winkler */ 350781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id) 3519ca9050bSTomas Winkler { 35290e0b5f1STomas Winkler struct mei_device *dev; 35322f96a0eSTomas Winkler long open_handle_count; 3549ca9050bSTomas Winkler 355781d0d89STomas Winkler if (WARN_ON(!cl || !cl->dev)) 3569ca9050bSTomas Winkler return -EINVAL; 3579ca9050bSTomas Winkler 35890e0b5f1STomas Winkler dev = cl->dev; 35990e0b5f1STomas Winkler 36083ce0741SAlexander Usyskin /* If Id is not assigned get one*/ 361781d0d89STomas Winkler if (id == MEI_HOST_CLIENT_ID_ANY) 362781d0d89STomas Winkler id = find_first_zero_bit(dev->host_clients_map, 363781d0d89STomas Winkler MEI_CLIENTS_MAX); 3649ca9050bSTomas Winkler 365781d0d89STomas Winkler if (id >= MEI_CLIENTS_MAX) { 3662bf94cabSTomas Winkler dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); 367e036cc57STomas Winkler return -EMFILE; 368e036cc57STomas Winkler } 369e036cc57STomas Winkler 37022f96a0eSTomas Winkler open_handle_count = dev->open_handle_count + dev->iamthif_open_count; 37122f96a0eSTomas Winkler if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { 3722bf94cabSTomas Winkler dev_err(dev->dev, "open_handle_count exceeded %d", 373e036cc57STomas Winkler MEI_MAX_OPEN_HANDLE_COUNT); 374e036cc57STomas Winkler return -EMFILE; 3759ca9050bSTomas Winkler } 376781d0d89STomas Winkler 377781d0d89STomas Winkler dev->open_handle_count++; 378781d0d89STomas Winkler 379781d0d89STomas Winkler cl->host_client_id = id; 380781d0d89STomas Winkler list_add_tail(&cl->link, &dev->file_list); 381781d0d89STomas Winkler 382781d0d89STomas Winkler set_bit(id, dev->host_clients_map); 383781d0d89STomas Winkler 384781d0d89STomas Winkler cl->state = MEI_FILE_INITIALIZING; 385781d0d89STomas Winkler 386c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "link cl\n"); 387781d0d89STomas Winkler return 0; 388781d0d89STomas Winkler } 389781d0d89STomas Winkler 3909ca9050bSTomas Winkler /** 39190e0b5f1STomas Winkler * mei_cl_unlink - remove me_cl from the list 3929ca9050bSTomas Winkler * 393393b148fSMasanari Iida * @cl: host client 3949ca9050bSTomas Winkler */ 39590e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl) 3969ca9050bSTomas Winkler { 39790e0b5f1STomas Winkler struct mei_device *dev; 39890e0b5f1STomas Winkler 399781d0d89STomas Winkler /* don't shout on error exit path */ 400781d0d89STomas Winkler if (!cl) 401781d0d89STomas Winkler return 0; 402781d0d89STomas Winkler 4038e9a4a9aSTomas Winkler /* wd and amthif might not be initialized */ 4048e9a4a9aSTomas Winkler if (!cl->dev) 4058e9a4a9aSTomas Winkler return 0; 40690e0b5f1STomas Winkler 40790e0b5f1STomas Winkler dev = cl->dev; 40890e0b5f1STomas Winkler 409a14c44d8STomas Winkler cl_dbg(dev, cl, "unlink client"); 410a14c44d8STomas Winkler 41122f96a0eSTomas Winkler if (dev->open_handle_count > 0) 41222f96a0eSTomas Winkler dev->open_handle_count--; 41322f96a0eSTomas Winkler 41422f96a0eSTomas Winkler /* never clear the 0 bit */ 41522f96a0eSTomas Winkler if (cl->host_client_id) 41622f96a0eSTomas Winkler clear_bit(cl->host_client_id, dev->host_clients_map); 41722f96a0eSTomas Winkler 41822f96a0eSTomas Winkler list_del_init(&cl->link); 41922f96a0eSTomas Winkler 42022f96a0eSTomas Winkler cl->state = MEI_FILE_INITIALIZING; 42122f96a0eSTomas Winkler 42290e0b5f1STomas Winkler return 0; 4239ca9050bSTomas Winkler } 4249ca9050bSTomas Winkler 4259ca9050bSTomas Winkler 4269ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work) 4279ca9050bSTomas Winkler { 4289ca9050bSTomas Winkler struct mei_device *dev = container_of(work, 4299ca9050bSTomas Winkler struct mei_device, init_work); 4305ca2d388STomas Winkler struct mei_me_client *me_cl; 4315ca2d388STomas Winkler struct mei_client_properties *props; 4329ca9050bSTomas Winkler 4339ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 4349ca9050bSTomas Winkler 4355ca2d388STomas Winkler list_for_each_entry(me_cl, &dev->me_clients, list) { 4365ca2d388STomas Winkler props = &me_cl->props; 4379ca9050bSTomas Winkler 4385ca2d388STomas Winkler if (!uuid_le_cmp(props->protocol_name, mei_amthif_guid)) 4399ca9050bSTomas Winkler mei_amthif_host_init(dev); 4405ca2d388STomas Winkler else if (!uuid_le_cmp(props->protocol_name, mei_wd_guid)) 4419ca9050bSTomas Winkler mei_wd_host_init(dev); 4425ca2d388STomas Winkler else if (!uuid_le_cmp(props->protocol_name, mei_nfc_guid)) 44359fcd7c6SSamuel Ortiz mei_nfc_host_init(dev); 44459fcd7c6SSamuel Ortiz 4459ca9050bSTomas Winkler } 4469ca9050bSTomas Winkler 4479ca9050bSTomas Winkler dev->dev_state = MEI_DEV_ENABLED; 4486adb8efbSTomas Winkler dev->reset_count = 0; 4499ca9050bSTomas Winkler 4509ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 45104bb139aSTomas Winkler 4522bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 4532bf94cabSTomas Winkler dev_dbg(dev->dev, "rpm: autosuspend\n"); 4542bf94cabSTomas Winkler pm_runtime_autosuspend(dev->dev); 4559ca9050bSTomas Winkler } 4569ca9050bSTomas Winkler 4576aae48ffSTomas Winkler /** 4586aae48ffSTomas Winkler * mei_hbuf_acquire: try to acquire host buffer 4596aae48ffSTomas Winkler * 4606aae48ffSTomas Winkler * @dev: the device structure 4616aae48ffSTomas Winkler * returns true if host buffer was acquired 4626aae48ffSTomas Winkler */ 4636aae48ffSTomas Winkler bool mei_hbuf_acquire(struct mei_device *dev) 4646aae48ffSTomas Winkler { 46504bb139aSTomas Winkler if (mei_pg_state(dev) == MEI_PG_ON || 46604bb139aSTomas Winkler dev->pg_event == MEI_PG_EVENT_WAIT) { 4672bf94cabSTomas Winkler dev_dbg(dev->dev, "device is in pg\n"); 46804bb139aSTomas Winkler return false; 46904bb139aSTomas Winkler } 47004bb139aSTomas Winkler 4716aae48ffSTomas Winkler if (!dev->hbuf_is_ready) { 4722bf94cabSTomas Winkler dev_dbg(dev->dev, "hbuf is not ready\n"); 4736aae48ffSTomas Winkler return false; 4746aae48ffSTomas Winkler } 4756aae48ffSTomas Winkler 4766aae48ffSTomas Winkler dev->hbuf_is_ready = false; 4776aae48ffSTomas Winkler 4786aae48ffSTomas Winkler return true; 4796aae48ffSTomas Winkler } 4809ca9050bSTomas Winkler 4819ca9050bSTomas Winkler /** 48283ce0741SAlexander Usyskin * mei_cl_disconnect - disconnect host client from the me one 4839ca9050bSTomas Winkler * 48490e0b5f1STomas Winkler * @cl: host client 4859ca9050bSTomas Winkler * 4869ca9050bSTomas Winkler * Locking: called under "dev->device_lock" lock 4879ca9050bSTomas Winkler * 4889ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 4899ca9050bSTomas Winkler */ 49090e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl) 4919ca9050bSTomas Winkler { 49290e0b5f1STomas Winkler struct mei_device *dev; 4939ca9050bSTomas Winkler struct mei_cl_cb *cb; 494fe2f17ebSAlexander Usyskin int rets; 4959ca9050bSTomas Winkler 49690e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 4979ca9050bSTomas Winkler return -ENODEV; 4989ca9050bSTomas Winkler 49990e0b5f1STomas Winkler dev = cl->dev; 50090e0b5f1STomas Winkler 501c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "disconnecting"); 502c0abffbdSAlexander Usyskin 5039ca9050bSTomas Winkler if (cl->state != MEI_FILE_DISCONNECTING) 5049ca9050bSTomas Winkler return 0; 5059ca9050bSTomas Winkler 5062bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 50704bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 5082bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 50904bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 51004bb139aSTomas Winkler return rets; 51104bb139aSTomas Winkler } 51204bb139aSTomas Winkler 5139ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 51404bb139aSTomas Winkler if (!cb) { 51504bb139aSTomas Winkler rets = -ENOMEM; 51604bb139aSTomas Winkler goto free; 51704bb139aSTomas Winkler } 5189ca9050bSTomas Winkler 5195a8373fbSTomas Winkler cb->fop_type = MEI_FOP_DISCONNECT; 5205a8373fbSTomas Winkler 5216aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 5229ca9050bSTomas Winkler if (mei_hbm_cl_disconnect_req(dev, cl)) { 5239ca9050bSTomas Winkler rets = -ENODEV; 524c0abffbdSAlexander Usyskin cl_err(dev, cl, "failed to disconnect.\n"); 5259ca9050bSTomas Winkler goto free; 5269ca9050bSTomas Winkler } 52722b987a3SAlexander Usyskin cl->timer_count = MEI_CONNECT_TIMEOUT; 5289ca9050bSTomas Winkler mdelay(10); /* Wait for hardware disconnection ready */ 5299ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 5309ca9050bSTomas Winkler } else { 531c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 5329ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 5339ca9050bSTomas Winkler 5349ca9050bSTomas Winkler } 5359ca9050bSTomas Winkler mutex_unlock(&dev->device_lock); 5369ca9050bSTomas Winkler 53712f45ed4STomas Winkler wait_event_timeout(cl->wait, 5389ca9050bSTomas Winkler MEI_FILE_DISCONNECTED == cl->state, 5399ca9050bSTomas Winkler mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 5409ca9050bSTomas Winkler 5419ca9050bSTomas Winkler mutex_lock(&dev->device_lock); 542fe2f17ebSAlexander Usyskin 5439ca9050bSTomas Winkler if (MEI_FILE_DISCONNECTED == cl->state) { 5449ca9050bSTomas Winkler rets = 0; 545c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 5469ca9050bSTomas Winkler } else { 547fe2f17ebSAlexander Usyskin cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 548fe2f17ebSAlexander Usyskin rets = -ETIME; 5499ca9050bSTomas Winkler } 5509ca9050bSTomas Winkler 5519ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 5529ca9050bSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 5539ca9050bSTomas Winkler free: 55404bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 5552bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 5562bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 55704bb139aSTomas Winkler 5589ca9050bSTomas Winkler mei_io_cb_free(cb); 5599ca9050bSTomas Winkler return rets; 5609ca9050bSTomas Winkler } 5619ca9050bSTomas Winkler 5629ca9050bSTomas Winkler 5639ca9050bSTomas Winkler /** 56490e0b5f1STomas Winkler * mei_cl_is_other_connecting - checks if other 56590e0b5f1STomas Winkler * client with the same me client id is connecting 5669ca9050bSTomas Winkler * 5679ca9050bSTomas Winkler * @cl: private data of the file object 5689ca9050bSTomas Winkler * 56983ce0741SAlexander Usyskin * returns true if other client is connected, false - otherwise. 5709ca9050bSTomas Winkler */ 57190e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl) 5729ca9050bSTomas Winkler { 57390e0b5f1STomas Winkler struct mei_device *dev; 57431f88f57STomas Winkler struct mei_cl *ocl; /* the other client */ 5759ca9050bSTomas Winkler 57690e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 57790e0b5f1STomas Winkler return false; 57890e0b5f1STomas Winkler 57990e0b5f1STomas Winkler dev = cl->dev; 58090e0b5f1STomas Winkler 58131f88f57STomas Winkler list_for_each_entry(ocl, &dev->file_list, link) { 58231f88f57STomas Winkler if (ocl->state == MEI_FILE_CONNECTING && 58331f88f57STomas Winkler ocl != cl && 58431f88f57STomas Winkler cl->me_client_id == ocl->me_client_id) 58590e0b5f1STomas Winkler return true; 5869ca9050bSTomas Winkler 5879ca9050bSTomas Winkler } 58890e0b5f1STomas Winkler 58990e0b5f1STomas Winkler return false; 5909ca9050bSTomas Winkler } 5919ca9050bSTomas Winkler 5929ca9050bSTomas Winkler /** 59383ce0741SAlexander Usyskin * mei_cl_connect - connect host client to the me one 5949f81abdaSTomas Winkler * 5959f81abdaSTomas Winkler * @cl: host client 5969f81abdaSTomas Winkler * 5979f81abdaSTomas Winkler * Locking: called under "dev->device_lock" lock 5989f81abdaSTomas Winkler * 5999f81abdaSTomas Winkler * returns 0 on success, <0 on failure. 6009f81abdaSTomas Winkler */ 6019f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file) 6029f81abdaSTomas Winkler { 6039f81abdaSTomas Winkler struct mei_device *dev; 6049f81abdaSTomas Winkler struct mei_cl_cb *cb; 6059f81abdaSTomas Winkler int rets; 6069f81abdaSTomas Winkler 6079f81abdaSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 6089f81abdaSTomas Winkler return -ENODEV; 6099f81abdaSTomas Winkler 6109f81abdaSTomas Winkler dev = cl->dev; 6119f81abdaSTomas Winkler 6122bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 61304bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 6142bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 61504bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 61604bb139aSTomas Winkler return rets; 61704bb139aSTomas Winkler } 61804bb139aSTomas Winkler 6199f81abdaSTomas Winkler cb = mei_io_cb_init(cl, file); 6209f81abdaSTomas Winkler if (!cb) { 6219f81abdaSTomas Winkler rets = -ENOMEM; 6229f81abdaSTomas Winkler goto out; 6239f81abdaSTomas Winkler } 6249f81abdaSTomas Winkler 62502a7eeccSTomas Winkler cb->fop_type = MEI_FOP_CONNECT; 6269f81abdaSTomas Winkler 6276aae48ffSTomas Winkler /* run hbuf acquire last so we don't have to undo */ 6286aae48ffSTomas Winkler if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { 629e4d8270eSAlexander Usyskin cl->state = MEI_FILE_CONNECTING; 6309f81abdaSTomas Winkler if (mei_hbm_cl_connect_req(dev, cl)) { 6319f81abdaSTomas Winkler rets = -ENODEV; 6329f81abdaSTomas Winkler goto out; 6339f81abdaSTomas Winkler } 6349f81abdaSTomas Winkler cl->timer_count = MEI_CONNECT_TIMEOUT; 6359f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 6369f81abdaSTomas Winkler } else { 63773ab4232SAlexander Usyskin cl->state = MEI_FILE_INITIALIZING; 6389f81abdaSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 6399f81abdaSTomas Winkler } 6409f81abdaSTomas Winkler 6419f81abdaSTomas Winkler mutex_unlock(&dev->device_lock); 64212f45ed4STomas Winkler wait_event_timeout(cl->wait, 6439f81abdaSTomas Winkler (cl->state == MEI_FILE_CONNECTED || 6449f81abdaSTomas Winkler cl->state == MEI_FILE_DISCONNECTED), 645206ecfc2SFrode Isaksen mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 6469f81abdaSTomas Winkler mutex_lock(&dev->device_lock); 6479f81abdaSTomas Winkler 6489f81abdaSTomas Winkler if (cl->state != MEI_FILE_CONNECTED) { 6493e37ebb7SAlexander Usyskin cl->state = MEI_FILE_DISCONNECTED; 650285e2996SAlexander Usyskin /* something went really wrong */ 651285e2996SAlexander Usyskin if (!cl->status) 652285e2996SAlexander Usyskin cl->status = -EFAULT; 6539f81abdaSTomas Winkler 6549f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_rd_list, cl); 6559f81abdaSTomas Winkler mei_io_list_flush(&dev->ctrl_wr_list, cl); 6569f81abdaSTomas Winkler } 6579f81abdaSTomas Winkler 6589f81abdaSTomas Winkler rets = cl->status; 6599f81abdaSTomas Winkler 6609f81abdaSTomas Winkler out: 66104bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 6622bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 6632bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 66404bb139aSTomas Winkler 6659f81abdaSTomas Winkler mei_io_cb_free(cb); 6669f81abdaSTomas Winkler return rets; 6679f81abdaSTomas Winkler } 6689f81abdaSTomas Winkler 6699f81abdaSTomas Winkler /** 67090e0b5f1STomas Winkler * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. 6719ca9050bSTomas Winkler * 6729ca9050bSTomas Winkler * @cl: private data of the file object 6739ca9050bSTomas Winkler * 6749ca9050bSTomas Winkler * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. 6759ca9050bSTomas Winkler * -ENOENT if mei_cl is not present 6769ca9050bSTomas Winkler * -EINVAL if single_recv_buf == 0 6779ca9050bSTomas Winkler */ 67890e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl) 6799ca9050bSTomas Winkler { 68090e0b5f1STomas Winkler struct mei_device *dev; 68112d00665SAlexander Usyskin struct mei_me_client *me_cl; 6829ca9050bSTomas Winkler 68390e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 68490e0b5f1STomas Winkler return -EINVAL; 68590e0b5f1STomas Winkler 68690e0b5f1STomas Winkler dev = cl->dev; 68790e0b5f1STomas Winkler 6889ca9050bSTomas Winkler if (cl->mei_flow_ctrl_creds > 0) 6899ca9050bSTomas Winkler return 1; 6909ca9050bSTomas Winkler 691d320832fSTomas Winkler me_cl = mei_me_cl_by_id(dev, cl->me_client_id); 692d320832fSTomas Winkler if (!me_cl) { 69312d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 694d320832fSTomas Winkler return -ENOENT; 69512d00665SAlexander Usyskin } 69612d00665SAlexander Usyskin 6979ca9050bSTomas Winkler if (me_cl->mei_flow_ctrl_creds) { 6989ca9050bSTomas Winkler if (WARN_ON(me_cl->props.single_recv_buf == 0)) 6999ca9050bSTomas Winkler return -EINVAL; 7009ca9050bSTomas Winkler return 1; 70112d00665SAlexander Usyskin } 7029ca9050bSTomas Winkler return 0; 7039ca9050bSTomas Winkler } 7049ca9050bSTomas Winkler 7059ca9050bSTomas Winkler /** 70690e0b5f1STomas Winkler * mei_cl_flow_ctrl_reduce - reduces flow_control. 7079ca9050bSTomas Winkler * 7089ca9050bSTomas Winkler * @cl: private data of the file object 709393b148fSMasanari Iida * 7109ca9050bSTomas Winkler * @returns 7119ca9050bSTomas Winkler * 0 on success 7129ca9050bSTomas Winkler * -ENOENT when me client is not found 7139ca9050bSTomas Winkler * -EINVAL when ctrl credits are <= 0 7149ca9050bSTomas Winkler */ 71590e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) 7169ca9050bSTomas Winkler { 71790e0b5f1STomas Winkler struct mei_device *dev; 71812d00665SAlexander Usyskin struct mei_me_client *me_cl; 7199ca9050bSTomas Winkler 72090e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 72190e0b5f1STomas Winkler return -EINVAL; 72290e0b5f1STomas Winkler 72390e0b5f1STomas Winkler dev = cl->dev; 72490e0b5f1STomas Winkler 725d320832fSTomas Winkler me_cl = mei_me_cl_by_id(dev, cl->me_client_id); 726d320832fSTomas Winkler if (!me_cl) { 72712d00665SAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 728d320832fSTomas Winkler return -ENOENT; 72912d00665SAlexander Usyskin } 7309ca9050bSTomas Winkler 731d320832fSTomas Winkler if (me_cl->props.single_recv_buf) { 7329ca9050bSTomas Winkler if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) 7339ca9050bSTomas Winkler return -EINVAL; 73412d00665SAlexander Usyskin me_cl->mei_flow_ctrl_creds--; 7359ca9050bSTomas Winkler } else { 7369ca9050bSTomas Winkler if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) 7379ca9050bSTomas Winkler return -EINVAL; 7389ca9050bSTomas Winkler cl->mei_flow_ctrl_creds--; 7399ca9050bSTomas Winkler } 7409ca9050bSTomas Winkler return 0; 7419ca9050bSTomas Winkler } 7429ca9050bSTomas Winkler 7439ca9050bSTomas Winkler /** 744393b148fSMasanari Iida * mei_cl_read_start - the start read client message function. 7459ca9050bSTomas Winkler * 74690e0b5f1STomas Winkler * @cl: host client 7479ca9050bSTomas Winkler * 7489ca9050bSTomas Winkler * returns 0 on success, <0 on failure. 7499ca9050bSTomas Winkler */ 750fcb136e1STomas Winkler int mei_cl_read_start(struct mei_cl *cl, size_t length) 7519ca9050bSTomas Winkler { 75290e0b5f1STomas Winkler struct mei_device *dev; 7539ca9050bSTomas Winkler struct mei_cl_cb *cb; 754d320832fSTomas Winkler struct mei_me_client *me_cl; 7559ca9050bSTomas Winkler int rets; 7569ca9050bSTomas Winkler 75790e0b5f1STomas Winkler if (WARN_ON(!cl || !cl->dev)) 75890e0b5f1STomas Winkler return -ENODEV; 75990e0b5f1STomas Winkler 76090e0b5f1STomas Winkler dev = cl->dev; 76190e0b5f1STomas Winkler 762b950ac1dSTomas Winkler if (!mei_cl_is_connected(cl)) 7639ca9050bSTomas Winkler return -ENODEV; 7649ca9050bSTomas Winkler 765d91aaed3STomas Winkler if (cl->read_cb) { 766c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "read is pending.\n"); 7679ca9050bSTomas Winkler return -EBUSY; 7689ca9050bSTomas Winkler } 769d880f329STomas Winkler me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); 770d320832fSTomas Winkler if (!me_cl) { 771c0abffbdSAlexander Usyskin cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); 7727ca96aa2SAlexander Usyskin return -ENOTTY; 7739ca9050bSTomas Winkler } 7749ca9050bSTomas Winkler 7752bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 77604bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 7772bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 77804bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 77904bb139aSTomas Winkler return rets; 78004bb139aSTomas Winkler } 78104bb139aSTomas Winkler 7829ca9050bSTomas Winkler cb = mei_io_cb_init(cl, NULL); 78304bb139aSTomas Winkler if (!cb) { 78404bb139aSTomas Winkler rets = -ENOMEM; 78504bb139aSTomas Winkler goto out; 78604bb139aSTomas Winkler } 7879ca9050bSTomas Winkler 788fcb136e1STomas Winkler /* always allocate at least client max message */ 789d320832fSTomas Winkler length = max_t(size_t, length, me_cl->props.max_msg_length); 790fcb136e1STomas Winkler rets = mei_io_cb_alloc_resp_buf(cb, length); 7919ca9050bSTomas Winkler if (rets) 79204bb139aSTomas Winkler goto out; 7939ca9050bSTomas Winkler 7949ca9050bSTomas Winkler cb->fop_type = MEI_FOP_READ; 7956aae48ffSTomas Winkler if (mei_hbuf_acquire(dev)) { 79686113500SAlexander Usyskin rets = mei_hbm_cl_flow_control_req(dev, cl); 79786113500SAlexander Usyskin if (rets < 0) 79804bb139aSTomas Winkler goto out; 79904bb139aSTomas Winkler 8009ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->read_list.list); 8019ca9050bSTomas Winkler } else { 8029ca9050bSTomas Winkler list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 8039ca9050bSTomas Winkler } 804accb884bSChao Bi 805accb884bSChao Bi cl->read_cb = cb; 806accb884bSChao Bi 80704bb139aSTomas Winkler out: 80804bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 8092bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 8102bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 81104bb139aSTomas Winkler 81204bb139aSTomas Winkler if (rets) 8139ca9050bSTomas Winkler mei_io_cb_free(cb); 81404bb139aSTomas Winkler 8159ca9050bSTomas Winkler return rets; 8169ca9050bSTomas Winkler } 8179ca9050bSTomas Winkler 818074b4c01STomas Winkler /** 8199d098192STomas Winkler * mei_cl_irq_write - write a message to device 82021767546STomas Winkler * from the interrupt thread context 82121767546STomas Winkler * 82221767546STomas Winkler * @cl: client 82321767546STomas Winkler * @cb: callback block. 82421767546STomas Winkler * @cmpl_list: complete list. 82521767546STomas Winkler * 82621767546STomas Winkler * returns 0, OK; otherwise error. 82721767546STomas Winkler */ 8289d098192STomas Winkler int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, 8299d098192STomas Winkler struct mei_cl_cb *cmpl_list) 83021767546STomas Winkler { 831136698e5STomas Winkler struct mei_device *dev; 832136698e5STomas Winkler struct mei_msg_data *buf; 83321767546STomas Winkler struct mei_msg_hdr mei_hdr; 834136698e5STomas Winkler size_t len; 835136698e5STomas Winkler u32 msg_slots; 8369d098192STomas Winkler int slots; 8372ebf8c94STomas Winkler int rets; 83821767546STomas Winkler 839136698e5STomas Winkler if (WARN_ON(!cl || !cl->dev)) 840136698e5STomas Winkler return -ENODEV; 841136698e5STomas Winkler 842136698e5STomas Winkler dev = cl->dev; 843136698e5STomas Winkler 844136698e5STomas Winkler buf = &cb->request_buffer; 845136698e5STomas Winkler 846136698e5STomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 847136698e5STomas Winkler if (rets < 0) 848136698e5STomas Winkler return rets; 849136698e5STomas Winkler 850136698e5STomas Winkler if (rets == 0) { 851136698e5STomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 852136698e5STomas Winkler return 0; 853136698e5STomas Winkler } 854136698e5STomas Winkler 8559d098192STomas Winkler slots = mei_hbuf_empty_slots(dev); 856136698e5STomas Winkler len = buf->size - cb->buf_idx; 857136698e5STomas Winkler msg_slots = mei_data2slots(len); 858136698e5STomas Winkler 85921767546STomas Winkler mei_hdr.host_addr = cl->host_client_id; 86021767546STomas Winkler mei_hdr.me_addr = cl->me_client_id; 86121767546STomas Winkler mei_hdr.reserved = 0; 862479327fcSTomas Winkler mei_hdr.internal = cb->internal; 86321767546STomas Winkler 8649d098192STomas Winkler if (slots >= msg_slots) { 86521767546STomas Winkler mei_hdr.length = len; 86621767546STomas Winkler mei_hdr.msg_complete = 1; 86721767546STomas Winkler /* Split the message only if we can write the whole host buffer */ 8689d098192STomas Winkler } else if (slots == dev->hbuf_depth) { 8699d098192STomas Winkler msg_slots = slots; 8709d098192STomas Winkler len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); 87121767546STomas Winkler mei_hdr.length = len; 87221767546STomas Winkler mei_hdr.msg_complete = 0; 87321767546STomas Winkler } else { 87421767546STomas Winkler /* wait for next time the host buffer is empty */ 87521767546STomas Winkler return 0; 87621767546STomas Winkler } 87721767546STomas Winkler 878c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", 87921767546STomas Winkler cb->request_buffer.size, cb->buf_idx); 88021767546STomas Winkler 881136698e5STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); 8822ebf8c94STomas Winkler if (rets) { 8832ebf8c94STomas Winkler cl->status = rets; 88421767546STomas Winkler list_move_tail(&cb->list, &cmpl_list->list); 8852ebf8c94STomas Winkler return rets; 88621767546STomas Winkler } 88721767546STomas Winkler 88821767546STomas Winkler cl->status = 0; 8894dfaa9f7STomas Winkler cl->writing_state = MEI_WRITING; 89021767546STomas Winkler cb->buf_idx += mei_hdr.length; 8914dfaa9f7STomas Winkler 89221767546STomas Winkler if (mei_hdr.msg_complete) { 89321767546STomas Winkler if (mei_cl_flow_ctrl_reduce(cl)) 8942ebf8c94STomas Winkler return -EIO; 89521767546STomas Winkler list_move_tail(&cb->list, &dev->write_waiting_list.list); 89621767546STomas Winkler } 89721767546STomas Winkler 89821767546STomas Winkler return 0; 89921767546STomas Winkler } 90021767546STomas Winkler 90121767546STomas Winkler /** 9024234a6deSTomas Winkler * mei_cl_write - submit a write cb to mei device 9034234a6deSTomas Winkler assumes device_lock is locked 9044234a6deSTomas Winkler * 9054234a6deSTomas Winkler * @cl: host client 9064234a6deSTomas Winkler * @cl: write callback with filled data 9074234a6deSTomas Winkler * 90883ce0741SAlexander Usyskin * returns number of bytes sent on success, <0 on failure. 9094234a6deSTomas Winkler */ 9104234a6deSTomas Winkler int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) 9114234a6deSTomas Winkler { 9124234a6deSTomas Winkler struct mei_device *dev; 9134234a6deSTomas Winkler struct mei_msg_data *buf; 9144234a6deSTomas Winkler struct mei_msg_hdr mei_hdr; 9154234a6deSTomas Winkler int rets; 9164234a6deSTomas Winkler 9174234a6deSTomas Winkler 9184234a6deSTomas Winkler if (WARN_ON(!cl || !cl->dev)) 9194234a6deSTomas Winkler return -ENODEV; 9204234a6deSTomas Winkler 9214234a6deSTomas Winkler if (WARN_ON(!cb)) 9224234a6deSTomas Winkler return -EINVAL; 9234234a6deSTomas Winkler 9244234a6deSTomas Winkler dev = cl->dev; 9254234a6deSTomas Winkler 9264234a6deSTomas Winkler 9274234a6deSTomas Winkler buf = &cb->request_buffer; 9284234a6deSTomas Winkler 9290a01e974SAlexander Usyskin cl_dbg(dev, cl, "size=%d\n", buf->size); 9304234a6deSTomas Winkler 9312bf94cabSTomas Winkler rets = pm_runtime_get(dev->dev); 93204bb139aSTomas Winkler if (rets < 0 && rets != -EINPROGRESS) { 9332bf94cabSTomas Winkler pm_runtime_put_noidle(dev->dev); 93404bb139aSTomas Winkler cl_err(dev, cl, "rpm: get failed %d\n", rets); 93504bb139aSTomas Winkler return rets; 93604bb139aSTomas Winkler } 9374234a6deSTomas Winkler 9384234a6deSTomas Winkler cb->fop_type = MEI_FOP_WRITE; 9396aae48ffSTomas Winkler cb->buf_idx = 0; 9406aae48ffSTomas Winkler cl->writing_state = MEI_IDLE; 9416aae48ffSTomas Winkler 9426aae48ffSTomas Winkler mei_hdr.host_addr = cl->host_client_id; 9436aae48ffSTomas Winkler mei_hdr.me_addr = cl->me_client_id; 9446aae48ffSTomas Winkler mei_hdr.reserved = 0; 9456aae48ffSTomas Winkler mei_hdr.msg_complete = 0; 9466aae48ffSTomas Winkler mei_hdr.internal = cb->internal; 9474234a6deSTomas Winkler 9484234a6deSTomas Winkler rets = mei_cl_flow_ctrl_creds(cl); 9494234a6deSTomas Winkler if (rets < 0) 9504234a6deSTomas Winkler goto err; 9514234a6deSTomas Winkler 9526aae48ffSTomas Winkler if (rets == 0) { 9536aae48ffSTomas Winkler cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); 9544234a6deSTomas Winkler rets = buf->size; 9554234a6deSTomas Winkler goto out; 9564234a6deSTomas Winkler } 9576aae48ffSTomas Winkler if (!mei_hbuf_acquire(dev)) { 9586aae48ffSTomas Winkler cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); 9596aae48ffSTomas Winkler rets = buf->size; 9606aae48ffSTomas Winkler goto out; 9616aae48ffSTomas Winkler } 9624234a6deSTomas Winkler 9634234a6deSTomas Winkler /* Check for a maximum length */ 9644234a6deSTomas Winkler if (buf->size > mei_hbuf_max_len(dev)) { 9654234a6deSTomas Winkler mei_hdr.length = mei_hbuf_max_len(dev); 9664234a6deSTomas Winkler mei_hdr.msg_complete = 0; 9674234a6deSTomas Winkler } else { 9684234a6deSTomas Winkler mei_hdr.length = buf->size; 9694234a6deSTomas Winkler mei_hdr.msg_complete = 1; 9704234a6deSTomas Winkler } 9714234a6deSTomas Winkler 9722ebf8c94STomas Winkler rets = mei_write_message(dev, &mei_hdr, buf->data); 9732ebf8c94STomas Winkler if (rets) 9744234a6deSTomas Winkler goto err; 9754234a6deSTomas Winkler 9764234a6deSTomas Winkler cl->writing_state = MEI_WRITING; 9774234a6deSTomas Winkler cb->buf_idx = mei_hdr.length; 9784234a6deSTomas Winkler 9794234a6deSTomas Winkler out: 9804234a6deSTomas Winkler if (mei_hdr.msg_complete) { 9817ca96aa2SAlexander Usyskin rets = mei_cl_flow_ctrl_reduce(cl); 9827ca96aa2SAlexander Usyskin if (rets < 0) 9834234a6deSTomas Winkler goto err; 9847ca96aa2SAlexander Usyskin 9854234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_waiting_list.list); 9864234a6deSTomas Winkler } else { 9874234a6deSTomas Winkler list_add_tail(&cb->list, &dev->write_list.list); 9884234a6deSTomas Winkler } 9894234a6deSTomas Winkler 9904234a6deSTomas Winkler 9914234a6deSTomas Winkler if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { 9924234a6deSTomas Winkler 9934234a6deSTomas Winkler mutex_unlock(&dev->device_lock); 9947ca96aa2SAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 9957ca96aa2SAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE); 9967ca96aa2SAlexander Usyskin mutex_lock(&dev->device_lock); 9977ca96aa2SAlexander Usyskin /* wait_event_interruptible returns -ERESTARTSYS */ 9987ca96aa2SAlexander Usyskin if (rets) { 9994234a6deSTomas Winkler if (signal_pending(current)) 10004234a6deSTomas Winkler rets = -EINTR; 10017ca96aa2SAlexander Usyskin goto err; 10024234a6deSTomas Winkler } 10034234a6deSTomas Winkler } 10047ca96aa2SAlexander Usyskin 10057ca96aa2SAlexander Usyskin rets = buf->size; 10064234a6deSTomas Winkler err: 100704bb139aSTomas Winkler cl_dbg(dev, cl, "rpm: autosuspend\n"); 10082bf94cabSTomas Winkler pm_runtime_mark_last_busy(dev->dev); 10092bf94cabSTomas Winkler pm_runtime_put_autosuspend(dev->dev); 101004bb139aSTomas Winkler 10114234a6deSTomas Winkler return rets; 10124234a6deSTomas Winkler } 10134234a6deSTomas Winkler 10144234a6deSTomas Winkler 1015db086fa9STomas Winkler /** 1016db086fa9STomas Winkler * mei_cl_complete - processes completed operation for a client 1017db086fa9STomas Winkler * 1018db086fa9STomas Winkler * @cl: private data of the file object. 1019db086fa9STomas Winkler * @cb: callback block. 1020db086fa9STomas Winkler */ 1021db086fa9STomas Winkler void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1022db086fa9STomas Winkler { 1023db086fa9STomas Winkler if (cb->fop_type == MEI_FOP_WRITE) { 1024db086fa9STomas Winkler mei_io_cb_free(cb); 1025db086fa9STomas Winkler cb = NULL; 1026db086fa9STomas Winkler cl->writing_state = MEI_WRITE_COMPLETE; 1027db086fa9STomas Winkler if (waitqueue_active(&cl->tx_wait)) 1028db086fa9STomas Winkler wake_up_interruptible(&cl->tx_wait); 1029db086fa9STomas Winkler 1030db086fa9STomas Winkler } else if (cb->fop_type == MEI_FOP_READ && 1031db086fa9STomas Winkler MEI_READING == cl->reading_state) { 1032db086fa9STomas Winkler cl->reading_state = MEI_READ_COMPLETE; 1033db086fa9STomas Winkler if (waitqueue_active(&cl->rx_wait)) 1034db086fa9STomas Winkler wake_up_interruptible(&cl->rx_wait); 1035db086fa9STomas Winkler else 1036db086fa9STomas Winkler mei_cl_bus_rx_event(cl); 1037db086fa9STomas Winkler 1038db086fa9STomas Winkler } 1039db086fa9STomas Winkler } 1040db086fa9STomas Winkler 10414234a6deSTomas Winkler 10424234a6deSTomas Winkler /** 1043074b4c01STomas Winkler * mei_cl_all_disconnect - disconnect forcefully all connected clients 1044074b4c01STomas Winkler * 1045074b4c01STomas Winkler * @dev - mei device 1046074b4c01STomas Winkler */ 1047074b4c01STomas Winkler 1048074b4c01STomas Winkler void mei_cl_all_disconnect(struct mei_device *dev) 1049074b4c01STomas Winkler { 105031f88f57STomas Winkler struct mei_cl *cl; 1051074b4c01STomas Winkler 105231f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1053074b4c01STomas Winkler cl->state = MEI_FILE_DISCONNECTED; 1054074b4c01STomas Winkler cl->mei_flow_ctrl_creds = 0; 1055074b4c01STomas Winkler cl->timer_count = 0; 1056074b4c01STomas Winkler } 1057074b4c01STomas Winkler } 1058074b4c01STomas Winkler 1059074b4c01STomas Winkler 1060074b4c01STomas Winkler /** 10615290801cSTomas Winkler * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted 1062074b4c01STomas Winkler * 1063074b4c01STomas Winkler * @dev - mei device 1064074b4c01STomas Winkler */ 10655290801cSTomas Winkler void mei_cl_all_wakeup(struct mei_device *dev) 1066074b4c01STomas Winkler { 106731f88f57STomas Winkler struct mei_cl *cl; 106892db1555STomas Winkler 106931f88f57STomas Winkler list_for_each_entry(cl, &dev->file_list, link) { 1070074b4c01STomas Winkler if (waitqueue_active(&cl->rx_wait)) { 1071c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up reading client!\n"); 1072074b4c01STomas Winkler wake_up_interruptible(&cl->rx_wait); 1073074b4c01STomas Winkler } 10745290801cSTomas Winkler if (waitqueue_active(&cl->tx_wait)) { 1075c0abffbdSAlexander Usyskin cl_dbg(dev, cl, "Waking up writing client!\n"); 10765290801cSTomas Winkler wake_up_interruptible(&cl->tx_wait); 10775290801cSTomas Winkler } 1078074b4c01STomas Winkler } 1079074b4c01STomas Winkler } 1080074b4c01STomas Winkler 1081074b4c01STomas Winkler /** 1082074b4c01STomas Winkler * mei_cl_all_write_clear - clear all pending writes 1083074b4c01STomas Winkler 1084074b4c01STomas Winkler * @dev - mei device 1085074b4c01STomas Winkler */ 1086074b4c01STomas Winkler void mei_cl_all_write_clear(struct mei_device *dev) 1087074b4c01STomas Winkler { 1088cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_list, NULL); 1089cc99ecfdSTomas Winkler mei_io_list_free(&dev->write_waiting_list, NULL); 1090074b4c01STomas Winkler } 1091074b4c01STomas Winkler 1092074b4c01STomas Winkler 1093