xref: /linux/drivers/misc/mei/bus.c (revision fcb136e1ac5774909e0d85189f721b8dfa800e0f)
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,
156e46980a1SSamuel Ortiz 					uuid_le uuid, char *name,
157e46980a1SSamuel Ortiz 					struct mei_cl_ops *ops)
158e5354107SSamuel Ortiz {
159e5354107SSamuel Ortiz 	struct mei_cl_device *device;
160a7b71bc0SSamuel Ortiz 	struct mei_cl *cl;
161e5354107SSamuel Ortiz 	int status;
162e5354107SSamuel Ortiz 
163a7b71bc0SSamuel Ortiz 	cl = mei_bus_find_mei_cl_by_uuid(dev, uuid);
164a7b71bc0SSamuel Ortiz 	if (cl == NULL)
165a7b71bc0SSamuel Ortiz 		return NULL;
166a7b71bc0SSamuel Ortiz 
167e5354107SSamuel Ortiz 	device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
168e5354107SSamuel Ortiz 	if (!device)
169e5354107SSamuel Ortiz 		return NULL;
170e5354107SSamuel Ortiz 
171a7b71bc0SSamuel Ortiz 	device->cl = cl;
172e46980a1SSamuel Ortiz 	device->ops = ops;
173a7b71bc0SSamuel Ortiz 
174a7b71bc0SSamuel Ortiz 	device->dev.parent = &dev->pdev->dev;
175e5354107SSamuel Ortiz 	device->dev.bus = &mei_cl_bus_type;
176e5354107SSamuel Ortiz 	device->dev.type = &mei_cl_device_type;
177e5354107SSamuel Ortiz 
178e5354107SSamuel Ortiz 	dev_set_name(&device->dev, "%s", name);
179e5354107SSamuel Ortiz 
180e5354107SSamuel Ortiz 	status = device_register(&device->dev);
181a7b71bc0SSamuel Ortiz 	if (status) {
182a7b71bc0SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Failed to register MEI device\n");
183a7b71bc0SSamuel Ortiz 		kfree(device);
184a7b71bc0SSamuel Ortiz 		return NULL;
185a7b71bc0SSamuel Ortiz 	}
186a7b71bc0SSamuel Ortiz 
187a7b71bc0SSamuel Ortiz 	cl->device = device;
188e5354107SSamuel Ortiz 
189e5354107SSamuel Ortiz 	dev_dbg(&device->dev, "client %s registered\n", name);
190e5354107SSamuel Ortiz 
191e5354107SSamuel Ortiz 	return device;
192e5354107SSamuel Ortiz }
193e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_add_device);
194e5354107SSamuel Ortiz 
195e5354107SSamuel Ortiz void mei_cl_remove_device(struct mei_cl_device *device)
196e5354107SSamuel Ortiz {
197e5354107SSamuel Ortiz 	device_unregister(&device->dev);
198e5354107SSamuel Ortiz }
199e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_remove_device);
200333e4ee0SSamuel Ortiz 
201333e4ee0SSamuel Ortiz int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner)
202333e4ee0SSamuel Ortiz {
203333e4ee0SSamuel Ortiz 	int err;
204333e4ee0SSamuel Ortiz 
205333e4ee0SSamuel Ortiz 	driver->driver.name = driver->name;
206333e4ee0SSamuel Ortiz 	driver->driver.owner = owner;
207333e4ee0SSamuel Ortiz 	driver->driver.bus = &mei_cl_bus_type;
208333e4ee0SSamuel Ortiz 
209333e4ee0SSamuel Ortiz 	err = driver_register(&driver->driver);
210333e4ee0SSamuel Ortiz 	if (err)
211333e4ee0SSamuel Ortiz 		return err;
212333e4ee0SSamuel Ortiz 
213333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] registered\n", driver->driver.name);
214333e4ee0SSamuel Ortiz 
215333e4ee0SSamuel Ortiz 	return 0;
216333e4ee0SSamuel Ortiz }
217333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
218333e4ee0SSamuel Ortiz 
219333e4ee0SSamuel Ortiz void mei_cl_driver_unregister(struct mei_cl_driver *driver)
220333e4ee0SSamuel Ortiz {
221333e4ee0SSamuel Ortiz 	driver_unregister(&driver->driver);
222333e4ee0SSamuel Ortiz 
223333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
224333e4ee0SSamuel Ortiz }
225333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
2263e833295SSamuel Ortiz 
22744d88d91SSamuel Ortiz static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
22844d88d91SSamuel Ortiz 			bool blocking)
2293e833295SSamuel Ortiz {
2303e833295SSamuel Ortiz 	struct mei_device *dev;
2313e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
2324234a6deSTomas Winkler 	int id;
2334234a6deSTomas Winkler 	int rets;
2343e833295SSamuel Ortiz 
2353e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
2363e833295SSamuel Ortiz 		return -ENODEV;
2373e833295SSamuel Ortiz 
2384234a6deSTomas Winkler 	dev = cl->dev;
2394234a6deSTomas Winkler 
2403e833295SSamuel Ortiz 	if (cl->state != MEI_FILE_CONNECTED)
2413e833295SSamuel Ortiz 		return -ENODEV;
2423e833295SSamuel Ortiz 
2434234a6deSTomas Winkler 	/* Check if we have an ME client device */
2444234a6deSTomas Winkler 	id = mei_me_cl_by_id(dev, cl->me_client_id);
2454234a6deSTomas Winkler 	if (id < 0)
2464234a6deSTomas Winkler 		return -ENODEV;
2474234a6deSTomas Winkler 
2484234a6deSTomas Winkler 	if (length > dev->me_clients[id].props.max_msg_length)
2494234a6deSTomas Winkler 		return -EINVAL;
2504234a6deSTomas Winkler 
2513e833295SSamuel Ortiz 	cb = mei_io_cb_init(cl, NULL);
2523e833295SSamuel Ortiz 	if (!cb)
2533e833295SSamuel Ortiz 		return -ENOMEM;
2543e833295SSamuel Ortiz 
2554234a6deSTomas Winkler 	rets = mei_io_cb_alloc_req_buf(cb, length);
2564234a6deSTomas Winkler 	if (rets < 0) {
2573e833295SSamuel Ortiz 		mei_io_cb_free(cb);
2584234a6deSTomas Winkler 		return rets;
2593e833295SSamuel Ortiz 	}
2603e833295SSamuel Ortiz 
2613e833295SSamuel Ortiz 	memcpy(cb->request_buffer.data, buf, length);
2623e833295SSamuel Ortiz 
2633e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
2643e833295SSamuel Ortiz 
2654234a6deSTomas Winkler 	rets = mei_cl_write(cl, cb, blocking);
2663e833295SSamuel Ortiz 
2673e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
2684234a6deSTomas Winkler 	if (rets < 0)
2693e833295SSamuel Ortiz 		mei_io_cb_free(cb);
2703e833295SSamuel Ortiz 
2714234a6deSTomas Winkler 	return rets;
2723e833295SSamuel Ortiz }
2733e833295SSamuel Ortiz 
2743e833295SSamuel Ortiz int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
2753e833295SSamuel Ortiz {
2763e833295SSamuel Ortiz 	struct mei_device *dev;
2773e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
2783e833295SSamuel Ortiz 	size_t r_length;
2793e833295SSamuel Ortiz 	int err;
2803e833295SSamuel Ortiz 
2813e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
2823e833295SSamuel Ortiz 		return -ENODEV;
2833e833295SSamuel Ortiz 
2843e833295SSamuel Ortiz 	dev = cl->dev;
2853e833295SSamuel Ortiz 
2863e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
2873e833295SSamuel Ortiz 
2883e833295SSamuel Ortiz 	if (!cl->read_cb) {
289*fcb136e1STomas Winkler 		err = mei_cl_read_start(cl, length);
2903e833295SSamuel Ortiz 		if (err < 0) {
2913e833295SSamuel Ortiz 			mutex_unlock(&dev->device_lock);
2923e833295SSamuel Ortiz 			return err;
2933e833295SSamuel Ortiz 		}
2943e833295SSamuel Ortiz 	}
2953e833295SSamuel Ortiz 
2963e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE &&
2973e833295SSamuel Ortiz 	    !waitqueue_active(&cl->rx_wait)) {
2983e833295SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
2993e833295SSamuel Ortiz 
3003e833295SSamuel Ortiz 		if (wait_event_interruptible(cl->rx_wait,
3013e833295SSamuel Ortiz 				(MEI_READ_COMPLETE == cl->reading_state))) {
3023e833295SSamuel Ortiz 			if (signal_pending(current))
3033e833295SSamuel Ortiz 				return -EINTR;
3043e833295SSamuel Ortiz 			return -ERESTARTSYS;
3053e833295SSamuel Ortiz 		}
3063e833295SSamuel Ortiz 
3073e833295SSamuel Ortiz 		mutex_lock(&dev->device_lock);
3083e833295SSamuel Ortiz 	}
3093e833295SSamuel Ortiz 
3103e833295SSamuel Ortiz 	cb = cl->read_cb;
3113e833295SSamuel Ortiz 
3123e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE) {
3133e833295SSamuel Ortiz 		r_length = 0;
3143e833295SSamuel Ortiz 		goto out;
3153e833295SSamuel Ortiz 	}
3163e833295SSamuel Ortiz 
3173e833295SSamuel Ortiz 	r_length = min_t(size_t, length, cb->buf_idx);
3183e833295SSamuel Ortiz 
3193e833295SSamuel Ortiz 	memcpy(buf, cb->response_buffer.data, r_length);
3203e833295SSamuel Ortiz 
3213e833295SSamuel Ortiz 	mei_io_cb_free(cb);
3223e833295SSamuel Ortiz 	cl->reading_state = MEI_IDLE;
3233e833295SSamuel Ortiz 	cl->read_cb = NULL;
3243e833295SSamuel Ortiz 
3253e833295SSamuel Ortiz out:
3263e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
3273e833295SSamuel Ortiz 
3283e833295SSamuel Ortiz 	return r_length;
3293e833295SSamuel Ortiz }
3303e833295SSamuel Ortiz 
33144d88d91SSamuel Ortiz inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
33244d88d91SSamuel Ortiz {
33344d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 0);
33444d88d91SSamuel Ortiz }
33544d88d91SSamuel Ortiz 
33644d88d91SSamuel Ortiz inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
33744d88d91SSamuel Ortiz {
33844d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 1);
33944d88d91SSamuel Ortiz }
34044d88d91SSamuel Ortiz 
3413e833295SSamuel Ortiz int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
3423e833295SSamuel Ortiz {
343a7b71bc0SSamuel Ortiz 	struct mei_cl *cl = device->cl;
3443e833295SSamuel Ortiz 
345a7b71bc0SSamuel Ortiz 	if (cl == NULL)
346a7b71bc0SSamuel Ortiz 		return -ENODEV;
3473e833295SSamuel Ortiz 
3483e833295SSamuel Ortiz 	if (device->ops && device->ops->send)
3493e833295SSamuel Ortiz 		return device->ops->send(device, buf, length);
3503e833295SSamuel Ortiz 
3513e833295SSamuel Ortiz 	return __mei_cl_send(cl, buf, length);
3523e833295SSamuel Ortiz }
3533e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_send);
3543e833295SSamuel Ortiz 
3553e833295SSamuel Ortiz int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
3563e833295SSamuel Ortiz {
357a7b71bc0SSamuel Ortiz 	struct mei_cl *cl =  device->cl;
3583e833295SSamuel Ortiz 
359a7b71bc0SSamuel Ortiz 	if (cl == NULL)
360a7b71bc0SSamuel Ortiz 		return -ENODEV;
3613e833295SSamuel Ortiz 
3623e833295SSamuel Ortiz 	if (device->ops && device->ops->recv)
3633e833295SSamuel Ortiz 		return device->ops->recv(device, buf, length);
3643e833295SSamuel Ortiz 
3653e833295SSamuel Ortiz 	return __mei_cl_recv(cl, buf, length);
3663e833295SSamuel Ortiz }
3673e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_recv);
3683e833295SSamuel Ortiz 
3693e833295SSamuel Ortiz static void mei_bus_event_work(struct work_struct *work)
3703e833295SSamuel Ortiz {
3713e833295SSamuel Ortiz 	struct mei_cl_device *device;
3723e833295SSamuel Ortiz 
3733e833295SSamuel Ortiz 	device = container_of(work, struct mei_cl_device, event_work);
3743e833295SSamuel Ortiz 
3753e833295SSamuel Ortiz 	if (device->event_cb)
3763e833295SSamuel Ortiz 		device->event_cb(device, device->events, device->event_context);
3773e833295SSamuel Ortiz 
3783e833295SSamuel Ortiz 	device->events = 0;
3793e833295SSamuel Ortiz 
3803e833295SSamuel Ortiz 	/* Prepare for the next read */
381*fcb136e1STomas Winkler 	mei_cl_read_start(device->cl, 0);
3823e833295SSamuel Ortiz }
3833e833295SSamuel Ortiz 
3843e833295SSamuel Ortiz int mei_cl_register_event_cb(struct mei_cl_device *device,
3853e833295SSamuel Ortiz 			  mei_cl_event_cb_t event_cb, void *context)
3863e833295SSamuel Ortiz {
3873e833295SSamuel Ortiz 	if (device->event_cb)
3883e833295SSamuel Ortiz 		return -EALREADY;
3893e833295SSamuel Ortiz 
3903e833295SSamuel Ortiz 	device->events = 0;
3913e833295SSamuel Ortiz 	device->event_cb = event_cb;
3923e833295SSamuel Ortiz 	device->event_context = context;
3933e833295SSamuel Ortiz 	INIT_WORK(&device->event_work, mei_bus_event_work);
3943e833295SSamuel Ortiz 
395*fcb136e1STomas Winkler 	mei_cl_read_start(device->cl, 0);
3963e833295SSamuel Ortiz 
3973e833295SSamuel Ortiz 	return 0;
3983e833295SSamuel Ortiz }
3993e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
400cf3baefbSSamuel Ortiz 
401aa6aef21SSamuel Ortiz void *mei_cl_get_drvdata(const struct mei_cl_device *device)
402aa6aef21SSamuel Ortiz {
403aa6aef21SSamuel Ortiz 	return dev_get_drvdata(&device->dev);
404aa6aef21SSamuel Ortiz }
405aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
406aa6aef21SSamuel Ortiz 
407aa6aef21SSamuel Ortiz void mei_cl_set_drvdata(struct mei_cl_device *device, void *data)
408aa6aef21SSamuel Ortiz {
409aa6aef21SSamuel Ortiz 	dev_set_drvdata(&device->dev, data);
410aa6aef21SSamuel Ortiz }
411aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
412aa6aef21SSamuel Ortiz 
413e46980a1SSamuel Ortiz int mei_cl_enable_device(struct mei_cl_device *device)
414e46980a1SSamuel Ortiz {
415e46980a1SSamuel Ortiz 	int err;
416e46980a1SSamuel Ortiz 	struct mei_device *dev;
417e46980a1SSamuel Ortiz 	struct mei_cl *cl = device->cl;
418e46980a1SSamuel Ortiz 
419e46980a1SSamuel Ortiz 	if (cl == NULL)
420e46980a1SSamuel Ortiz 		return -ENODEV;
421e46980a1SSamuel Ortiz 
422e46980a1SSamuel Ortiz 	dev = cl->dev;
423e46980a1SSamuel Ortiz 
424e46980a1SSamuel Ortiz 	mutex_lock(&dev->device_lock);
425e46980a1SSamuel Ortiz 
426e46980a1SSamuel Ortiz 	cl->state = MEI_FILE_CONNECTING;
427e46980a1SSamuel Ortiz 
428e46980a1SSamuel Ortiz 	err = mei_cl_connect(cl, NULL);
429e46980a1SSamuel Ortiz 	if (err < 0) {
430e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
431e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Could not connect to the ME client");
432e46980a1SSamuel Ortiz 
433e46980a1SSamuel Ortiz 		return err;
434e46980a1SSamuel Ortiz 	}
435e46980a1SSamuel Ortiz 
436e46980a1SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
437e46980a1SSamuel Ortiz 
438e46980a1SSamuel Ortiz 	if (device->event_cb && !cl->read_cb)
439*fcb136e1STomas Winkler 		mei_cl_read_start(device->cl, 0);
440e46980a1SSamuel Ortiz 
441e46980a1SSamuel Ortiz 	if (!device->ops || !device->ops->enable)
442e46980a1SSamuel Ortiz 		return 0;
443e46980a1SSamuel Ortiz 
444e46980a1SSamuel Ortiz 	return device->ops->enable(device);
445e46980a1SSamuel Ortiz }
446e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_enable_device);
447e46980a1SSamuel Ortiz 
448e46980a1SSamuel Ortiz int mei_cl_disable_device(struct mei_cl_device *device)
449e46980a1SSamuel Ortiz {
450e46980a1SSamuel Ortiz 	int err;
451e46980a1SSamuel Ortiz 	struct mei_device *dev;
452e46980a1SSamuel Ortiz 	struct mei_cl *cl = device->cl;
453e46980a1SSamuel Ortiz 
454e46980a1SSamuel Ortiz 	if (cl == NULL)
455e46980a1SSamuel Ortiz 		return -ENODEV;
456e46980a1SSamuel Ortiz 
457e46980a1SSamuel Ortiz 	dev = cl->dev;
458e46980a1SSamuel Ortiz 
459e46980a1SSamuel Ortiz 	mutex_lock(&dev->device_lock);
460e46980a1SSamuel Ortiz 
461e46980a1SSamuel Ortiz 	if (cl->state != MEI_FILE_CONNECTED) {
462e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
463e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Already disconnected");
464e46980a1SSamuel Ortiz 
465e46980a1SSamuel Ortiz 		return 0;
466e46980a1SSamuel Ortiz 	}
467e46980a1SSamuel Ortiz 
468e46980a1SSamuel Ortiz 	cl->state = MEI_FILE_DISCONNECTING;
469e46980a1SSamuel Ortiz 
470e46980a1SSamuel Ortiz 	err = mei_cl_disconnect(cl);
471e46980a1SSamuel Ortiz 	if (err < 0) {
472e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
473e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev,
474e46980a1SSamuel Ortiz 			"Could not disconnect from the ME client");
475e46980a1SSamuel Ortiz 
476e46980a1SSamuel Ortiz 		return err;
477e46980a1SSamuel Ortiz 	}
478e46980a1SSamuel Ortiz 
479e46980a1SSamuel Ortiz 	/* Flush queues and remove any pending read */
480e46980a1SSamuel Ortiz 	mei_cl_flush_queues(cl);
481e46980a1SSamuel Ortiz 
482e46980a1SSamuel Ortiz 	if (cl->read_cb) {
483e46980a1SSamuel Ortiz 		struct mei_cl_cb *cb = NULL;
484e46980a1SSamuel Ortiz 
485e46980a1SSamuel Ortiz 		cb = mei_cl_find_read_cb(cl);
486e46980a1SSamuel Ortiz 		/* Remove entry from read list */
487e46980a1SSamuel Ortiz 		if (cb)
488e46980a1SSamuel Ortiz 			list_del(&cb->list);
489e46980a1SSamuel Ortiz 
490e46980a1SSamuel Ortiz 		cb = cl->read_cb;
491e46980a1SSamuel Ortiz 		cl->read_cb = NULL;
492e46980a1SSamuel Ortiz 
493e46980a1SSamuel Ortiz 		if (cb) {
494e46980a1SSamuel Ortiz 			mei_io_cb_free(cb);
495e46980a1SSamuel Ortiz 			cb = NULL;
496e46980a1SSamuel Ortiz 		}
497e46980a1SSamuel Ortiz 	}
498e46980a1SSamuel Ortiz 
499e46980a1SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
500e46980a1SSamuel Ortiz 
501e46980a1SSamuel Ortiz 	if (!device->ops || !device->ops->disable)
502e46980a1SSamuel Ortiz 		return 0;
503e46980a1SSamuel Ortiz 
504e46980a1SSamuel Ortiz 	return device->ops->disable(device);
505e46980a1SSamuel Ortiz }
506e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_disable_device);
507e46980a1SSamuel Ortiz 
508cf3baefbSSamuel Ortiz void mei_cl_bus_rx_event(struct mei_cl *cl)
509cf3baefbSSamuel Ortiz {
510cf3baefbSSamuel Ortiz 	struct mei_cl_device *device = cl->device;
511cf3baefbSSamuel Ortiz 
512cf3baefbSSamuel Ortiz 	if (!device || !device->event_cb)
513cf3baefbSSamuel Ortiz 		return;
514cf3baefbSSamuel Ortiz 
515cf3baefbSSamuel Ortiz 	set_bit(MEI_CL_EVENT_RX, &device->events);
516cf3baefbSSamuel Ortiz 
517cf3baefbSSamuel Ortiz 	schedule_work(&device->event_work);
518cf3baefbSSamuel Ortiz }
519cf3baefbSSamuel Ortiz 
520cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void)
521cf3baefbSSamuel Ortiz {
522cf3baefbSSamuel Ortiz 	return bus_register(&mei_cl_bus_type);
523cf3baefbSSamuel Ortiz }
524cf3baefbSSamuel Ortiz 
525cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void)
526cf3baefbSSamuel Ortiz {
527cf3baefbSSamuel Ortiz 	bus_unregister(&mei_cl_bus_type);
528cf3baefbSSamuel Ortiz }
529