blob: 17b00baa53b1cc462984a8ac64ae8a69bff75b74 [file] [log] [blame]
Samuel Ortize5354102013-03-27 17:29:53 +02001/*
2 * Intel Management Engine Interface (Intel MEI) Linux driver
3 * Copyright (c) 2012-2013, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/device.h>
18#include <linux/kernel.h>
Samuel Ortiz3e833292013-03-27 17:29:55 +020019#include <linux/sched.h>
Samuel Ortize5354102013-03-27 17:29:53 +020020#include <linux/init.h>
21#include <linux/errno.h>
22#include <linux/slab.h>
23#include <linux/mutex.h>
24#include <linux/interrupt.h>
Samuel Ortize5354102013-03-27 17:29:53 +020025#include <linux/mei_cl_bus.h>
26
27#include "mei_dev.h"
Samuel Ortiz3e833292013-03-27 17:29:55 +020028#include "client.h"
Samuel Ortize5354102013-03-27 17:29:53 +020029
30#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
31#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
32
Tomas Winklerc93b76b2015-05-07 15:54:02 +030033static inline uuid_le uuid_le_cast(const __u8 uuid[16])
34{
35 return *(uuid_le *)uuid;
36}
37
Samuel Ortize5354102013-03-27 17:29:53 +020038static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
39{
40 struct mei_cl_device *device = to_mei_cl_device(dev);
41 struct mei_cl_driver *driver = to_mei_cl_driver(drv);
42 const struct mei_cl_device_id *id;
Tomas Winklerc93b76b2015-05-07 15:54:02 +030043 const uuid_le *uuid;
44 const char *name;
Samuel Ortize5354102013-03-27 17:29:53 +020045
46 if (!device)
47 return 0;
48
Tomas Winklerc93b76b2015-05-07 15:54:02 +030049 uuid = mei_me_cl_uuid(device->me_cl);
50 name = device->name;
51
Samuel Ortize5354102013-03-27 17:29:53 +020052 if (!driver || !driver->id_table)
53 return 0;
54
55 id = driver->id_table;
56
Tomas Winklerc93b76b2015-05-07 15:54:02 +030057 while (uuid_le_cmp(NULL_UUID_LE, uuid_le_cast(id->uuid))) {
58
59 if (!uuid_le_cmp(*uuid, uuid_le_cast(id->uuid))) {
60 if (id->name[0]) {
61 if (!strncmp(name, id->name, sizeof(id->name)))
62 return 1;
63 } else {
64 return 1;
65 }
66 }
Samuel Ortize5354102013-03-27 17:29:53 +020067
68 id++;
69 }
70
71 return 0;
72}
73
74static int mei_cl_device_probe(struct device *dev)
75{
76 struct mei_cl_device *device = to_mei_cl_device(dev);
77 struct mei_cl_driver *driver;
78 struct mei_cl_device_id id;
79
80 if (!device)
81 return 0;
82
83 driver = to_mei_cl_driver(dev->driver);
84 if (!driver || !driver->probe)
85 return -ENODEV;
86
87 dev_dbg(dev, "Device probe\n");
88
Tomas Winklerc93b76b2015-05-07 15:54:02 +030089 strlcpy(id.name, device->name, sizeof(id.name));
Samuel Ortize5354102013-03-27 17:29:53 +020090
91 return driver->probe(device, &id);
92}
93
94static int mei_cl_device_remove(struct device *dev)
95{
96 struct mei_cl_device *device = to_mei_cl_device(dev);
97 struct mei_cl_driver *driver;
98
99 if (!device || !dev->driver)
100 return 0;
101
Samuel Ortiz3e833292013-03-27 17:29:55 +0200102 if (device->event_cb) {
103 device->event_cb = NULL;
104 cancel_work_sync(&device->event_work);
105 }
106
Samuel Ortize5354102013-03-27 17:29:53 +0200107 driver = to_mei_cl_driver(dev->driver);
108 if (!driver->remove) {
109 dev->driver = NULL;
110
111 return 0;
112 }
113
114 return driver->remove(device);
115}
116
117static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
118 char *buf)
119{
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300120 struct mei_cl_device *device = to_mei_cl_device(dev);
121 const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
122 size_t len;
Samuel Ortize5354102013-03-27 17:29:53 +0200123
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300124 len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":",
125 device->name, MEI_CL_UUID_ARGS(uuid->b));
Samuel Ortize5354102013-03-27 17:29:53 +0200126
127 return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
128}
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700129static DEVICE_ATTR_RO(modalias);
Samuel Ortize5354102013-03-27 17:29:53 +0200130
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700131static struct attribute *mei_cl_dev_attrs[] = {
132 &dev_attr_modalias.attr,
133 NULL,
Samuel Ortize5354102013-03-27 17:29:53 +0200134};
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700135ATTRIBUTE_GROUPS(mei_cl_dev);
Samuel Ortize5354102013-03-27 17:29:53 +0200136
137static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
138{
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300139 struct mei_cl_device *device = to_mei_cl_device(dev);
140 const uuid_le *uuid = mei_me_cl_uuid(device->me_cl);
141
142 if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":",
143 device->name, MEI_CL_UUID_ARGS(uuid->b)))
Samuel Ortize5354102013-03-27 17:29:53 +0200144 return -ENOMEM;
145
146 return 0;
147}
148
149static struct bus_type mei_cl_bus_type = {
150 .name = "mei",
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700151 .dev_groups = mei_cl_dev_groups,
Samuel Ortize5354102013-03-27 17:29:53 +0200152 .match = mei_cl_device_match,
153 .probe = mei_cl_device_probe,
154 .remove = mei_cl_device_remove,
155 .uevent = mei_cl_uevent,
156};
157
158static void mei_cl_dev_release(struct device *dev)
159{
Alexander Usyskind49ed642015-05-04 09:43:54 +0300160 struct mei_cl_device *device = to_mei_cl_device(dev);
161
162 if (!device)
163 return;
164
165 mei_me_cl_put(device->me_cl);
166 kfree(device);
Samuel Ortize5354102013-03-27 17:29:53 +0200167}
168
169static struct device_type mei_cl_device_type = {
170 .release = mei_cl_dev_release,
171};
172
Tomas Winklera176c242014-11-05 18:18:52 +0200173struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev,
Alexander Usyskind49ed642015-05-04 09:43:54 +0300174 uuid_le uuid)
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200175{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200176 struct mei_cl *cl;
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200177
Tomas Winkler31f88f52014-02-17 15:13:25 +0200178 list_for_each_entry(cl, &dev->device_list, device_link) {
Alexander Usyskind49ed642015-05-04 09:43:54 +0300179 if (cl->device && cl->device->me_cl &&
180 !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->device->me_cl)))
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200181 return cl;
182 }
183
184 return NULL;
185}
Alexander Usyskind49ed642015-05-04 09:43:54 +0300186
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200187struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
Alexander Usyskind49ed642015-05-04 09:43:54 +0300188 struct mei_me_client *me_cl,
189 struct mei_cl *cl,
190 char *name,
Samuel Ortize46980a2013-04-09 01:51:38 +0300191 struct mei_cl_ops *ops)
Samuel Ortize5354102013-03-27 17:29:53 +0200192{
193 struct mei_cl_device *device;
194 int status;
195
196 device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
197 if (!device)
198 return NULL;
199
Alexander Usyskind49ed642015-05-04 09:43:54 +0300200 device->me_cl = mei_me_cl_get(me_cl);
201 if (!device->me_cl) {
202 kfree(device);
203 return NULL;
204 }
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200205 device->cl = cl;
Samuel Ortize46980a2013-04-09 01:51:38 +0300206 device->ops = ops;
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200207
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300208 device->dev.parent = dev->dev;
Samuel Ortize5354102013-03-27 17:29:53 +0200209 device->dev.bus = &mei_cl_bus_type;
210 device->dev.type = &mei_cl_device_type;
211
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300212 strlcpy(device->name, name, sizeof(device->name));
213
214 dev_set_name(&device->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl));
Samuel Ortize5354102013-03-27 17:29:53 +0200215
216 status = device_register(&device->dev);
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200217 if (status) {
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300218 dev_err(dev->dev, "Failed to register MEI device\n");
Alexander Usyskind49ed642015-05-04 09:43:54 +0300219 mei_me_cl_put(device->me_cl);
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200220 kfree(device);
221 return NULL;
222 }
223
224 cl->device = device;
Samuel Ortize5354102013-03-27 17:29:53 +0200225
226 dev_dbg(&device->dev, "client %s registered\n", name);
227
228 return device;
Samuel Ortize5354102013-03-27 17:29:53 +0200229}
230EXPORT_SYMBOL_GPL(mei_cl_add_device);
231
232void mei_cl_remove_device(struct mei_cl_device *device)
233{
234 device_unregister(&device->dev);
235}
236EXPORT_SYMBOL_GPL(mei_cl_remove_device);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200237
238int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner)
239{
240 int err;
241
242 driver->driver.name = driver->name;
243 driver->driver.owner = owner;
244 driver->driver.bus = &mei_cl_bus_type;
245
246 err = driver_register(&driver->driver);
247 if (err)
248 return err;
249
250 pr_debug("mei: driver [%s] registered\n", driver->driver.name);
251
252 return 0;
253}
254EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
255
256void mei_cl_driver_unregister(struct mei_cl_driver *driver)
257{
258 driver_unregister(&driver->driver);
259
260 pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
261}
262EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200263
Tomas Winkler39db74c2014-11-27 14:07:28 +0200264static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
Samuel Ortiz44d88d92013-03-27 17:29:58 +0200265 bool blocking)
Samuel Ortiz3e833292013-03-27 17:29:55 +0200266{
267 struct mei_device *dev;
Tomas Winkler79563db2015-01-11 00:07:16 +0200268 struct mei_cl_cb *cb = NULL;
Tomas Winkler39db74c2014-11-27 14:07:28 +0200269 ssize_t rets;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200270
271 if (WARN_ON(!cl || !cl->dev))
272 return -ENODEV;
273
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300274 dev = cl->dev;
275
Tomas Winkler79563db2015-01-11 00:07:16 +0200276 mutex_lock(&dev->device_lock);
Tomas Winklerf3de9b62015-03-27 00:27:58 +0200277 if (!mei_cl_is_connected(cl)) {
Tomas Winkler79563db2015-01-11 00:07:16 +0200278 rets = -ENODEV;
279 goto out;
280 }
Samuel Ortiz3e833292013-03-27 17:29:55 +0200281
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300282 /* Check if we have an ME client device */
Alexander Usyskind49ed642015-05-04 09:43:54 +0300283 if (!mei_me_cl_is_active(cl->me_cl)) {
Tomas Winkler79563db2015-01-11 00:07:16 +0200284 rets = -ENOTTY;
285 goto out;
286 }
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300287
Alexander Usyskind49ed642015-05-04 09:43:54 +0300288 if (length > mei_cl_mtu(cl)) {
Tomas Winkler79563db2015-01-11 00:07:16 +0200289 rets = -EFBIG;
290 goto out;
291 }
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300292
Tomas Winklerbca67d62015-02-10 10:39:43 +0200293 cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
Tomas Winkler79563db2015-01-11 00:07:16 +0200294 if (!cb) {
295 rets = -ENOMEM;
296 goto out;
297 }
Samuel Ortiz3e833292013-03-27 17:29:55 +0200298
Tomas Winkler5db75142015-02-10 10:39:42 +0200299 memcpy(cb->buf.data, buf, length);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200300
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300301 rets = mei_cl_write(cl, cb, blocking);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200302
Tomas Winkler79563db2015-01-11 00:07:16 +0200303out:
Samuel Ortiz3e833292013-03-27 17:29:55 +0200304 mutex_unlock(&dev->device_lock);
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300305 if (rets < 0)
306 mei_io_cb_free(cb);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200307
Tomas Winkler4234a6d2013-04-08 21:56:37 +0300308 return rets;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200309}
310
Tomas Winkler39db74c2014-11-27 14:07:28 +0200311ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
Samuel Ortiz3e833292013-03-27 17:29:55 +0200312{
313 struct mei_device *dev;
314 struct mei_cl_cb *cb;
315 size_t r_length;
Tomas Winkler39db74c2014-11-27 14:07:28 +0200316 ssize_t rets;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200317
318 if (WARN_ON(!cl || !cl->dev))
319 return -ENODEV;
320
321 dev = cl->dev;
322
323 mutex_lock(&dev->device_lock);
324
Tomas Winklera9bed612015-02-10 10:39:46 +0200325 cb = mei_cl_read_cb(cl, NULL);
326 if (cb)
327 goto copy;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200328
Tomas Winklera9bed612015-02-10 10:39:46 +0200329 rets = mei_cl_read_start(cl, length, NULL);
330 if (rets && rets != -EBUSY)
331 goto out;
332
333 if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
Tomas Winklere2b31642013-09-02 13:29:46 +0300334
Samuel Ortiz3e833292013-03-27 17:29:55 +0200335 mutex_unlock(&dev->device_lock);
336
337 if (wait_event_interruptible(cl->rx_wait,
Tomas Winklera9bed612015-02-10 10:39:46 +0200338 (!list_empty(&cl->rd_completed)) ||
Tomas Winkler6a84d632015-03-27 00:27:59 +0200339 (!mei_cl_is_connected(cl)))) {
Tomas Winklere2b31642013-09-02 13:29:46 +0300340
Samuel Ortiz3e833292013-03-27 17:29:55 +0200341 if (signal_pending(current))
342 return -EINTR;
343 return -ERESTARTSYS;
344 }
345
346 mutex_lock(&dev->device_lock);
Tomas Winklera9bed612015-02-10 10:39:46 +0200347
Tomas Winkler6a84d632015-03-27 00:27:59 +0200348 if (!mei_cl_is_connected(cl)) {
Tomas Winklera9bed612015-02-10 10:39:46 +0200349 rets = -EBUSY;
350 goto out;
351 }
Samuel Ortiz3e833292013-03-27 17:29:55 +0200352 }
353
Tomas Winklera9bed612015-02-10 10:39:46 +0200354 cb = mei_cl_read_cb(cl, NULL);
355 if (!cb) {
Tomas Winkler39db74c2014-11-27 14:07:28 +0200356 rets = 0;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200357 goto out;
358 }
359
Tomas Winklera9bed612015-02-10 10:39:46 +0200360copy:
Tomas Winkler3d33ff22015-02-10 10:39:36 +0200361 if (cb->status) {
362 rets = cb->status;
363 goto free;
364 }
365
Samuel Ortiz3e833292013-03-27 17:29:55 +0200366 r_length = min_t(size_t, length, cb->buf_idx);
Tomas Winkler5db75142015-02-10 10:39:42 +0200367 memcpy(buf, cb->buf.data, r_length);
Tomas Winkler39db74c2014-11-27 14:07:28 +0200368 rets = r_length;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200369
Tomas Winkler3d33ff22015-02-10 10:39:36 +0200370free:
Samuel Ortiz3e833292013-03-27 17:29:55 +0200371 mei_io_cb_free(cb);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200372out:
373 mutex_unlock(&dev->device_lock);
374
Tomas Winkler39db74c2014-11-27 14:07:28 +0200375 return rets;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200376}
377
Tomas Winkler39db74c2014-11-27 14:07:28 +0200378inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
Samuel Ortiz44d88d92013-03-27 17:29:58 +0200379{
380 return ___mei_cl_send(cl, buf, length, 0);
381}
382
Tomas Winkler39db74c2014-11-27 14:07:28 +0200383inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
Samuel Ortiz44d88d92013-03-27 17:29:58 +0200384{
385 return ___mei_cl_send(cl, buf, length, 1);
386}
387
Tomas Winkler39db74c2014-11-27 14:07:28 +0200388ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
Samuel Ortiz3e833292013-03-27 17:29:55 +0200389{
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200390 struct mei_cl *cl = device->cl;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200391
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200392 if (cl == NULL)
393 return -ENODEV;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200394
395 if (device->ops && device->ops->send)
396 return device->ops->send(device, buf, length);
397
398 return __mei_cl_send(cl, buf, length);
399}
400EXPORT_SYMBOL_GPL(mei_cl_send);
401
Tomas Winkler39db74c2014-11-27 14:07:28 +0200402ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
Samuel Ortiz3e833292013-03-27 17:29:55 +0200403{
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200404 struct mei_cl *cl = device->cl;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200405
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200406 if (cl == NULL)
407 return -ENODEV;
Samuel Ortiz3e833292013-03-27 17:29:55 +0200408
409 if (device->ops && device->ops->recv)
410 return device->ops->recv(device, buf, length);
411
412 return __mei_cl_recv(cl, buf, length);
413}
414EXPORT_SYMBOL_GPL(mei_cl_recv);
415
416static void mei_bus_event_work(struct work_struct *work)
417{
418 struct mei_cl_device *device;
419
420 device = container_of(work, struct mei_cl_device, event_work);
421
422 if (device->event_cb)
423 device->event_cb(device, device->events, device->event_context);
424
425 device->events = 0;
426
427 /* Prepare for the next read */
Tomas Winklerbca67d62015-02-10 10:39:43 +0200428 mei_cl_read_start(device->cl, 0, NULL);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200429}
430
431int mei_cl_register_event_cb(struct mei_cl_device *device,
432 mei_cl_event_cb_t event_cb, void *context)
433{
434 if (device->event_cb)
435 return -EALREADY;
436
437 device->events = 0;
438 device->event_cb = event_cb;
439 device->event_context = context;
440 INIT_WORK(&device->event_work, mei_bus_event_work);
441
Tomas Winklerbca67d62015-02-10 10:39:43 +0200442 mei_cl_read_start(device->cl, 0, NULL);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200443
444 return 0;
445}
446EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
Samuel Ortizcf3baef2013-03-27 17:29:57 +0200447
Samuel Ortizaa6aef22013-03-27 17:29:59 +0200448void *mei_cl_get_drvdata(const struct mei_cl_device *device)
449{
450 return dev_get_drvdata(&device->dev);
451}
452EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
453
454void mei_cl_set_drvdata(struct mei_cl_device *device, void *data)
455{
456 dev_set_drvdata(&device->dev, data);
457}
458EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
459
Samuel Ortize46980a2013-04-09 01:51:38 +0300460int mei_cl_enable_device(struct mei_cl_device *device)
461{
462 int err;
463 struct mei_device *dev;
464 struct mei_cl *cl = device->cl;
465
466 if (cl == NULL)
467 return -ENODEV;
468
469 dev = cl->dev;
470
471 mutex_lock(&dev->device_lock);
472
Tomas Winkler0c533572015-05-04 09:43:53 +0300473 if (mei_cl_is_connected(cl)) {
474 mutex_unlock(&dev->device_lock);
475 dev_warn(dev->dev, "Already connected");
476 return -EBUSY;
477 }
478
Alexander Usyskind49ed642015-05-04 09:43:54 +0300479 err = mei_cl_connect(cl, device->me_cl, NULL);
Samuel Ortize46980a2013-04-09 01:51:38 +0300480 if (err < 0) {
481 mutex_unlock(&dev->device_lock);
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300482 dev_err(dev->dev, "Could not connect to the ME client");
Samuel Ortize46980a2013-04-09 01:51:38 +0300483
484 return err;
485 }
486
487 mutex_unlock(&dev->device_lock);
488
Tomas Winklera9bed612015-02-10 10:39:46 +0200489 if (device->event_cb)
Tomas Winklerbca67d62015-02-10 10:39:43 +0200490 mei_cl_read_start(device->cl, 0, NULL);
Samuel Ortize46980a2013-04-09 01:51:38 +0300491
492 if (!device->ops || !device->ops->enable)
493 return 0;
494
495 return device->ops->enable(device);
496}
497EXPORT_SYMBOL_GPL(mei_cl_enable_device);
498
499int mei_cl_disable_device(struct mei_cl_device *device)
500{
501 int err;
502 struct mei_device *dev;
503 struct mei_cl *cl = device->cl;
504
505 if (cl == NULL)
506 return -ENODEV;
507
508 dev = cl->dev;
509
Tomas Winklerb3de8e32015-02-10 10:39:47 +0200510 if (device->ops && device->ops->disable)
511 device->ops->disable(device);
512
513 device->event_cb = NULL;
514
Samuel Ortize46980a2013-04-09 01:51:38 +0300515 mutex_lock(&dev->device_lock);
516
Tomas Winklerf3de9b62015-03-27 00:27:58 +0200517 if (!mei_cl_is_connected(cl)) {
Tomas Winkler2bf94cab2014-09-29 16:31:42 +0300518 dev_err(dev->dev, "Already disconnected");
Tomas Winklerb3de8e32015-02-10 10:39:47 +0200519 err = 0;
520 goto out;
Samuel Ortize46980a2013-04-09 01:51:38 +0300521 }
522
Samuel Ortize46980a2013-04-09 01:51:38 +0300523 err = mei_cl_disconnect(cl);
524 if (err < 0) {
Tomas Winklerb3de8e32015-02-10 10:39:47 +0200525 dev_err(dev->dev, "Could not disconnect from the ME client");
526 goto out;
Samuel Ortize46980a2013-04-09 01:51:38 +0300527 }
528
529 /* Flush queues and remove any pending read */
Tomas Winklera9bed612015-02-10 10:39:46 +0200530 mei_cl_flush_queues(cl, NULL);
Samuel Ortize46980a2013-04-09 01:51:38 +0300531
Tomas Winklerb3de8e32015-02-10 10:39:47 +0200532out:
Samuel Ortize46980a2013-04-09 01:51:38 +0300533 mutex_unlock(&dev->device_lock);
Tomas Winklerb3de8e32015-02-10 10:39:47 +0200534 return err;
Samuel Ortize46980a2013-04-09 01:51:38 +0300535
Samuel Ortize46980a2013-04-09 01:51:38 +0300536}
537EXPORT_SYMBOL_GPL(mei_cl_disable_device);
538
Samuel Ortizcf3baef2013-03-27 17:29:57 +0200539void mei_cl_bus_rx_event(struct mei_cl *cl)
540{
541 struct mei_cl_device *device = cl->device;
542
543 if (!device || !device->event_cb)
544 return;
545
546 set_bit(MEI_CL_EVENT_RX, &device->events);
547
548 schedule_work(&device->event_work);
549}
550
Tomas Winkler48705692014-02-17 15:13:19 +0200551void mei_cl_bus_remove_devices(struct mei_device *dev)
552{
553 struct mei_cl *cl, *next;
554
555 mutex_lock(&dev->device_lock);
556 list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
557 if (cl->device)
558 mei_cl_remove_device(cl->device);
559
560 list_del(&cl->device_link);
561 mei_cl_unlink(cl);
562 kfree(cl);
563 }
564 mutex_unlock(&dev->device_lock);
565}
566
Samuel Ortizcf3baef2013-03-27 17:29:57 +0200567int __init mei_cl_bus_init(void)
568{
569 return bus_register(&mei_cl_bus_type);
570}
571
572void __exit mei_cl_bus_exit(void)
573{
574 bus_unregister(&mei_cl_bus_type);
575}