xref: /linux/drivers/misc/mei/bus.c (revision 32f389ec5689751bae758c9b3e3982b2acb696c1)
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 }
111*32f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias);
112e5354107SSamuel Ortiz 
113*32f389ecSGreg Kroah-Hartman static struct attribute *mei_cl_dev_attrs[] = {
114*32f389ecSGreg Kroah-Hartman 	&dev_attr_modalias.attr,
115*32f389ecSGreg Kroah-Hartman 	NULL,
116e5354107SSamuel Ortiz };
117*32f389ecSGreg Kroah-Hartman ATTRIBUTE_GROUPS(mei_cl_dev);
118e5354107SSamuel Ortiz 
119e5354107SSamuel Ortiz static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
120e5354107SSamuel Ortiz {
121e5354107SSamuel Ortiz 	if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
122e5354107SSamuel Ortiz 		return -ENOMEM;
123e5354107SSamuel Ortiz 
124e5354107SSamuel Ortiz 	return 0;
125e5354107SSamuel Ortiz }
126e5354107SSamuel Ortiz 
127e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = {
128e5354107SSamuel Ortiz 	.name		= "mei",
129*32f389ecSGreg Kroah-Hartman 	.dev_groups	= mei_cl_dev_groups,
130e5354107SSamuel Ortiz 	.match		= mei_cl_device_match,
131e5354107SSamuel Ortiz 	.probe		= mei_cl_device_probe,
132e5354107SSamuel Ortiz 	.remove		= mei_cl_device_remove,
133e5354107SSamuel Ortiz 	.uevent		= mei_cl_uevent,
134e5354107SSamuel Ortiz };
135e5354107SSamuel Ortiz 
136e5354107SSamuel Ortiz static void mei_cl_dev_release(struct device *dev)
137e5354107SSamuel Ortiz {
138e5354107SSamuel Ortiz 	kfree(to_mei_cl_device(dev));
139e5354107SSamuel Ortiz }
140e5354107SSamuel Ortiz 
141e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = {
142e5354107SSamuel Ortiz 	.release	= mei_cl_dev_release,
143e5354107SSamuel Ortiz };
144e5354107SSamuel Ortiz 
145a7b71bc0SSamuel Ortiz static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev,
146a7b71bc0SSamuel Ortiz 						uuid_le uuid)
147a7b71bc0SSamuel Ortiz {
148a7b71bc0SSamuel Ortiz 	struct mei_cl *cl, *next;
149a7b71bc0SSamuel Ortiz 
150a7b71bc0SSamuel Ortiz 	list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
151a7b71bc0SSamuel Ortiz 		if (!uuid_le_cmp(uuid, cl->device_uuid))
152a7b71bc0SSamuel Ortiz 			return cl;
153a7b71bc0SSamuel Ortiz 	}
154a7b71bc0SSamuel Ortiz 
155a7b71bc0SSamuel Ortiz 	return NULL;
156a7b71bc0SSamuel Ortiz }
157a7b71bc0SSamuel Ortiz struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
158e46980a1SSamuel Ortiz 					uuid_le uuid, char *name,
159e46980a1SSamuel Ortiz 					struct mei_cl_ops *ops)
160e5354107SSamuel Ortiz {
161e5354107SSamuel Ortiz 	struct mei_cl_device *device;
162a7b71bc0SSamuel Ortiz 	struct mei_cl *cl;
163e5354107SSamuel Ortiz 	int status;
164e5354107SSamuel Ortiz 
165a7b71bc0SSamuel Ortiz 	cl = mei_bus_find_mei_cl_by_uuid(dev, uuid);
166a7b71bc0SSamuel Ortiz 	if (cl == NULL)
167a7b71bc0SSamuel Ortiz 		return NULL;
168a7b71bc0SSamuel Ortiz 
169e5354107SSamuel Ortiz 	device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
170e5354107SSamuel Ortiz 	if (!device)
171e5354107SSamuel Ortiz 		return NULL;
172e5354107SSamuel Ortiz 
173a7b71bc0SSamuel Ortiz 	device->cl = cl;
174e46980a1SSamuel Ortiz 	device->ops = ops;
175a7b71bc0SSamuel Ortiz 
176a7b71bc0SSamuel Ortiz 	device->dev.parent = &dev->pdev->dev;
177e5354107SSamuel Ortiz 	device->dev.bus = &mei_cl_bus_type;
178e5354107SSamuel Ortiz 	device->dev.type = &mei_cl_device_type;
179e5354107SSamuel Ortiz 
180e5354107SSamuel Ortiz 	dev_set_name(&device->dev, "%s", name);
181e5354107SSamuel Ortiz 
182e5354107SSamuel Ortiz 	status = device_register(&device->dev);
183a7b71bc0SSamuel Ortiz 	if (status) {
184a7b71bc0SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Failed to register MEI device\n");
185a7b71bc0SSamuel Ortiz 		kfree(device);
186a7b71bc0SSamuel Ortiz 		return NULL;
187a7b71bc0SSamuel Ortiz 	}
188a7b71bc0SSamuel Ortiz 
189a7b71bc0SSamuel Ortiz 	cl->device = device;
190e5354107SSamuel Ortiz 
191e5354107SSamuel Ortiz 	dev_dbg(&device->dev, "client %s registered\n", name);
192e5354107SSamuel Ortiz 
193e5354107SSamuel Ortiz 	return device;
194e5354107SSamuel Ortiz }
195e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_add_device);
196e5354107SSamuel Ortiz 
197e5354107SSamuel Ortiz void mei_cl_remove_device(struct mei_cl_device *device)
198e5354107SSamuel Ortiz {
199e5354107SSamuel Ortiz 	device_unregister(&device->dev);
200e5354107SSamuel Ortiz }
201e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_remove_device);
202333e4ee0SSamuel Ortiz 
203333e4ee0SSamuel Ortiz int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner)
204333e4ee0SSamuel Ortiz {
205333e4ee0SSamuel Ortiz 	int err;
206333e4ee0SSamuel Ortiz 
207333e4ee0SSamuel Ortiz 	driver->driver.name = driver->name;
208333e4ee0SSamuel Ortiz 	driver->driver.owner = owner;
209333e4ee0SSamuel Ortiz 	driver->driver.bus = &mei_cl_bus_type;
210333e4ee0SSamuel Ortiz 
211333e4ee0SSamuel Ortiz 	err = driver_register(&driver->driver);
212333e4ee0SSamuel Ortiz 	if (err)
213333e4ee0SSamuel Ortiz 		return err;
214333e4ee0SSamuel Ortiz 
215333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] registered\n", driver->driver.name);
216333e4ee0SSamuel Ortiz 
217333e4ee0SSamuel Ortiz 	return 0;
218333e4ee0SSamuel Ortiz }
219333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
220333e4ee0SSamuel Ortiz 
221333e4ee0SSamuel Ortiz void mei_cl_driver_unregister(struct mei_cl_driver *driver)
222333e4ee0SSamuel Ortiz {
223333e4ee0SSamuel Ortiz 	driver_unregister(&driver->driver);
224333e4ee0SSamuel Ortiz 
225333e4ee0SSamuel Ortiz 	pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
226333e4ee0SSamuel Ortiz }
227333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
2283e833295SSamuel Ortiz 
22944d88d91SSamuel Ortiz static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
23044d88d91SSamuel Ortiz 			bool blocking)
2313e833295SSamuel Ortiz {
2323e833295SSamuel Ortiz 	struct mei_device *dev;
2333e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
2344234a6deSTomas Winkler 	int id;
2354234a6deSTomas Winkler 	int rets;
2363e833295SSamuel Ortiz 
2373e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
2383e833295SSamuel Ortiz 		return -ENODEV;
2393e833295SSamuel Ortiz 
2404234a6deSTomas Winkler 	dev = cl->dev;
2414234a6deSTomas Winkler 
2423e833295SSamuel Ortiz 	if (cl->state != MEI_FILE_CONNECTED)
2433e833295SSamuel Ortiz 		return -ENODEV;
2443e833295SSamuel Ortiz 
2454234a6deSTomas Winkler 	/* Check if we have an ME client device */
2464234a6deSTomas Winkler 	id = mei_me_cl_by_id(dev, cl->me_client_id);
2474234a6deSTomas Winkler 	if (id < 0)
2484234a6deSTomas Winkler 		return -ENODEV;
2494234a6deSTomas Winkler 
2504234a6deSTomas Winkler 	if (length > dev->me_clients[id].props.max_msg_length)
2514234a6deSTomas Winkler 		return -EINVAL;
2524234a6deSTomas Winkler 
2533e833295SSamuel Ortiz 	cb = mei_io_cb_init(cl, NULL);
2543e833295SSamuel Ortiz 	if (!cb)
2553e833295SSamuel Ortiz 		return -ENOMEM;
2563e833295SSamuel Ortiz 
2574234a6deSTomas Winkler 	rets = mei_io_cb_alloc_req_buf(cb, length);
2584234a6deSTomas Winkler 	if (rets < 0) {
2593e833295SSamuel Ortiz 		mei_io_cb_free(cb);
2604234a6deSTomas Winkler 		return rets;
2613e833295SSamuel Ortiz 	}
2623e833295SSamuel Ortiz 
2633e833295SSamuel Ortiz 	memcpy(cb->request_buffer.data, buf, length);
2643e833295SSamuel Ortiz 
2653e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
2663e833295SSamuel Ortiz 
2674234a6deSTomas Winkler 	rets = mei_cl_write(cl, cb, blocking);
2683e833295SSamuel Ortiz 
2693e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
2704234a6deSTomas Winkler 	if (rets < 0)
2713e833295SSamuel Ortiz 		mei_io_cb_free(cb);
2723e833295SSamuel Ortiz 
2734234a6deSTomas Winkler 	return rets;
2743e833295SSamuel Ortiz }
2753e833295SSamuel Ortiz 
2763e833295SSamuel Ortiz int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
2773e833295SSamuel Ortiz {
2783e833295SSamuel Ortiz 	struct mei_device *dev;
2793e833295SSamuel Ortiz 	struct mei_cl_cb *cb;
2803e833295SSamuel Ortiz 	size_t r_length;
2813e833295SSamuel Ortiz 	int err;
2823e833295SSamuel Ortiz 
2833e833295SSamuel Ortiz 	if (WARN_ON(!cl || !cl->dev))
2843e833295SSamuel Ortiz 		return -ENODEV;
2853e833295SSamuel Ortiz 
2863e833295SSamuel Ortiz 	dev = cl->dev;
2873e833295SSamuel Ortiz 
2883e833295SSamuel Ortiz 	mutex_lock(&dev->device_lock);
2893e833295SSamuel Ortiz 
2903e833295SSamuel Ortiz 	if (!cl->read_cb) {
291fcb136e1STomas Winkler 		err = mei_cl_read_start(cl, length);
2923e833295SSamuel Ortiz 		if (err < 0) {
2933e833295SSamuel Ortiz 			mutex_unlock(&dev->device_lock);
2943e833295SSamuel Ortiz 			return err;
2953e833295SSamuel Ortiz 		}
2963e833295SSamuel Ortiz 	}
2973e833295SSamuel Ortiz 
2983e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE &&
2993e833295SSamuel Ortiz 	    !waitqueue_active(&cl->rx_wait)) {
3003e833295SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
3013e833295SSamuel Ortiz 
3023e833295SSamuel Ortiz 		if (wait_event_interruptible(cl->rx_wait,
3033e833295SSamuel Ortiz 				(MEI_READ_COMPLETE == cl->reading_state))) {
3043e833295SSamuel Ortiz 			if (signal_pending(current))
3053e833295SSamuel Ortiz 				return -EINTR;
3063e833295SSamuel Ortiz 			return -ERESTARTSYS;
3073e833295SSamuel Ortiz 		}
3083e833295SSamuel Ortiz 
3093e833295SSamuel Ortiz 		mutex_lock(&dev->device_lock);
3103e833295SSamuel Ortiz 	}
3113e833295SSamuel Ortiz 
3123e833295SSamuel Ortiz 	cb = cl->read_cb;
3133e833295SSamuel Ortiz 
3143e833295SSamuel Ortiz 	if (cl->reading_state != MEI_READ_COMPLETE) {
3153e833295SSamuel Ortiz 		r_length = 0;
3163e833295SSamuel Ortiz 		goto out;
3173e833295SSamuel Ortiz 	}
3183e833295SSamuel Ortiz 
3193e833295SSamuel Ortiz 	r_length = min_t(size_t, length, cb->buf_idx);
3203e833295SSamuel Ortiz 
3213e833295SSamuel Ortiz 	memcpy(buf, cb->response_buffer.data, r_length);
3223e833295SSamuel Ortiz 
3233e833295SSamuel Ortiz 	mei_io_cb_free(cb);
3243e833295SSamuel Ortiz 	cl->reading_state = MEI_IDLE;
3253e833295SSamuel Ortiz 	cl->read_cb = NULL;
3263e833295SSamuel Ortiz 
3273e833295SSamuel Ortiz out:
3283e833295SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
3293e833295SSamuel Ortiz 
3303e833295SSamuel Ortiz 	return r_length;
3313e833295SSamuel Ortiz }
3323e833295SSamuel Ortiz 
33344d88d91SSamuel Ortiz inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
33444d88d91SSamuel Ortiz {
33544d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 0);
33644d88d91SSamuel Ortiz }
33744d88d91SSamuel Ortiz 
33844d88d91SSamuel Ortiz inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
33944d88d91SSamuel Ortiz {
34044d88d91SSamuel Ortiz 	return ___mei_cl_send(cl, buf, length, 1);
34144d88d91SSamuel Ortiz }
34244d88d91SSamuel Ortiz 
3433e833295SSamuel Ortiz int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
3443e833295SSamuel Ortiz {
345a7b71bc0SSamuel Ortiz 	struct mei_cl *cl = device->cl;
3463e833295SSamuel Ortiz 
347a7b71bc0SSamuel Ortiz 	if (cl == NULL)
348a7b71bc0SSamuel Ortiz 		return -ENODEV;
3493e833295SSamuel Ortiz 
3503e833295SSamuel Ortiz 	if (device->ops && device->ops->send)
3513e833295SSamuel Ortiz 		return device->ops->send(device, buf, length);
3523e833295SSamuel Ortiz 
3533e833295SSamuel Ortiz 	return __mei_cl_send(cl, buf, length);
3543e833295SSamuel Ortiz }
3553e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_send);
3563e833295SSamuel Ortiz 
3573e833295SSamuel Ortiz int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
3583e833295SSamuel Ortiz {
359a7b71bc0SSamuel Ortiz 	struct mei_cl *cl =  device->cl;
3603e833295SSamuel Ortiz 
361a7b71bc0SSamuel Ortiz 	if (cl == NULL)
362a7b71bc0SSamuel Ortiz 		return -ENODEV;
3633e833295SSamuel Ortiz 
3643e833295SSamuel Ortiz 	if (device->ops && device->ops->recv)
3653e833295SSamuel Ortiz 		return device->ops->recv(device, buf, length);
3663e833295SSamuel Ortiz 
3673e833295SSamuel Ortiz 	return __mei_cl_recv(cl, buf, length);
3683e833295SSamuel Ortiz }
3693e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_recv);
3703e833295SSamuel Ortiz 
3713e833295SSamuel Ortiz static void mei_bus_event_work(struct work_struct *work)
3723e833295SSamuel Ortiz {
3733e833295SSamuel Ortiz 	struct mei_cl_device *device;
3743e833295SSamuel Ortiz 
3753e833295SSamuel Ortiz 	device = container_of(work, struct mei_cl_device, event_work);
3763e833295SSamuel Ortiz 
3773e833295SSamuel Ortiz 	if (device->event_cb)
3783e833295SSamuel Ortiz 		device->event_cb(device, device->events, device->event_context);
3793e833295SSamuel Ortiz 
3803e833295SSamuel Ortiz 	device->events = 0;
3813e833295SSamuel Ortiz 
3823e833295SSamuel Ortiz 	/* Prepare for the next read */
383fcb136e1STomas Winkler 	mei_cl_read_start(device->cl, 0);
3843e833295SSamuel Ortiz }
3853e833295SSamuel Ortiz 
3863e833295SSamuel Ortiz int mei_cl_register_event_cb(struct mei_cl_device *device,
3873e833295SSamuel Ortiz 			  mei_cl_event_cb_t event_cb, void *context)
3883e833295SSamuel Ortiz {
3893e833295SSamuel Ortiz 	if (device->event_cb)
3903e833295SSamuel Ortiz 		return -EALREADY;
3913e833295SSamuel Ortiz 
3923e833295SSamuel Ortiz 	device->events = 0;
3933e833295SSamuel Ortiz 	device->event_cb = event_cb;
3943e833295SSamuel Ortiz 	device->event_context = context;
3953e833295SSamuel Ortiz 	INIT_WORK(&device->event_work, mei_bus_event_work);
3963e833295SSamuel Ortiz 
397fcb136e1STomas Winkler 	mei_cl_read_start(device->cl, 0);
3983e833295SSamuel Ortiz 
3993e833295SSamuel Ortiz 	return 0;
4003e833295SSamuel Ortiz }
4013e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
402cf3baefbSSamuel Ortiz 
403aa6aef21SSamuel Ortiz void *mei_cl_get_drvdata(const struct mei_cl_device *device)
404aa6aef21SSamuel Ortiz {
405aa6aef21SSamuel Ortiz 	return dev_get_drvdata(&device->dev);
406aa6aef21SSamuel Ortiz }
407aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
408aa6aef21SSamuel Ortiz 
409aa6aef21SSamuel Ortiz void mei_cl_set_drvdata(struct mei_cl_device *device, void *data)
410aa6aef21SSamuel Ortiz {
411aa6aef21SSamuel Ortiz 	dev_set_drvdata(&device->dev, data);
412aa6aef21SSamuel Ortiz }
413aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
414aa6aef21SSamuel Ortiz 
415e46980a1SSamuel Ortiz int mei_cl_enable_device(struct mei_cl_device *device)
416e46980a1SSamuel Ortiz {
417e46980a1SSamuel Ortiz 	int err;
418e46980a1SSamuel Ortiz 	struct mei_device *dev;
419e46980a1SSamuel Ortiz 	struct mei_cl *cl = device->cl;
420e46980a1SSamuel Ortiz 
421e46980a1SSamuel Ortiz 	if (cl == NULL)
422e46980a1SSamuel Ortiz 		return -ENODEV;
423e46980a1SSamuel Ortiz 
424e46980a1SSamuel Ortiz 	dev = cl->dev;
425e46980a1SSamuel Ortiz 
426e46980a1SSamuel Ortiz 	mutex_lock(&dev->device_lock);
427e46980a1SSamuel Ortiz 
428e46980a1SSamuel Ortiz 	cl->state = MEI_FILE_CONNECTING;
429e46980a1SSamuel Ortiz 
430e46980a1SSamuel Ortiz 	err = mei_cl_connect(cl, NULL);
431e46980a1SSamuel Ortiz 	if (err < 0) {
432e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
433e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Could not connect to the ME client");
434e46980a1SSamuel Ortiz 
435e46980a1SSamuel Ortiz 		return err;
436e46980a1SSamuel Ortiz 	}
437e46980a1SSamuel Ortiz 
438e46980a1SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
439e46980a1SSamuel Ortiz 
440e46980a1SSamuel Ortiz 	if (device->event_cb && !cl->read_cb)
441fcb136e1STomas Winkler 		mei_cl_read_start(device->cl, 0);
442e46980a1SSamuel Ortiz 
443e46980a1SSamuel Ortiz 	if (!device->ops || !device->ops->enable)
444e46980a1SSamuel Ortiz 		return 0;
445e46980a1SSamuel Ortiz 
446e46980a1SSamuel Ortiz 	return device->ops->enable(device);
447e46980a1SSamuel Ortiz }
448e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_enable_device);
449e46980a1SSamuel Ortiz 
450e46980a1SSamuel Ortiz int mei_cl_disable_device(struct mei_cl_device *device)
451e46980a1SSamuel Ortiz {
452e46980a1SSamuel Ortiz 	int err;
453e46980a1SSamuel Ortiz 	struct mei_device *dev;
454e46980a1SSamuel Ortiz 	struct mei_cl *cl = device->cl;
455e46980a1SSamuel Ortiz 
456e46980a1SSamuel Ortiz 	if (cl == NULL)
457e46980a1SSamuel Ortiz 		return -ENODEV;
458e46980a1SSamuel Ortiz 
459e46980a1SSamuel Ortiz 	dev = cl->dev;
460e46980a1SSamuel Ortiz 
461e46980a1SSamuel Ortiz 	mutex_lock(&dev->device_lock);
462e46980a1SSamuel Ortiz 
463e46980a1SSamuel Ortiz 	if (cl->state != MEI_FILE_CONNECTED) {
464e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
465e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev, "Already disconnected");
466e46980a1SSamuel Ortiz 
467e46980a1SSamuel Ortiz 		return 0;
468e46980a1SSamuel Ortiz 	}
469e46980a1SSamuel Ortiz 
470e46980a1SSamuel Ortiz 	cl->state = MEI_FILE_DISCONNECTING;
471e46980a1SSamuel Ortiz 
472e46980a1SSamuel Ortiz 	err = mei_cl_disconnect(cl);
473e46980a1SSamuel Ortiz 	if (err < 0) {
474e46980a1SSamuel Ortiz 		mutex_unlock(&dev->device_lock);
475e46980a1SSamuel Ortiz 		dev_err(&dev->pdev->dev,
476e46980a1SSamuel Ortiz 			"Could not disconnect from the ME client");
477e46980a1SSamuel Ortiz 
478e46980a1SSamuel Ortiz 		return err;
479e46980a1SSamuel Ortiz 	}
480e46980a1SSamuel Ortiz 
481e46980a1SSamuel Ortiz 	/* Flush queues and remove any pending read */
482e46980a1SSamuel Ortiz 	mei_cl_flush_queues(cl);
483e46980a1SSamuel Ortiz 
484e46980a1SSamuel Ortiz 	if (cl->read_cb) {
485e46980a1SSamuel Ortiz 		struct mei_cl_cb *cb = NULL;
486e46980a1SSamuel Ortiz 
487e46980a1SSamuel Ortiz 		cb = mei_cl_find_read_cb(cl);
488e46980a1SSamuel Ortiz 		/* Remove entry from read list */
489e46980a1SSamuel Ortiz 		if (cb)
490e46980a1SSamuel Ortiz 			list_del(&cb->list);
491e46980a1SSamuel Ortiz 
492e46980a1SSamuel Ortiz 		cb = cl->read_cb;
493e46980a1SSamuel Ortiz 		cl->read_cb = NULL;
494e46980a1SSamuel Ortiz 
495e46980a1SSamuel Ortiz 		if (cb) {
496e46980a1SSamuel Ortiz 			mei_io_cb_free(cb);
497e46980a1SSamuel Ortiz 			cb = NULL;
498e46980a1SSamuel Ortiz 		}
499e46980a1SSamuel Ortiz 	}
500e46980a1SSamuel Ortiz 
501bbedf2fcSSamuel Ortiz 	device->event_cb = NULL;
502bbedf2fcSSamuel Ortiz 
503e46980a1SSamuel Ortiz 	mutex_unlock(&dev->device_lock);
504e46980a1SSamuel Ortiz 
505e46980a1SSamuel Ortiz 	if (!device->ops || !device->ops->disable)
506e46980a1SSamuel Ortiz 		return 0;
507e46980a1SSamuel Ortiz 
508e46980a1SSamuel Ortiz 	return device->ops->disable(device);
509e46980a1SSamuel Ortiz }
510e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_disable_device);
511e46980a1SSamuel Ortiz 
512cf3baefbSSamuel Ortiz void mei_cl_bus_rx_event(struct mei_cl *cl)
513cf3baefbSSamuel Ortiz {
514cf3baefbSSamuel Ortiz 	struct mei_cl_device *device = cl->device;
515cf3baefbSSamuel Ortiz 
516cf3baefbSSamuel Ortiz 	if (!device || !device->event_cb)
517cf3baefbSSamuel Ortiz 		return;
518cf3baefbSSamuel Ortiz 
519cf3baefbSSamuel Ortiz 	set_bit(MEI_CL_EVENT_RX, &device->events);
520cf3baefbSSamuel Ortiz 
521cf3baefbSSamuel Ortiz 	schedule_work(&device->event_work);
522cf3baefbSSamuel Ortiz }
523cf3baefbSSamuel Ortiz 
524cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void)
525cf3baefbSSamuel Ortiz {
526cf3baefbSSamuel Ortiz 	return bus_register(&mei_cl_bus_type);
527cf3baefbSSamuel Ortiz }
528cf3baefbSSamuel Ortiz 
529cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void)
530cf3baefbSSamuel Ortiz {
531cf3baefbSSamuel Ortiz 	bus_unregister(&mei_cl_bus_type);
532cf3baefbSSamuel Ortiz }
533