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