xref: /openbmc/linux/drivers/misc/mei/client.c (revision 781d0d89)
19ca9050bSTomas Winkler /*
29ca9050bSTomas Winkler  *
39ca9050bSTomas Winkler  * Intel Management Engine Interface (Intel MEI) Linux driver
49ca9050bSTomas Winkler  * Copyright (c) 2003-2012, Intel Corporation.
59ca9050bSTomas Winkler  *
69ca9050bSTomas Winkler  * This program is free software; you can redistribute it and/or modify it
79ca9050bSTomas Winkler  * under the terms and conditions of the GNU General Public License,
89ca9050bSTomas Winkler  * version 2, as published by the Free Software Foundation.
99ca9050bSTomas Winkler  *
109ca9050bSTomas Winkler  * This program is distributed in the hope it will be useful, but WITHOUT
119ca9050bSTomas Winkler  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
129ca9050bSTomas Winkler  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
139ca9050bSTomas Winkler  * more details.
149ca9050bSTomas Winkler  *
159ca9050bSTomas Winkler  */
169ca9050bSTomas Winkler 
179ca9050bSTomas Winkler #include <linux/pci.h>
189ca9050bSTomas Winkler #include <linux/sched.h>
199ca9050bSTomas Winkler #include <linux/wait.h>
209ca9050bSTomas Winkler #include <linux/delay.h>
219ca9050bSTomas Winkler 
229ca9050bSTomas Winkler #include <linux/mei.h>
239ca9050bSTomas Winkler 
249ca9050bSTomas Winkler #include "mei_dev.h"
259ca9050bSTomas Winkler #include "hbm.h"
2690e0b5f1STomas Winkler #include "client.h"
2790e0b5f1STomas Winkler 
2890e0b5f1STomas Winkler /**
2990e0b5f1STomas Winkler  * mei_me_cl_by_uuid - locate index of me client
3090e0b5f1STomas Winkler  *
3190e0b5f1STomas Winkler  * @dev: mei device
3290e0b5f1STomas Winkler  * returns me client index or -ENOENT if not found
3390e0b5f1STomas Winkler  */
3490e0b5f1STomas Winkler int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
3590e0b5f1STomas Winkler {
3690e0b5f1STomas Winkler 	int i, res = -ENOENT;
3790e0b5f1STomas Winkler 
3890e0b5f1STomas Winkler 	for (i = 0; i < dev->me_clients_num; ++i)
3990e0b5f1STomas Winkler 		if (uuid_le_cmp(*uuid,
4090e0b5f1STomas Winkler 				dev->me_clients[i].props.protocol_name) == 0) {
4190e0b5f1STomas Winkler 			res = i;
4290e0b5f1STomas Winkler 			break;
4390e0b5f1STomas Winkler 		}
4490e0b5f1STomas Winkler 
4590e0b5f1STomas Winkler 	return res;
4690e0b5f1STomas Winkler }
4790e0b5f1STomas Winkler 
4890e0b5f1STomas Winkler 
4990e0b5f1STomas Winkler /**
5090e0b5f1STomas Winkler  * mei_me_cl_by_id return index to me_clients for client_id
5190e0b5f1STomas Winkler  *
5290e0b5f1STomas Winkler  * @dev: the device structure
5390e0b5f1STomas Winkler  * @client_id: me client id
5490e0b5f1STomas Winkler  *
5590e0b5f1STomas Winkler  * Locking: called under "dev->device_lock" lock
5690e0b5f1STomas Winkler  *
5790e0b5f1STomas Winkler  * returns index on success, -ENOENT on failure.
5890e0b5f1STomas Winkler  */
5990e0b5f1STomas Winkler 
6090e0b5f1STomas Winkler int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
6190e0b5f1STomas Winkler {
6290e0b5f1STomas Winkler 	int i;
6390e0b5f1STomas Winkler 	for (i = 0; i < dev->me_clients_num; i++)
6490e0b5f1STomas Winkler 		if (dev->me_clients[i].client_id == client_id)
6590e0b5f1STomas Winkler 			break;
6690e0b5f1STomas Winkler 	if (WARN_ON(dev->me_clients[i].client_id != client_id))
6790e0b5f1STomas Winkler 		return -ENOENT;
6890e0b5f1STomas Winkler 
6990e0b5f1STomas Winkler 	if (i == dev->me_clients_num)
7090e0b5f1STomas Winkler 		return -ENOENT;
7190e0b5f1STomas Winkler 
7290e0b5f1STomas Winkler 	return i;
7390e0b5f1STomas Winkler }
749ca9050bSTomas Winkler 
759ca9050bSTomas Winkler 
769ca9050bSTomas Winkler /**
779ca9050bSTomas Winkler  * mei_io_list_flush - removes list entry belonging to cl.
789ca9050bSTomas Winkler  *
799ca9050bSTomas Winkler  * @list:  An instance of our list structure
809ca9050bSTomas Winkler  * @cl: host client
819ca9050bSTomas Winkler  */
829ca9050bSTomas Winkler void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
839ca9050bSTomas Winkler {
849ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
859ca9050bSTomas Winkler 	struct mei_cl_cb *next;
869ca9050bSTomas Winkler 
879ca9050bSTomas Winkler 	list_for_each_entry_safe(cb, next, &list->list, list) {
889ca9050bSTomas Winkler 		if (cb->cl && mei_cl_cmp_id(cl, cb->cl))
899ca9050bSTomas Winkler 			list_del(&cb->list);
909ca9050bSTomas Winkler 	}
919ca9050bSTomas Winkler }
929ca9050bSTomas Winkler 
939ca9050bSTomas Winkler /**
949ca9050bSTomas Winkler  * mei_io_cb_free - free mei_cb_private related memory
959ca9050bSTomas Winkler  *
969ca9050bSTomas Winkler  * @cb: mei callback struct
979ca9050bSTomas Winkler  */
989ca9050bSTomas Winkler void mei_io_cb_free(struct mei_cl_cb *cb)
999ca9050bSTomas Winkler {
1009ca9050bSTomas Winkler 	if (cb == NULL)
1019ca9050bSTomas Winkler 		return;
1029ca9050bSTomas Winkler 
1039ca9050bSTomas Winkler 	kfree(cb->request_buffer.data);
1049ca9050bSTomas Winkler 	kfree(cb->response_buffer.data);
1059ca9050bSTomas Winkler 	kfree(cb);
1069ca9050bSTomas Winkler }
1079ca9050bSTomas Winkler 
1089ca9050bSTomas Winkler /**
1099ca9050bSTomas Winkler  * mei_io_cb_init - allocate and initialize io callback
1109ca9050bSTomas Winkler  *
1119ca9050bSTomas Winkler  * @cl - mei client
1129ca9050bSTomas Winkler  * @file: pointer to file structure
1139ca9050bSTomas Winkler  *
1149ca9050bSTomas Winkler  * returns mei_cl_cb pointer or NULL;
1159ca9050bSTomas Winkler  */
1169ca9050bSTomas Winkler struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
1179ca9050bSTomas Winkler {
1189ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
1199ca9050bSTomas Winkler 
1209ca9050bSTomas Winkler 	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
1219ca9050bSTomas Winkler 	if (!cb)
1229ca9050bSTomas Winkler 		return NULL;
1239ca9050bSTomas Winkler 
1249ca9050bSTomas Winkler 	mei_io_list_init(cb);
1259ca9050bSTomas Winkler 
1269ca9050bSTomas Winkler 	cb->file_object = fp;
1279ca9050bSTomas Winkler 	cb->cl = cl;
1289ca9050bSTomas Winkler 	cb->buf_idx = 0;
1299ca9050bSTomas Winkler 	return cb;
1309ca9050bSTomas Winkler }
1319ca9050bSTomas Winkler 
1329ca9050bSTomas Winkler /**
1339ca9050bSTomas Winkler  * mei_io_cb_alloc_req_buf - allocate request buffer
1349ca9050bSTomas Winkler  *
1359ca9050bSTomas Winkler  * @cb -  io callback structure
1369ca9050bSTomas Winkler  * @size: size of the buffer
1379ca9050bSTomas Winkler  *
1389ca9050bSTomas Winkler  * returns 0 on success
1399ca9050bSTomas Winkler  *         -EINVAL if cb is NULL
1409ca9050bSTomas Winkler  *         -ENOMEM if allocation failed
1419ca9050bSTomas Winkler  */
1429ca9050bSTomas Winkler int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
1439ca9050bSTomas Winkler {
1449ca9050bSTomas Winkler 	if (!cb)
1459ca9050bSTomas Winkler 		return -EINVAL;
1469ca9050bSTomas Winkler 
1479ca9050bSTomas Winkler 	if (length == 0)
1489ca9050bSTomas Winkler 		return 0;
1499ca9050bSTomas Winkler 
1509ca9050bSTomas Winkler 	cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
1519ca9050bSTomas Winkler 	if (!cb->request_buffer.data)
1529ca9050bSTomas Winkler 		return -ENOMEM;
1539ca9050bSTomas Winkler 	cb->request_buffer.size = length;
1549ca9050bSTomas Winkler 	return 0;
1559ca9050bSTomas Winkler }
1569ca9050bSTomas Winkler /**
1579ca9050bSTomas Winkler  * mei_io_cb_alloc_req_buf - allocate respose buffer
1589ca9050bSTomas Winkler  *
1599ca9050bSTomas Winkler  * @cb -  io callback structure
1609ca9050bSTomas Winkler  * @size: size of the buffer
1619ca9050bSTomas Winkler  *
1629ca9050bSTomas Winkler  * returns 0 on success
1639ca9050bSTomas Winkler  *         -EINVAL if cb is NULL
1649ca9050bSTomas Winkler  *         -ENOMEM if allocation failed
1659ca9050bSTomas Winkler  */
1669ca9050bSTomas Winkler int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
1679ca9050bSTomas Winkler {
1689ca9050bSTomas Winkler 	if (!cb)
1699ca9050bSTomas Winkler 		return -EINVAL;
1709ca9050bSTomas Winkler 
1719ca9050bSTomas Winkler 	if (length == 0)
1729ca9050bSTomas Winkler 		return 0;
1739ca9050bSTomas Winkler 
1749ca9050bSTomas Winkler 	cb->response_buffer.data = kmalloc(length, GFP_KERNEL);
1759ca9050bSTomas Winkler 	if (!cb->response_buffer.data)
1769ca9050bSTomas Winkler 		return -ENOMEM;
1779ca9050bSTomas Winkler 	cb->response_buffer.size = length;
1789ca9050bSTomas Winkler 	return 0;
1799ca9050bSTomas Winkler }
1809ca9050bSTomas Winkler 
1819ca9050bSTomas Winkler 
1829ca9050bSTomas Winkler 
1839ca9050bSTomas Winkler /**
1849ca9050bSTomas Winkler  * mei_cl_flush_queues - flushes queue lists belonging to cl.
1859ca9050bSTomas Winkler  *
1869ca9050bSTomas Winkler  * @dev: the device structure
1879ca9050bSTomas Winkler  * @cl: host client
1889ca9050bSTomas Winkler  */
1899ca9050bSTomas Winkler int mei_cl_flush_queues(struct mei_cl *cl)
1909ca9050bSTomas Winkler {
19190e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
1929ca9050bSTomas Winkler 		return -EINVAL;
1939ca9050bSTomas Winkler 
1949ca9050bSTomas Winkler 	dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
1959ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->read_list, cl);
1969ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->write_list, cl);
1979ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->write_waiting_list, cl);
1989ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
1999ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
2009ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
2019ca9050bSTomas Winkler 	mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
2029ca9050bSTomas Winkler 	return 0;
2039ca9050bSTomas Winkler }
2049ca9050bSTomas Winkler 
2059ca9050bSTomas Winkler 
2069ca9050bSTomas Winkler /**
2079ca9050bSTomas Winkler  * mei_cl_init - initializes intialize cl.
2089ca9050bSTomas Winkler  *
2099ca9050bSTomas Winkler  * @cl: host client to be initialized
2109ca9050bSTomas Winkler  * @dev: mei device
2119ca9050bSTomas Winkler  */
2129ca9050bSTomas Winkler void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
2139ca9050bSTomas Winkler {
2149ca9050bSTomas Winkler 	memset(cl, 0, sizeof(struct mei_cl));
2159ca9050bSTomas Winkler 	init_waitqueue_head(&cl->wait);
2169ca9050bSTomas Winkler 	init_waitqueue_head(&cl->rx_wait);
2179ca9050bSTomas Winkler 	init_waitqueue_head(&cl->tx_wait);
2189ca9050bSTomas Winkler 	INIT_LIST_HEAD(&cl->link);
2199ca9050bSTomas Winkler 	cl->reading_state = MEI_IDLE;
2209ca9050bSTomas Winkler 	cl->writing_state = MEI_IDLE;
2219ca9050bSTomas Winkler 	cl->dev = dev;
2229ca9050bSTomas Winkler }
2239ca9050bSTomas Winkler 
2249ca9050bSTomas Winkler /**
2259ca9050bSTomas Winkler  * mei_cl_allocate - allocates cl  structure and sets it up.
2269ca9050bSTomas Winkler  *
2279ca9050bSTomas Winkler  * @dev: mei device
2289ca9050bSTomas Winkler  * returns  The allocated file or NULL on failure
2299ca9050bSTomas Winkler  */
2309ca9050bSTomas Winkler struct mei_cl *mei_cl_allocate(struct mei_device *dev)
2319ca9050bSTomas Winkler {
2329ca9050bSTomas Winkler 	struct mei_cl *cl;
2339ca9050bSTomas Winkler 
2349ca9050bSTomas Winkler 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
2359ca9050bSTomas Winkler 	if (!cl)
2369ca9050bSTomas Winkler 		return NULL;
2379ca9050bSTomas Winkler 
2389ca9050bSTomas Winkler 	mei_cl_init(cl, dev);
2399ca9050bSTomas Winkler 
2409ca9050bSTomas Winkler 	return cl;
2419ca9050bSTomas Winkler }
2429ca9050bSTomas Winkler 
24390e0b5f1STomas Winkler /**
24490e0b5f1STomas Winkler  * mei_cl_find_read_cb - find this cl's callback in the read list
24590e0b5f1STomas Winkler  *
24690e0b5f1STomas Winkler  * @dev: device structure
24790e0b5f1STomas Winkler  * returns cb on success, NULL on error
24890e0b5f1STomas Winkler  */
24990e0b5f1STomas Winkler struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
25090e0b5f1STomas Winkler {
25190e0b5f1STomas Winkler 	struct mei_device *dev = cl->dev;
25290e0b5f1STomas Winkler 	struct mei_cl_cb *cb = NULL;
25390e0b5f1STomas Winkler 	struct mei_cl_cb *next = NULL;
25490e0b5f1STomas Winkler 
25590e0b5f1STomas Winkler 	list_for_each_entry_safe(cb, next, &dev->read_list.list, list)
25690e0b5f1STomas Winkler 		if (mei_cl_cmp_id(cl, cb->cl))
25790e0b5f1STomas Winkler 			return cb;
25890e0b5f1STomas Winkler 	return NULL;
25990e0b5f1STomas Winkler }
26090e0b5f1STomas Winkler 
261781d0d89STomas Winkler /** mei_cl_link: allocte host id in the host map
2629ca9050bSTomas Winkler  *
263781d0d89STomas Winkler  * @cl - host client
264781d0d89STomas Winkler  * @id - fixed host id or -1 for genereting one
265781d0d89STomas Winkler  * returns 0 on success
2669ca9050bSTomas Winkler  *	-EINVAL on incorrect values
2679ca9050bSTomas Winkler  *	-ENONET if client not found
2689ca9050bSTomas Winkler  */
269781d0d89STomas Winkler int mei_cl_link(struct mei_cl *cl, int id)
2709ca9050bSTomas Winkler {
27190e0b5f1STomas Winkler 	struct mei_device *dev;
2729ca9050bSTomas Winkler 
273781d0d89STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
2749ca9050bSTomas Winkler 		return -EINVAL;
2759ca9050bSTomas Winkler 
27690e0b5f1STomas Winkler 	dev = cl->dev;
27790e0b5f1STomas Winkler 
278781d0d89STomas Winkler 	/* If Id is not asigned get one*/
279781d0d89STomas Winkler 	if (id == MEI_HOST_CLIENT_ID_ANY)
280781d0d89STomas Winkler 		id = find_first_zero_bit(dev->host_clients_map,
281781d0d89STomas Winkler 					MEI_CLIENTS_MAX);
2829ca9050bSTomas Winkler 
283781d0d89STomas Winkler 	if (id >= MEI_CLIENTS_MAX) {
284781d0d89STomas Winkler 		dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ;
2859ca9050bSTomas Winkler 		return -ENOENT;
2869ca9050bSTomas Winkler 	}
287781d0d89STomas Winkler 
288781d0d89STomas Winkler 	dev->open_handle_count++;
289781d0d89STomas Winkler 
290781d0d89STomas Winkler 	cl->host_client_id = id;
291781d0d89STomas Winkler 	list_add_tail(&cl->link, &dev->file_list);
292781d0d89STomas Winkler 
293781d0d89STomas Winkler 	set_bit(id, dev->host_clients_map);
294781d0d89STomas Winkler 
295781d0d89STomas Winkler 	cl->state = MEI_FILE_INITIALIZING;
296781d0d89STomas Winkler 
297781d0d89STomas Winkler 	dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id);
298781d0d89STomas Winkler 	return 0;
299781d0d89STomas Winkler }
300781d0d89STomas Winkler 
3019ca9050bSTomas Winkler /**
30290e0b5f1STomas Winkler  * mei_cl_unlink - remove me_cl from the list
3039ca9050bSTomas Winkler  *
3049ca9050bSTomas Winkler  * @dev: the device structure
3059ca9050bSTomas Winkler  */
30690e0b5f1STomas Winkler int mei_cl_unlink(struct mei_cl *cl)
3079ca9050bSTomas Winkler {
30890e0b5f1STomas Winkler 	struct mei_device *dev;
3099ca9050bSTomas Winkler 	struct mei_cl *pos, *next;
31090e0b5f1STomas Winkler 
311781d0d89STomas Winkler 	/* don't shout on error exit path */
312781d0d89STomas Winkler 	if (!cl)
313781d0d89STomas Winkler 		return 0;
314781d0d89STomas Winkler 
315781d0d89STomas Winkler 	if (WARN_ON(!cl->dev))
31690e0b5f1STomas Winkler 		return -EINVAL;
31790e0b5f1STomas Winkler 
31890e0b5f1STomas Winkler 	dev = cl->dev;
31990e0b5f1STomas Winkler 
3209ca9050bSTomas Winkler 	list_for_each_entry_safe(pos, next, &dev->file_list, link) {
3219ca9050bSTomas Winkler 		if (cl->host_client_id == pos->host_client_id) {
3229ca9050bSTomas Winkler 			dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
3239ca9050bSTomas Winkler 				pos->host_client_id, pos->me_client_id);
3249ca9050bSTomas Winkler 			list_del_init(&pos->link);
3259ca9050bSTomas Winkler 			break;
3269ca9050bSTomas Winkler 		}
3279ca9050bSTomas Winkler 	}
32890e0b5f1STomas Winkler 	return 0;
3299ca9050bSTomas Winkler }
3309ca9050bSTomas Winkler 
3319ca9050bSTomas Winkler 
3329ca9050bSTomas Winkler void mei_host_client_init(struct work_struct *work)
3339ca9050bSTomas Winkler {
3349ca9050bSTomas Winkler 	struct mei_device *dev = container_of(work,
3359ca9050bSTomas Winkler 					      struct mei_device, init_work);
3369ca9050bSTomas Winkler 	struct mei_client_properties *client_props;
3379ca9050bSTomas Winkler 	int i;
3389ca9050bSTomas Winkler 
3399ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
3409ca9050bSTomas Winkler 
3419ca9050bSTomas Winkler 	bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
3429ca9050bSTomas Winkler 	dev->open_handle_count = 0;
3439ca9050bSTomas Winkler 
3449ca9050bSTomas Winkler 	/*
3459ca9050bSTomas Winkler 	 * Reserving the first three client IDs
3469ca9050bSTomas Winkler 	 * 0: Reserved for MEI Bus Message communications
3479ca9050bSTomas Winkler 	 * 1: Reserved for Watchdog
3489ca9050bSTomas Winkler 	 * 2: Reserved for AMTHI
3499ca9050bSTomas Winkler 	 */
3509ca9050bSTomas Winkler 	bitmap_set(dev->host_clients_map, 0, 3);
3519ca9050bSTomas Winkler 
3529ca9050bSTomas Winkler 	for (i = 0; i < dev->me_clients_num; i++) {
3539ca9050bSTomas Winkler 		client_props = &dev->me_clients[i].props;
3549ca9050bSTomas Winkler 
3551a1aca42STomas Winkler 		if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid))
3569ca9050bSTomas Winkler 			mei_amthif_host_init(dev);
3579ca9050bSTomas Winkler 		else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
3589ca9050bSTomas Winkler 			mei_wd_host_init(dev);
3599ca9050bSTomas Winkler 	}
3609ca9050bSTomas Winkler 
3619ca9050bSTomas Winkler 	dev->dev_state = MEI_DEV_ENABLED;
3629ca9050bSTomas Winkler 
3639ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
3649ca9050bSTomas Winkler }
3659ca9050bSTomas Winkler 
3669ca9050bSTomas Winkler 
3679ca9050bSTomas Winkler /**
36890e0b5f1STomas Winkler  * mei_cl_disconnect - disconnect host clinet form the me one
3699ca9050bSTomas Winkler  *
37090e0b5f1STomas Winkler  * @cl: host client
3719ca9050bSTomas Winkler  *
3729ca9050bSTomas Winkler  * Locking: called under "dev->device_lock" lock
3739ca9050bSTomas Winkler  *
3749ca9050bSTomas Winkler  * returns 0 on success, <0 on failure.
3759ca9050bSTomas Winkler  */
37690e0b5f1STomas Winkler int mei_cl_disconnect(struct mei_cl *cl)
3779ca9050bSTomas Winkler {
37890e0b5f1STomas Winkler 	struct mei_device *dev;
3799ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
3809ca9050bSTomas Winkler 	int rets, err;
3819ca9050bSTomas Winkler 
38290e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
3839ca9050bSTomas Winkler 		return -ENODEV;
3849ca9050bSTomas Winkler 
38590e0b5f1STomas Winkler 	dev = cl->dev;
38690e0b5f1STomas Winkler 
3879ca9050bSTomas Winkler 	if (cl->state != MEI_FILE_DISCONNECTING)
3889ca9050bSTomas Winkler 		return 0;
3899ca9050bSTomas Winkler 
3909ca9050bSTomas Winkler 	cb = mei_io_cb_init(cl, NULL);
3919ca9050bSTomas Winkler 	if (!cb)
3929ca9050bSTomas Winkler 		return -ENOMEM;
3939ca9050bSTomas Winkler 
3949ca9050bSTomas Winkler 	cb->fop_type = MEI_FOP_CLOSE;
3959ca9050bSTomas Winkler 	if (dev->mei_host_buffer_is_empty) {
3969ca9050bSTomas Winkler 		dev->mei_host_buffer_is_empty = false;
3979ca9050bSTomas Winkler 		if (mei_hbm_cl_disconnect_req(dev, cl)) {
3989ca9050bSTomas Winkler 			rets = -ENODEV;
3999ca9050bSTomas Winkler 			dev_err(&dev->pdev->dev, "failed to disconnect.\n");
4009ca9050bSTomas Winkler 			goto free;
4019ca9050bSTomas Winkler 		}
4029ca9050bSTomas Winkler 		mdelay(10); /* Wait for hardware disconnection ready */
4039ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
4049ca9050bSTomas Winkler 	} else {
4059ca9050bSTomas Winkler 		dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
4069ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
4079ca9050bSTomas Winkler 
4089ca9050bSTomas Winkler 	}
4099ca9050bSTomas Winkler 	mutex_unlock(&dev->device_lock);
4109ca9050bSTomas Winkler 
4119ca9050bSTomas Winkler 	err = wait_event_timeout(dev->wait_recvd_msg,
4129ca9050bSTomas Winkler 			MEI_FILE_DISCONNECTED == cl->state,
4139ca9050bSTomas Winkler 			mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
4149ca9050bSTomas Winkler 
4159ca9050bSTomas Winkler 	mutex_lock(&dev->device_lock);
4169ca9050bSTomas Winkler 	if (MEI_FILE_DISCONNECTED == cl->state) {
4179ca9050bSTomas Winkler 		rets = 0;
4189ca9050bSTomas Winkler 		dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
4199ca9050bSTomas Winkler 	} else {
4209ca9050bSTomas Winkler 		rets = -ENODEV;
4219ca9050bSTomas Winkler 		if (MEI_FILE_DISCONNECTED != cl->state)
4229ca9050bSTomas Winkler 			dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
4239ca9050bSTomas Winkler 
4249ca9050bSTomas Winkler 		if (err)
4259ca9050bSTomas Winkler 			dev_dbg(&dev->pdev->dev,
4269ca9050bSTomas Winkler 					"wait failed disconnect err=%08x\n",
4279ca9050bSTomas Winkler 					err);
4289ca9050bSTomas Winkler 
4299ca9050bSTomas Winkler 		dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
4309ca9050bSTomas Winkler 	}
4319ca9050bSTomas Winkler 
4329ca9050bSTomas Winkler 	mei_io_list_flush(&dev->ctrl_rd_list, cl);
4339ca9050bSTomas Winkler 	mei_io_list_flush(&dev->ctrl_wr_list, cl);
4349ca9050bSTomas Winkler free:
4359ca9050bSTomas Winkler 	mei_io_cb_free(cb);
4369ca9050bSTomas Winkler 	return rets;
4379ca9050bSTomas Winkler }
4389ca9050bSTomas Winkler 
4399ca9050bSTomas Winkler 
4409ca9050bSTomas Winkler /**
44190e0b5f1STomas Winkler  * mei_cl_is_other_connecting - checks if other
44290e0b5f1STomas Winkler  *    client with the same me client id is connecting
4439ca9050bSTomas Winkler  *
4449ca9050bSTomas Winkler  * @cl: private data of the file object
4459ca9050bSTomas Winkler  *
44690e0b5f1STomas Winkler  * returns ture if other client is connected, 0 - otherwise.
4479ca9050bSTomas Winkler  */
44890e0b5f1STomas Winkler bool mei_cl_is_other_connecting(struct mei_cl *cl)
4499ca9050bSTomas Winkler {
45090e0b5f1STomas Winkler 	struct mei_device *dev;
45190e0b5f1STomas Winkler 	struct mei_cl *pos;
45290e0b5f1STomas Winkler 	struct mei_cl *next;
4539ca9050bSTomas Winkler 
45490e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
45590e0b5f1STomas Winkler 		return false;
45690e0b5f1STomas Winkler 
45790e0b5f1STomas Winkler 	dev = cl->dev;
45890e0b5f1STomas Winkler 
45990e0b5f1STomas Winkler 	list_for_each_entry_safe(pos, next, &dev->file_list, link) {
46090e0b5f1STomas Winkler 		if ((pos->state == MEI_FILE_CONNECTING) &&
46190e0b5f1STomas Winkler 		    (pos != cl) && cl->me_client_id == pos->me_client_id)
46290e0b5f1STomas Winkler 			return true;
4639ca9050bSTomas Winkler 
4649ca9050bSTomas Winkler 	}
46590e0b5f1STomas Winkler 
46690e0b5f1STomas Winkler 	return false;
4679ca9050bSTomas Winkler }
4689ca9050bSTomas Winkler 
4699ca9050bSTomas Winkler /**
4709f81abdaSTomas Winkler  * mei_cl_connect - connect host clinet to the me one
4719f81abdaSTomas Winkler  *
4729f81abdaSTomas Winkler  * @cl: host client
4739f81abdaSTomas Winkler  *
4749f81abdaSTomas Winkler  * Locking: called under "dev->device_lock" lock
4759f81abdaSTomas Winkler  *
4769f81abdaSTomas Winkler  * returns 0 on success, <0 on failure.
4779f81abdaSTomas Winkler  */
4789f81abdaSTomas Winkler int mei_cl_connect(struct mei_cl *cl, struct file *file)
4799f81abdaSTomas Winkler {
4809f81abdaSTomas Winkler 	struct mei_device *dev;
4819f81abdaSTomas Winkler 	struct mei_cl_cb *cb;
4829f81abdaSTomas Winkler 	long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT);
4839f81abdaSTomas Winkler 	int rets;
4849f81abdaSTomas Winkler 
4859f81abdaSTomas Winkler 	if (WARN_ON(!cl || !cl->dev))
4869f81abdaSTomas Winkler 		return -ENODEV;
4879f81abdaSTomas Winkler 
4889f81abdaSTomas Winkler 	dev = cl->dev;
4899f81abdaSTomas Winkler 
4909f81abdaSTomas Winkler 	cb = mei_io_cb_init(cl, file);
4919f81abdaSTomas Winkler 	if (!cb) {
4929f81abdaSTomas Winkler 		rets = -ENOMEM;
4939f81abdaSTomas Winkler 		goto out;
4949f81abdaSTomas Winkler 	}
4959f81abdaSTomas Winkler 
4969f81abdaSTomas Winkler 	cb->fop_type = MEI_FOP_IOCTL;
4979f81abdaSTomas Winkler 
4989f81abdaSTomas Winkler 	if (dev->mei_host_buffer_is_empty &&
4999f81abdaSTomas Winkler 	    !mei_cl_is_other_connecting(cl)) {
5009f81abdaSTomas Winkler 		dev->mei_host_buffer_is_empty = false;
5019f81abdaSTomas Winkler 
5029f81abdaSTomas Winkler 		if (mei_hbm_cl_connect_req(dev, cl)) {
5039f81abdaSTomas Winkler 			rets = -ENODEV;
5049f81abdaSTomas Winkler 			goto out;
5059f81abdaSTomas Winkler 		}
5069f81abdaSTomas Winkler 		cl->timer_count = MEI_CONNECT_TIMEOUT;
5079f81abdaSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
5089f81abdaSTomas Winkler 	} else {
5099f81abdaSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
5109f81abdaSTomas Winkler 	}
5119f81abdaSTomas Winkler 
5129f81abdaSTomas Winkler 	mutex_unlock(&dev->device_lock);
5139f81abdaSTomas Winkler 	rets = wait_event_timeout(dev->wait_recvd_msg,
5149f81abdaSTomas Winkler 				 (cl->state == MEI_FILE_CONNECTED ||
5159f81abdaSTomas Winkler 				  cl->state == MEI_FILE_DISCONNECTED),
5169f81abdaSTomas Winkler 				 timeout * HZ);
5179f81abdaSTomas Winkler 	mutex_lock(&dev->device_lock);
5189f81abdaSTomas Winkler 
5199f81abdaSTomas Winkler 	if (cl->state != MEI_FILE_CONNECTED) {
5209f81abdaSTomas Winkler 		rets = -EFAULT;
5219f81abdaSTomas Winkler 
5229f81abdaSTomas Winkler 		mei_io_list_flush(&dev->ctrl_rd_list, cl);
5239f81abdaSTomas Winkler 		mei_io_list_flush(&dev->ctrl_wr_list, cl);
5249f81abdaSTomas Winkler 		goto out;
5259f81abdaSTomas Winkler 	}
5269f81abdaSTomas Winkler 
5279f81abdaSTomas Winkler 	rets = cl->status;
5289f81abdaSTomas Winkler 
5299f81abdaSTomas Winkler out:
5309f81abdaSTomas Winkler 	mei_io_cb_free(cb);
5319f81abdaSTomas Winkler 	return rets;
5329f81abdaSTomas Winkler }
5339f81abdaSTomas Winkler 
5349f81abdaSTomas Winkler /**
53590e0b5f1STomas Winkler  * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
5369ca9050bSTomas Winkler  *
5379ca9050bSTomas Winkler  * @dev: the device structure
5389ca9050bSTomas Winkler  * @cl: private data of the file object
5399ca9050bSTomas Winkler  *
5409ca9050bSTomas Winkler  * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
5419ca9050bSTomas Winkler  *	-ENOENT if mei_cl is not present
5429ca9050bSTomas Winkler  *	-EINVAL if single_recv_buf == 0
5439ca9050bSTomas Winkler  */
54490e0b5f1STomas Winkler int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
5459ca9050bSTomas Winkler {
54690e0b5f1STomas Winkler 	struct mei_device *dev;
5479ca9050bSTomas Winkler 	int i;
5489ca9050bSTomas Winkler 
54990e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
55090e0b5f1STomas Winkler 		return -EINVAL;
55190e0b5f1STomas Winkler 
55290e0b5f1STomas Winkler 	dev = cl->dev;
55390e0b5f1STomas Winkler 
5549ca9050bSTomas Winkler 	if (!dev->me_clients_num)
5559ca9050bSTomas Winkler 		return 0;
5569ca9050bSTomas Winkler 
5579ca9050bSTomas Winkler 	if (cl->mei_flow_ctrl_creds > 0)
5589ca9050bSTomas Winkler 		return 1;
5599ca9050bSTomas Winkler 
5609ca9050bSTomas Winkler 	for (i = 0; i < dev->me_clients_num; i++) {
5619ca9050bSTomas Winkler 		struct mei_me_client  *me_cl = &dev->me_clients[i];
5629ca9050bSTomas Winkler 		if (me_cl->client_id == cl->me_client_id) {
5639ca9050bSTomas Winkler 			if (me_cl->mei_flow_ctrl_creds) {
5649ca9050bSTomas Winkler 				if (WARN_ON(me_cl->props.single_recv_buf == 0))
5659ca9050bSTomas Winkler 					return -EINVAL;
5669ca9050bSTomas Winkler 				return 1;
5679ca9050bSTomas Winkler 			} else {
5689ca9050bSTomas Winkler 				return 0;
5699ca9050bSTomas Winkler 			}
5709ca9050bSTomas Winkler 		}
5719ca9050bSTomas Winkler 	}
5729ca9050bSTomas Winkler 	return -ENOENT;
5739ca9050bSTomas Winkler }
5749ca9050bSTomas Winkler 
5759ca9050bSTomas Winkler /**
57690e0b5f1STomas Winkler  * mei_cl_flow_ctrl_reduce - reduces flow_control.
5779ca9050bSTomas Winkler  *
5789ca9050bSTomas Winkler  * @dev: the device structure
5799ca9050bSTomas Winkler  * @cl: private data of the file object
5809ca9050bSTomas Winkler  * @returns
5819ca9050bSTomas Winkler  *	0 on success
5829ca9050bSTomas Winkler  *	-ENOENT when me client is not found
5839ca9050bSTomas Winkler  *	-EINVAL when ctrl credits are <= 0
5849ca9050bSTomas Winkler  */
58590e0b5f1STomas Winkler int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
5869ca9050bSTomas Winkler {
58790e0b5f1STomas Winkler 	struct mei_device *dev;
5889ca9050bSTomas Winkler 	int i;
5899ca9050bSTomas Winkler 
59090e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
59190e0b5f1STomas Winkler 		return -EINVAL;
59290e0b5f1STomas Winkler 
59390e0b5f1STomas Winkler 	dev = cl->dev;
59490e0b5f1STomas Winkler 
5959ca9050bSTomas Winkler 	if (!dev->me_clients_num)
5969ca9050bSTomas Winkler 		return -ENOENT;
5979ca9050bSTomas Winkler 
5989ca9050bSTomas Winkler 	for (i = 0; i < dev->me_clients_num; i++) {
5999ca9050bSTomas Winkler 		struct mei_me_client  *me_cl = &dev->me_clients[i];
6009ca9050bSTomas Winkler 		if (me_cl->client_id == cl->me_client_id) {
6019ca9050bSTomas Winkler 			if (me_cl->props.single_recv_buf != 0) {
6029ca9050bSTomas Winkler 				if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
6039ca9050bSTomas Winkler 					return -EINVAL;
6049ca9050bSTomas Winkler 				dev->me_clients[i].mei_flow_ctrl_creds--;
6059ca9050bSTomas Winkler 			} else {
6069ca9050bSTomas Winkler 				if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
6079ca9050bSTomas Winkler 					return -EINVAL;
6089ca9050bSTomas Winkler 				cl->mei_flow_ctrl_creds--;
6099ca9050bSTomas Winkler 			}
6109ca9050bSTomas Winkler 			return 0;
6119ca9050bSTomas Winkler 		}
6129ca9050bSTomas Winkler 	}
6139ca9050bSTomas Winkler 	return -ENOENT;
6149ca9050bSTomas Winkler }
6159ca9050bSTomas Winkler 
6169ca9050bSTomas Winkler /**
61790e0b5f1STomas Winkler  * mei_cl_start_read - the start read client message function.
6189ca9050bSTomas Winkler  *
61990e0b5f1STomas Winkler  * @cl: host client
6209ca9050bSTomas Winkler  *
6219ca9050bSTomas Winkler  * returns 0 on success, <0 on failure.
6229ca9050bSTomas Winkler  */
62390e0b5f1STomas Winkler int mei_cl_read_start(struct mei_cl *cl)
6249ca9050bSTomas Winkler {
62590e0b5f1STomas Winkler 	struct mei_device *dev;
6269ca9050bSTomas Winkler 	struct mei_cl_cb *cb;
6279ca9050bSTomas Winkler 	int rets;
6289ca9050bSTomas Winkler 	int i;
6299ca9050bSTomas Winkler 
63090e0b5f1STomas Winkler 	if (WARN_ON(!cl || !cl->dev))
63190e0b5f1STomas Winkler 		return -ENODEV;
63290e0b5f1STomas Winkler 
63390e0b5f1STomas Winkler 	dev = cl->dev;
63490e0b5f1STomas Winkler 
6359ca9050bSTomas Winkler 	if (cl->state != MEI_FILE_CONNECTED)
6369ca9050bSTomas Winkler 		return -ENODEV;
6379ca9050bSTomas Winkler 
6389ca9050bSTomas Winkler 	if (dev->dev_state != MEI_DEV_ENABLED)
6399ca9050bSTomas Winkler 		return -ENODEV;
6409ca9050bSTomas Winkler 
641d91aaed3STomas Winkler 	if (cl->read_cb) {
6429ca9050bSTomas Winkler 		dev_dbg(&dev->pdev->dev, "read is pending.\n");
6439ca9050bSTomas Winkler 		return -EBUSY;
6449ca9050bSTomas Winkler 	}
6459ca9050bSTomas Winkler 	i = mei_me_cl_by_id(dev, cl->me_client_id);
6469ca9050bSTomas Winkler 	if (i < 0) {
6479ca9050bSTomas Winkler 		dev_err(&dev->pdev->dev, "no such me client %d\n",
6489ca9050bSTomas Winkler 			cl->me_client_id);
6499ca9050bSTomas Winkler 		return  -ENODEV;
6509ca9050bSTomas Winkler 	}
6519ca9050bSTomas Winkler 
6529ca9050bSTomas Winkler 	cb = mei_io_cb_init(cl, NULL);
6539ca9050bSTomas Winkler 	if (!cb)
6549ca9050bSTomas Winkler 		return -ENOMEM;
6559ca9050bSTomas Winkler 
6569ca9050bSTomas Winkler 	rets = mei_io_cb_alloc_resp_buf(cb,
6579ca9050bSTomas Winkler 			dev->me_clients[i].props.max_msg_length);
6589ca9050bSTomas Winkler 	if (rets)
6599ca9050bSTomas Winkler 		goto err;
6609ca9050bSTomas Winkler 
6619ca9050bSTomas Winkler 	cb->fop_type = MEI_FOP_READ;
6629ca9050bSTomas Winkler 	cl->read_cb = cb;
6639ca9050bSTomas Winkler 	if (dev->mei_host_buffer_is_empty) {
6649ca9050bSTomas Winkler 		dev->mei_host_buffer_is_empty = false;
6659ca9050bSTomas Winkler 		if (mei_hbm_cl_flow_control_req(dev, cl)) {
6669ca9050bSTomas Winkler 			rets = -ENODEV;
6679ca9050bSTomas Winkler 			goto err;
6689ca9050bSTomas Winkler 		}
6699ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->read_list.list);
6709ca9050bSTomas Winkler 	} else {
6719ca9050bSTomas Winkler 		list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
6729ca9050bSTomas Winkler 	}
6739ca9050bSTomas Winkler 	return rets;
6749ca9050bSTomas Winkler err:
6759ca9050bSTomas Winkler 	mei_io_cb_free(cb);
6769ca9050bSTomas Winkler 	return rets;
6779ca9050bSTomas Winkler }
6789ca9050bSTomas Winkler 
679