xref: /linux/drivers/misc/mei/bus.c (revision aa6aef216f8aea1a00b56aafc29b8745237a9b62)
1e5354107SSamuel Ortiz /*
2e5354107SSamuel Ortiz  * Intel Management Engine Interface (Intel MEI) Linux driver
3e5354107SSamuel Ortiz  * Copyright (c) 2012-2013, Intel Corporation.
4e5354107SSamuel Ortiz  *
5e5354107SSamuel Ortiz  * This program is free software; you can redistribute it and/or modify it
6e5354107SSamuel Ortiz  * under the terms and conditions of the GNU General Public License,
7e5354107SSamuel Ortiz  * version 2, as published by the Free Software Foundation.
8e5354107SSamuel Ortiz  *
9e5354107SSamuel Ortiz  * This program is distributed in the hope it will be useful, but WITHOUT
10e5354107SSamuel Ortiz  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11e5354107SSamuel Ortiz  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12e5354107SSamuel Ortiz  * more details.
13e5354107SSamuel Ortiz  *
14e5354107SSamuel Ortiz  */
15e5354107SSamuel Ortiz 
16e5354107SSamuel Ortiz #include <linux/module.h>
17e5354107SSamuel Ortiz #include <linux/device.h>
18e5354107SSamuel Ortiz #include <linux/kernel.h>
193e833295SSamuel Ortiz #include <linux/sched.h>
20e5354107SSamuel Ortiz #include <linux/init.h>
21e5354107SSamuel Ortiz #include <linux/errno.h>
22e5354107SSamuel Ortiz #include <linux/slab.h>
23e5354107SSamuel Ortiz #include <linux/mutex.h>
24e5354107SSamuel Ortiz #include <linux/interrupt.h>
25e5354107SSamuel Ortiz #include <linux/pci.h>
26e5354107SSamuel Ortiz #include <linux/mei_cl_bus.h>
27e5354107SSamuel Ortiz 
28e5354107SSamuel Ortiz #include "mei_dev.h"
293e833295SSamuel Ortiz #include "hw-me.h"
303e833295SSamuel Ortiz #include "client.h"
31e5354107SSamuel Ortiz 
32e5354107SSamuel Ortiz #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
33e5354107SSamuel Ortiz #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
34e5354107SSamuel Ortiz 
35e5354107SSamuel Ortiz static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
36e5354107SSamuel Ortiz {
37e5354107SSamuel Ortiz 	struct mei_cl_device *device = to_mei_cl_device(dev);
38e5354107SSamuel Ortiz 	struct mei_cl_driver *driver = to_mei_cl_driver(drv);
39e5354107SSamuel Ortiz 	const struct mei_cl_device_id *id;
40e5354107SSamuel Ortiz 
41e5354107SSamuel Ortiz 	if (!device)
42e5354107SSamuel Ortiz 		return 0;
43e5354107SSamuel Ortiz 
44e5354107SSamuel Ortiz 	if (!driver || !driver->id_table)
45e5354107SSamuel Ortiz 		return 0;
46e5354107SSamuel Ortiz 
47e5354107SSamuel Ortiz 	id = driver->id_table;
48e5354107SSamuel Ortiz 
49e5354107SSamuel Ortiz 	while (id->name[0]) {
50e5354107SSamuel Ortiz 		if (!strcmp(dev_name(dev), id->name))
51e5354107SSamuel Ortiz 			return 1;
52e5354107SSamuel Ortiz 
53e5354107SSamuel Ortiz 		id++;
54e5354107SSamuel Ortiz 	}
55e5354107SSamuel Ortiz 
56e5354107SSamuel Ortiz 	return 0;
57e5354107SSamuel Ortiz }
58e5354107SSamuel Ortiz 
59e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev)
60e5354107SSamuel Ortiz {
61e5354107SSamuel Ortiz 	struct mei_cl_device *device = to_mei_cl_device(dev);
62e5354107SSamuel Ortiz 	struct mei_cl_driver *driver;
63e5354107SSamuel Ortiz 	struct mei_cl_device_id id;
64e5354107SSamuel Ortiz 
65e5354107SSamuel Ortiz 	if (!device)
66e5354107SSamuel Ortiz 		return 0;
67e5354107SSamuel Ortiz 
68e5354107SSamuel Ortiz 	driver = to_mei_cl_driver(dev->driver);
69e5354107SSamuel Ortiz 	if (!driver || !driver->probe)
70e5354107SSamuel Ortiz 		return -ENODEV;
71e5354107SSamuel Ortiz 
72e5354107SSamuel Ortiz 	dev_dbg(dev, "Device probe\n");
73e5354107SSamuel Ortiz 
74e5354107SSamuel Ortiz 	strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE);
75e5354107SSamuel Ortiz 
76e5354107SSamuel Ortiz 	return driver->probe(device, &id);
77e5354107SSamuel Ortiz }
78e5354107SSamuel Ortiz 
79e5354107SSamuel Ortiz static int mei_cl_device_remove(struct device *dev)
80e5354107SSamuel Ortiz {
81e5354107SSamuel Ortiz 	struct mei_cl_device *device = to_mei_cl_device(dev);
82e5354107SSamuel Ortiz 	struct mei_cl_driver *driver;
83e5354107SSamuel Ortiz 
84e5354107SSamuel Ortiz 	if (!device || !dev->driver)
85e5354107SSamuel Ortiz 		return 0;
86e5354107SSamuel Ortiz 
873e833295SSamuel Ortiz 	if (device->event_cb) {
883e833295SSamuel Ortiz 		device->event_cb = NULL;
893e833295SSamuel Ortiz 		cancel_work_sync(&device->event_work);
903e833295SSamuel Ortiz 	}
913e833295SSamuel Ortiz 
92e5354107SSamuel Ortiz 	driver = to_mei_cl_driver(dev->driver);
93e5354107SSamuel Ortiz 	if (!driver->remove) {
94e5354107SSamuel Ortiz 		dev->driver = NULL;
95e5354107SSamuel Ortiz 
96e5354107SSamuel Ortiz 		return 0;
97e5354107SSamuel Ortiz 	}
98e5354107SSamuel Ortiz 
99e5354107SSamuel Ortiz 	return driver->remove(device);
100e5354107SSamuel Ortiz }
101e5354107SSamuel Ortiz 
102e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
103e5354107SSamuel Ortiz 			     char *buf)
104e5354107SSamuel Ortiz {
105e5354107SSamuel Ortiz 	int len;
106e5354107SSamuel Ortiz 
107e5354107SSamuel Ortiz 	len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev));
108e5354107SSamuel Ortiz 
109e5354107SSamuel Ortiz 	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
110e5354107SSamuel Ortiz }
111e5354107SSamuel Ortiz 
112e5354107SSamuel Ortiz static struct device_attribute mei_cl_dev_attrs[] = {
113e5354107SSamuel Ortiz 	__ATTR_RO(modalias),
114e5354107SSamuel Ortiz 	__ATTR_NULL,
115e5354107SSamuel Ortiz };
116e5354107SSamuel Ortiz 
117e5354107SSamuel Ortiz static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
118e5354107SSamuel Ortiz {
119e5354107SSamuel Ortiz 	if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
120e5354107SSamuel Ortiz 		return -ENOMEM;
121e5354107SSamuel Ortiz 
122e5354107SSamuel Ortiz 	return 0;
123e5354107SSamuel Ortiz }
124e5354107SSamuel Ortiz 
125e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = {
126e5354107SSamuel Ortiz 	.name		= "mei",
127e5354107SSamuel Ortiz 	.dev_attrs	= mei_cl_dev_attrs,
128e5354107SSamuel Ortiz 	.match		= mei_cl_device_match,
129e5354107SSamuel Ortiz 	.probe		= mei_cl_device_probe,
130e5354107SSamuel Ortiz 	.remove		= mei_cl_device_remove,
131e5354107SSamuel Ortiz 	.uevent		= mei_cl_uevent,
132e5354107SSamuel Ortiz };
133e5354107SSamuel Ortiz 
134e5354107SSamuel Ortiz static void mei_cl_dev_release(struct device *dev)
135e5354107SSamuel Ortiz {
136e5354107SSamuel Ortiz 	kfree(to_mei_cl_device(dev));
137e5354107SSamuel Ortiz }
138e5354107SSamuel Ortiz 
139e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = {
140e5354107SSamuel Ortiz 	.release	= mei_cl_dev_release,
141e5354107SSamuel Ortiz };
142e5354107SSamuel Ortiz 
143a7b71bc0SSamuel Ortiz static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev,
144a7b71bc0SSamuel Ortiz 						uuid_le uuid)
145a7b71bc0SSamuel Ortiz {
146a7b71bc0SSamuel Ortiz 	struct mei_cl *cl, *next;
147a7b71bc0SSamuel Ortiz 
148a7b71bc0SSamuel Ortiz 	list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
149a7b71bc0SSamuel Ortiz 		if (!uuid_le_cmp(uuid, cl->device_uuid))
150a7b71bc0SSamuel Ortiz 			return cl;
151a7b71bc0SSamuel Ortiz 	}
152a7b71bc0SSamuel Ortiz 
153a7b71bc0SSamuel Ortiz 	return NULL;
154a7b71bc0SSamuel Ortiz }
155a7b71bc0SSamuel Ortiz struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
156e5354107SSamuel Ortiz 				  uuid_le uuid, char *name)
157e5354107SSamuel Ortiz {
158e5354107SSamuel Ortiz 	struct mei_cl_device *device;
159a7b71bc0SSamuel Ortiz 	struct mei_cl *cl;
160e5354107SSamuel Ortiz 	int status;
161e5354107SSamuel Ortiz 
162a7b71bc0SSamuel Ortiz 	cl = mei_bus_find_mei_cl_by_uuid(dev, uuid);
163a7b71bc0SSamuel Ortiz 	if (cl == NULL)
164a7b71bc0SSamuel Ortiz 		return NULL;
165a7b71bc0SSamuel Ortiz 
166e5354107SSamuel Ortiz 	device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
167e5354107SSamuel Ortiz 	if (!device)
168e5354107SSamuel Ortiz 		return NULL;
169e5354107SSamuel Ortiz 
170a7b71bc0SSamuel Ortiz 	device->cl = cl;
171a7b71bc0SSamuel Ortiz 
172a7b71bc0SSamuel Ortiz 	device->dev.parent = &dev->pdev->dev;
173e5354107SSamuel Ortiz 	device->dev.bus = &mei_cl_bus_type;
174e5354107SSamuel Ortiz 	device->dev.type = &mei_cl_device_type;
175e5354107SSamuel Ortiz 
176e5354107SSamuel Ortiz 	dev_set_name(&device->dev, "%s", name);
177e5354107SSamuel Ortiz 
178e5354107SSamuel Ortiz 	status = device_register(&device->dev);
179a7b71bc0SSamuel Ortiz 	if (status) {
180a7b71bc0SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Failed to register MEI device\n");
181a7b71bc0SSamuel Ortiz 		kfree(device);
182a7b71bc0SSamuel Ortiz 		return NULL;
183a7b71bc0SSamuel Ortiz 	}
184a7b71bc0SSamuel Ortiz 
185a7b71bc0SSamuel Ortiz 	cl->device = device;
186e5354107SSamuel Ortiz 
187e5354107SSamuel Ortiz 	dev_dbg(&device->dev, "client %s registered\n", name);
188e5354107SSamuel Ortiz 
189e5354107SSamuel Ortiz 	return device;
190e5354107SSamuel Ortiz }
191e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_add_device);
192e5354107SSamuel Ortiz 
193e5354107SSamuel Ortiz void mei_cl_remove_device(struct mei_cl_device *device)
194e5354107SSamuel Ortiz {
195e5354107SSamuel Ortiz 	device_unregister(&device->dev);
196e5354107SSamuel Ortiz }
197e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_remove_device);
198333e4ee0SSamuel Ortiz 
199333e4ee0SSamuel Ortiz int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner)
200333e4ee0SSamuel Ortiz {
201333e4ee0SSamuel Ortiz 	int err;
202333e4ee0SSamuel Ortiz 
203333e4ee0SSamuel Ortiz 	driver->driver.name = driver->name;
204333e4ee0SSamuel Ortiz 	driver->driver.owner = owner;
205333e4ee0SSamuel Ortiz 	driver->driver.bus = &mei_cl_bus_type;
206333e4ee0SSamuel Ortiz 
207333e4ee0SSamuel Ortiz 	err = driver_register(&driver->driver);
208333e4ee0SSamuel Ortiz 	if (err)
209333e4ee0SSamuel Ortiz 		return err;
210333e4ee0SSamuel Ortiz 
211333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] registered\n", driver->driver.name);
212333e4ee0SSamuel Ortiz 
213333e4ee0SSamuel Ortiz 	return 0;
214333e4ee0SSamuel Ortiz }
215333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
216333e4ee0SSamuel Ortiz 
217333e4ee0SSamuel Ortiz void mei_cl_driver_unregister(struct mei_cl_driver *driver)
218333e4ee0SSamuel Ortiz {
219333e4ee0SSamuel Ortiz 	driver_unregister(&driver->driver);
220333e4ee0SSamuel Ortiz 
221333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
222333e4ee0SSamuel Ortiz }
223333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
2243e833295SSamuel Ortiz 
22544d88d91SSamuel Ortiz static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
22644d88d91SSamuel Ortiz 			bool blocking)
2273e833295SSamuel Ortiz {
2283e833295SSamuel Ortiz 	struct mei_device *dev;
2293e833295SSamuel Ortiz 	struct mei_msg_hdr mei_hdr;
2303e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
2313e833295SSamuel Ortiz 	int me_cl_id, err;
2323e833295SSamuel Ortiz 
2333e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
2343e833295SSamuel Ortiz 		return -ENODEV;
2353e833295SSamuel Ortiz 
2363e833295SSamuel Ortiz 	if (cl->state != MEI_FILE_CONNECTED)
2373e833295SSamuel Ortiz 		return -ENODEV;
2383e833295SSamuel Ortiz 
2393e833295SSamuel Ortiz 	cb = mei_io_cb_init(cl, NULL);
2403e833295SSamuel Ortiz 	if (!cb)
2413e833295SSamuel Ortiz 		return -ENOMEM;
2423e833295SSamuel Ortiz 
2433e833295SSamuel Ortiz 	err = mei_io_cb_alloc_req_buf(cb, length);
2443e833295SSamuel Ortiz 	if (err < 0) {
2453e833295SSamuel Ortiz 		mei_io_cb_free(cb);
2463e833295SSamuel Ortiz 		return err;
2473e833295SSamuel Ortiz 	}
2483e833295SSamuel Ortiz 
2493e833295SSamuel Ortiz 	memcpy(cb->request_buffer.data, buf, length);
2503e833295SSamuel Ortiz 	cb->fop_type = MEI_FOP_WRITE;
2513e833295SSamuel Ortiz 
2523e833295SSamuel Ortiz 	dev = cl->dev;
2533e833295SSamuel Ortiz 
2543e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
2553e833295SSamuel Ortiz 
2563e833295SSamuel Ortiz 	/* Check if we have an ME client device */
2573e833295SSamuel Ortiz 	me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id);
2583e833295SSamuel Ortiz 	if (me_cl_id == dev->me_clients_num) {
2593e833295SSamuel Ortiz 		err = -ENODEV;
2603e833295SSamuel Ortiz 		goto out_err;
2613e833295SSamuel Ortiz 	}
2623e833295SSamuel Ortiz 
2633e833295SSamuel Ortiz 	if (length > dev->me_clients[me_cl_id].props.max_msg_length) {
2643e833295SSamuel Ortiz 		err = -EINVAL;
2653e833295SSamuel Ortiz 		goto out_err;
2663e833295SSamuel Ortiz 	}
2673e833295SSamuel Ortiz 
2683e833295SSamuel Ortiz 	err = mei_cl_flow_ctrl_creds(cl);
2693e833295SSamuel Ortiz 	if (err < 0)
2703e833295SSamuel Ortiz 		goto out_err;
2713e833295SSamuel Ortiz 
2723e833295SSamuel Ortiz 	/* Host buffer is not ready, we queue the request */
2733e833295SSamuel Ortiz 	if (err == 0 || !dev->hbuf_is_ready) {
2743e833295SSamuel Ortiz 		cb->buf_idx = 0;
2753e833295SSamuel Ortiz 		mei_hdr.msg_complete = 0;
2763e833295SSamuel Ortiz 		cl->writing_state = MEI_WRITING;
2773e833295SSamuel Ortiz 
27844d88d91SSamuel Ortiz 		goto out;
2793e833295SSamuel Ortiz 	}
2803e833295SSamuel Ortiz 
2813e833295SSamuel Ortiz 	dev->hbuf_is_ready = false;
2823e833295SSamuel Ortiz 
2833e833295SSamuel Ortiz 	/* Check for a maximum length */
2843e833295SSamuel Ortiz 	if (length > mei_hbuf_max_len(dev)) {
2853e833295SSamuel Ortiz 		mei_hdr.length = mei_hbuf_max_len(dev);
2863e833295SSamuel Ortiz 		mei_hdr.msg_complete = 0;
2873e833295SSamuel Ortiz 	} else {
2883e833295SSamuel Ortiz 		mei_hdr.length = length;
2893e833295SSamuel Ortiz 		mei_hdr.msg_complete = 1;
2903e833295SSamuel Ortiz 	}
2913e833295SSamuel Ortiz 
2923e833295SSamuel Ortiz 	mei_hdr.host_addr = cl->host_client_id;
2933e833295SSamuel Ortiz 	mei_hdr.me_addr = cl->me_client_id;
2943e833295SSamuel Ortiz 	mei_hdr.reserved = 0;
2953e833295SSamuel Ortiz 
2963e833295SSamuel Ortiz 	if (mei_write_message(dev, &mei_hdr, buf)) {
2973e833295SSamuel Ortiz 		err = -EIO;
2983e833295SSamuel Ortiz 		goto out_err;
2993e833295SSamuel Ortiz 	}
3003e833295SSamuel Ortiz 
3013e833295SSamuel Ortiz 	cl->writing_state = MEI_WRITING;
3023e833295SSamuel Ortiz 	cb->buf_idx = mei_hdr.length;
3033e833295SSamuel Ortiz 
30444d88d91SSamuel Ortiz out:
30544d88d91SSamuel Ortiz 	if (mei_hdr.msg_complete) {
3063e833295SSamuel Ortiz 		if (mei_cl_flow_ctrl_reduce(cl)) {
30744d88d91SSamuel Ortiz 			err = -ENODEV;
3083e833295SSamuel Ortiz 			goto out_err;
3093e833295SSamuel Ortiz 		}
3103e833295SSamuel Ortiz 		list_add_tail(&cb->list, &dev->write_waiting_list.list);
31144d88d91SSamuel Ortiz 	} else {
31244d88d91SSamuel Ortiz 		list_add_tail(&cb->list, &dev->write_list.list);
3133e833295SSamuel Ortiz 	}
3143e833295SSamuel Ortiz 
3153e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
3163e833295SSamuel Ortiz 
31744d88d91SSamuel Ortiz 	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
31844d88d91SSamuel Ortiz 		if (wait_event_interruptible(cl->tx_wait,
31944d88d91SSamuel Ortiz 			cl->writing_state == MEI_WRITE_COMPLETE)) {
32044d88d91SSamuel Ortiz 				if (signal_pending(current))
32144d88d91SSamuel Ortiz 					err = -EINTR;
32244d88d91SSamuel Ortiz 			err = -ERESTARTSYS;
32344d88d91SSamuel Ortiz 			mutex_lock(&dev->device_lock);
32444d88d91SSamuel Ortiz 			goto out_err;
32544d88d91SSamuel Ortiz 		}
32644d88d91SSamuel Ortiz 	}
32744d88d91SSamuel Ortiz 
3283e833295SSamuel Ortiz 	return mei_hdr.length;
3293e833295SSamuel Ortiz 
3303e833295SSamuel Ortiz out_err:
3313e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
3323e833295SSamuel Ortiz 	mei_io_cb_free(cb);
3333e833295SSamuel Ortiz 
3343e833295SSamuel Ortiz 	return err;
3353e833295SSamuel Ortiz }
3363e833295SSamuel Ortiz 
3373e833295SSamuel Ortiz int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
3383e833295SSamuel Ortiz {
3393e833295SSamuel Ortiz 	struct mei_device *dev;
3403e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
3413e833295SSamuel Ortiz 	size_t r_length;
3423e833295SSamuel Ortiz 	int err;
3433e833295SSamuel Ortiz 
3443e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
3453e833295SSamuel Ortiz 		return -ENODEV;
3463e833295SSamuel Ortiz 
3473e833295SSamuel Ortiz 	dev = cl->dev;
3483e833295SSamuel Ortiz 
3493e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
3503e833295SSamuel Ortiz 
3513e833295SSamuel Ortiz 	if (!cl->read_cb) {
3523e833295SSamuel Ortiz 		err = mei_cl_read_start(cl);
3533e833295SSamuel Ortiz 		if (err < 0) {
3543e833295SSamuel Ortiz 			mutex_unlock(&dev->device_lock);
3553e833295SSamuel Ortiz 			return err;
3563e833295SSamuel Ortiz 		}
3573e833295SSamuel Ortiz 	}
3583e833295SSamuel Ortiz 
3593e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE &&
3603e833295SSamuel Ortiz 	    !waitqueue_active(&cl->rx_wait)) {
3613e833295SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
3623e833295SSamuel Ortiz 
3633e833295SSamuel Ortiz 		if (wait_event_interruptible(cl->rx_wait,
3643e833295SSamuel Ortiz 				(MEI_READ_COMPLETE == cl->reading_state))) {
3653e833295SSamuel Ortiz 			if (signal_pending(current))
3663e833295SSamuel Ortiz 				return -EINTR;
3673e833295SSamuel Ortiz 			return -ERESTARTSYS;
3683e833295SSamuel Ortiz 		}
3693e833295SSamuel Ortiz 
3703e833295SSamuel Ortiz 		mutex_lock(&dev->device_lock);
3713e833295SSamuel Ortiz 	}
3723e833295SSamuel Ortiz 
3733e833295SSamuel Ortiz 	cb = cl->read_cb;
3743e833295SSamuel Ortiz 
3753e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE) {
3763e833295SSamuel Ortiz 		r_length = 0;
3773e833295SSamuel Ortiz 		goto out;
3783e833295SSamuel Ortiz 	}
3793e833295SSamuel Ortiz 
3803e833295SSamuel Ortiz 	r_length = min_t(size_t, length, cb->buf_idx);
3813e833295SSamuel Ortiz 
3823e833295SSamuel Ortiz 	memcpy(buf, cb->response_buffer.data, r_length);
3833e833295SSamuel Ortiz 
3843e833295SSamuel Ortiz 	mei_io_cb_free(cb);
3853e833295SSamuel Ortiz 	cl->reading_state = MEI_IDLE;
3863e833295SSamuel Ortiz 	cl->read_cb = NULL;
3873e833295SSamuel Ortiz 
3883e833295SSamuel Ortiz out:
3893e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
3903e833295SSamuel Ortiz 
3913e833295SSamuel Ortiz 	return r_length;
3923e833295SSamuel Ortiz }
3933e833295SSamuel Ortiz 
39444d88d91SSamuel Ortiz inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
39544d88d91SSamuel Ortiz {
39644d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 0);
39744d88d91SSamuel Ortiz }
39844d88d91SSamuel Ortiz 
39944d88d91SSamuel Ortiz inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
40044d88d91SSamuel Ortiz {
40144d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 1);
40244d88d91SSamuel Ortiz }
40344d88d91SSamuel Ortiz 
4043e833295SSamuel Ortiz int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
4053e833295SSamuel Ortiz {
406a7b71bc0SSamuel Ortiz 	struct mei_cl *cl = device->cl;
4073e833295SSamuel Ortiz 
408a7b71bc0SSamuel Ortiz 	if (cl == NULL)
409a7b71bc0SSamuel Ortiz 		return -ENODEV;
4103e833295SSamuel Ortiz 
4113e833295SSamuel Ortiz 	if (device->ops && device->ops->send)
4123e833295SSamuel Ortiz 		return device->ops->send(device, buf, length);
4133e833295SSamuel Ortiz 
4143e833295SSamuel Ortiz 	return __mei_cl_send(cl, buf, length);
4153e833295SSamuel Ortiz }
4163e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_send);
4173e833295SSamuel Ortiz 
4183e833295SSamuel Ortiz int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
4193e833295SSamuel Ortiz {
420a7b71bc0SSamuel Ortiz 	struct mei_cl *cl =  device->cl;
4213e833295SSamuel Ortiz 
422a7b71bc0SSamuel Ortiz 	if (cl == NULL)
423a7b71bc0SSamuel Ortiz 		return -ENODEV;
4243e833295SSamuel Ortiz 
4253e833295SSamuel Ortiz 	if (device->ops && device->ops->recv)
4263e833295SSamuel Ortiz 		return device->ops->recv(device, buf, length);
4273e833295SSamuel Ortiz 
4283e833295SSamuel Ortiz 	return __mei_cl_recv(cl, buf, length);
4293e833295SSamuel Ortiz }
4303e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_recv);
4313e833295SSamuel Ortiz 
4323e833295SSamuel Ortiz static void mei_bus_event_work(struct work_struct *work)
4333e833295SSamuel Ortiz {
4343e833295SSamuel Ortiz 	struct mei_cl_device *device;
4353e833295SSamuel Ortiz 
4363e833295SSamuel Ortiz 	device = container_of(work, struct mei_cl_device, event_work);
4373e833295SSamuel Ortiz 
4383e833295SSamuel Ortiz 	if (device->event_cb)
4393e833295SSamuel Ortiz 		device->event_cb(device, device->events, device->event_context);
4403e833295SSamuel Ortiz 
4413e833295SSamuel Ortiz 	device->events = 0;
4423e833295SSamuel Ortiz 
4433e833295SSamuel Ortiz 	/* Prepare for the next read */
4443e833295SSamuel Ortiz 	mei_cl_read_start(device->cl);
4453e833295SSamuel Ortiz }
4463e833295SSamuel Ortiz 
4473e833295SSamuel Ortiz int mei_cl_register_event_cb(struct mei_cl_device *device,
4483e833295SSamuel Ortiz 			  mei_cl_event_cb_t event_cb, void *context)
4493e833295SSamuel Ortiz {
4503e833295SSamuel Ortiz 	if (device->event_cb)
4513e833295SSamuel Ortiz 		return -EALREADY;
4523e833295SSamuel Ortiz 
4533e833295SSamuel Ortiz 	device->events = 0;
4543e833295SSamuel Ortiz 	device->event_cb = event_cb;
4553e833295SSamuel Ortiz 	device->event_context = context;
4563e833295SSamuel Ortiz 	INIT_WORK(&device->event_work, mei_bus_event_work);
4573e833295SSamuel Ortiz 
4583e833295SSamuel Ortiz 	mei_cl_read_start(device->cl);
4593e833295SSamuel Ortiz 
4603e833295SSamuel Ortiz 	return 0;
4613e833295SSamuel Ortiz }
4623e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
463cf3baefbSSamuel Ortiz 
464*aa6aef21SSamuel Ortiz void *mei_cl_get_drvdata(const struct mei_cl_device *device)
465*aa6aef21SSamuel Ortiz {
466*aa6aef21SSamuel Ortiz 	return dev_get_drvdata(&device->dev);
467*aa6aef21SSamuel Ortiz }
468*aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
469*aa6aef21SSamuel Ortiz 
470*aa6aef21SSamuel Ortiz void mei_cl_set_drvdata(struct mei_cl_device *device, void *data)
471*aa6aef21SSamuel Ortiz {
472*aa6aef21SSamuel Ortiz 	dev_set_drvdata(&device->dev, data);
473*aa6aef21SSamuel Ortiz }
474*aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
475*aa6aef21SSamuel Ortiz 
476cf3baefbSSamuel Ortiz void mei_cl_bus_rx_event(struct mei_cl *cl)
477cf3baefbSSamuel Ortiz {
478cf3baefbSSamuel Ortiz 	struct mei_cl_device *device = cl->device;
479cf3baefbSSamuel Ortiz 
480cf3baefbSSamuel Ortiz 	if (!device || !device->event_cb)
481cf3baefbSSamuel Ortiz 		return;
482cf3baefbSSamuel Ortiz 
483cf3baefbSSamuel Ortiz 	set_bit(MEI_CL_EVENT_RX, &device->events);
484cf3baefbSSamuel Ortiz 
485cf3baefbSSamuel Ortiz 	schedule_work(&device->event_work);
486cf3baefbSSamuel Ortiz }
487cf3baefbSSamuel Ortiz 
488cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void)
489cf3baefbSSamuel Ortiz {
490cf3baefbSSamuel Ortiz 	return bus_register(&mei_cl_bus_type);
491cf3baefbSSamuel Ortiz }
492cf3baefbSSamuel Ortiz 
493cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void)
494cf3baefbSSamuel Ortiz {
495cf3baefbSSamuel Ortiz 	bus_unregister(&mei_cl_bus_type);
496cf3baefbSSamuel Ortiz }
497