Commit | Line | Data |
---|---|---|
e5354107 SO |
1 | /* |
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> | |
3e833295 | 19 | #include <linux/sched.h> |
e5354107 SO |
20 | #include <linux/init.h> |
21 | #include <linux/errno.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/mutex.h> | |
24 | #include <linux/interrupt.h> | |
e5354107 SO |
25 | #include <linux/mei_cl_bus.h> |
26 | ||
27 | #include "mei_dev.h" | |
3e833295 | 28 | #include "client.h" |
e5354107 SO |
29 | |
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 | ||
33 | static int mei_cl_device_match(struct device *dev, struct device_driver *drv) | |
34 | { | |
35 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
36 | struct mei_cl_driver *driver = to_mei_cl_driver(drv); | |
37 | const struct mei_cl_device_id *id; | |
38 | ||
39 | if (!device) | |
40 | return 0; | |
41 | ||
42 | if (!driver || !driver->id_table) | |
43 | return 0; | |
44 | ||
45 | id = driver->id_table; | |
46 | ||
47 | while (id->name[0]) { | |
8b613bb8 | 48 | if (!strncmp(dev_name(dev), id->name, sizeof(id->name))) |
e5354107 SO |
49 | return 1; |
50 | ||
51 | id++; | |
52 | } | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | static int mei_cl_device_probe(struct device *dev) | |
58 | { | |
59 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
60 | struct mei_cl_driver *driver; | |
61 | struct mei_cl_device_id id; | |
62 | ||
63 | if (!device) | |
64 | return 0; | |
65 | ||
66 | driver = to_mei_cl_driver(dev->driver); | |
67 | if (!driver || !driver->probe) | |
68 | return -ENODEV; | |
69 | ||
70 | dev_dbg(dev, "Device probe\n"); | |
71 | ||
cfda2794 | 72 | strlcpy(id.name, dev_name(dev), sizeof(id.name)); |
e5354107 SO |
73 | |
74 | return driver->probe(device, &id); | |
75 | } | |
76 | ||
77 | static int mei_cl_device_remove(struct device *dev) | |
78 | { | |
79 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
80 | struct mei_cl_driver *driver; | |
81 | ||
82 | if (!device || !dev->driver) | |
83 | return 0; | |
84 | ||
3e833295 SO |
85 | if (device->event_cb) { |
86 | device->event_cb = NULL; | |
87 | cancel_work_sync(&device->event_work); | |
88 | } | |
89 | ||
e5354107 SO |
90 | driver = to_mei_cl_driver(dev->driver); |
91 | if (!driver->remove) { | |
92 | dev->driver = NULL; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | return driver->remove(device); | |
98 | } | |
99 | ||
100 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | |
101 | char *buf) | |
102 | { | |
103 | int len; | |
104 | ||
105 | len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); | |
106 | ||
107 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | |
108 | } | |
32f389ec | 109 | static DEVICE_ATTR_RO(modalias); |
e5354107 | 110 | |
32f389ec GKH |
111 | static struct attribute *mei_cl_dev_attrs[] = { |
112 | &dev_attr_modalias.attr, | |
113 | NULL, | |
e5354107 | 114 | }; |
32f389ec | 115 | ATTRIBUTE_GROUPS(mei_cl_dev); |
e5354107 SO |
116 | |
117 | static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) | |
118 | { | |
119 | if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) | |
120 | return -ENOMEM; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static struct bus_type mei_cl_bus_type = { | |
126 | .name = "mei", | |
32f389ec | 127 | .dev_groups = mei_cl_dev_groups, |
e5354107 SO |
128 | .match = mei_cl_device_match, |
129 | .probe = mei_cl_device_probe, | |
130 | .remove = mei_cl_device_remove, | |
131 | .uevent = mei_cl_uevent, | |
132 | }; | |
133 | ||
134 | static void mei_cl_dev_release(struct device *dev) | |
135 | { | |
136 | kfree(to_mei_cl_device(dev)); | |
137 | } | |
138 | ||
139 | static struct device_type mei_cl_device_type = { | |
140 | .release = mei_cl_dev_release, | |
141 | }; | |
142 | ||
a176c24d | 143 | struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev, |
a7b71bc0 SO |
144 | uuid_le uuid) |
145 | { | |
31f88f57 | 146 | struct mei_cl *cl; |
a7b71bc0 | 147 | |
31f88f57 | 148 | list_for_each_entry(cl, &dev->device_list, device_link) { |
d880f329 | 149 | if (!uuid_le_cmp(uuid, cl->cl_uuid)) |
a7b71bc0 SO |
150 | return cl; |
151 | } | |
152 | ||
153 | return NULL; | |
154 | } | |
155 | struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, | |
e46980a1 SO |
156 | uuid_le uuid, char *name, |
157 | struct mei_cl_ops *ops) | |
e5354107 SO |
158 | { |
159 | struct mei_cl_device *device; | |
a7b71bc0 | 160 | struct mei_cl *cl; |
e5354107 SO |
161 | int status; |
162 | ||
a176c24d | 163 | cl = mei_cl_bus_find_cl_by_uuid(dev, uuid); |
a7b71bc0 SO |
164 | if (cl == NULL) |
165 | return NULL; | |
166 | ||
e5354107 SO |
167 | device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); |
168 | if (!device) | |
169 | return NULL; | |
170 | ||
a7b71bc0 | 171 | device->cl = cl; |
e46980a1 | 172 | device->ops = ops; |
a7b71bc0 | 173 | |
2bf94cab | 174 | device->dev.parent = dev->dev; |
e5354107 SO |
175 | device->dev.bus = &mei_cl_bus_type; |
176 | device->dev.type = &mei_cl_device_type; | |
177 | ||
178 | dev_set_name(&device->dev, "%s", name); | |
179 | ||
180 | status = device_register(&device->dev); | |
a7b71bc0 | 181 | if (status) { |
2bf94cab | 182 | dev_err(dev->dev, "Failed to register MEI device\n"); |
a7b71bc0 SO |
183 | kfree(device); |
184 | return NULL; | |
185 | } | |
186 | ||
187 | cl->device = device; | |
e5354107 SO |
188 | |
189 | dev_dbg(&device->dev, "client %s registered\n", name); | |
190 | ||
191 | return device; | |
e5354107 SO |
192 | } |
193 | EXPORT_SYMBOL_GPL(mei_cl_add_device); | |
194 | ||
195 | void mei_cl_remove_device(struct mei_cl_device *device) | |
196 | { | |
197 | device_unregister(&device->dev); | |
198 | } | |
199 | EXPORT_SYMBOL_GPL(mei_cl_remove_device); | |
333e4ee0 SO |
200 | |
201 | int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) | |
202 | { | |
203 | int err; | |
204 | ||
205 | driver->driver.name = driver->name; | |
206 | driver->driver.owner = owner; | |
207 | driver->driver.bus = &mei_cl_bus_type; | |
208 | ||
209 | err = driver_register(&driver->driver); | |
210 | if (err) | |
211 | return err; | |
212 | ||
213 | pr_debug("mei: driver [%s] registered\n", driver->driver.name); | |
214 | ||
215 | return 0; | |
216 | } | |
217 | EXPORT_SYMBOL_GPL(__mei_cl_driver_register); | |
218 | ||
219 | void mei_cl_driver_unregister(struct mei_cl_driver *driver) | |
220 | { | |
221 | driver_unregister(&driver->driver); | |
222 | ||
223 | pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); | |
224 | } | |
225 | EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); | |
3e833295 | 226 | |
39db74ce | 227 | static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, |
44d88d91 | 228 | bool blocking) |
3e833295 SO |
229 | { |
230 | struct mei_device *dev; | |
d320832f | 231 | struct mei_me_client *me_cl; |
3e833295 | 232 | struct mei_cl_cb *cb; |
39db74ce | 233 | ssize_t rets; |
3e833295 SO |
234 | |
235 | if (WARN_ON(!cl || !cl->dev)) | |
236 | return -ENODEV; | |
237 | ||
4234a6de TW |
238 | dev = cl->dev; |
239 | ||
3e833295 SO |
240 | if (cl->state != MEI_FILE_CONNECTED) |
241 | return -ENODEV; | |
242 | ||
4234a6de | 243 | /* Check if we have an ME client device */ |
d880f329 | 244 | me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); |
d320832f TW |
245 | if (!me_cl) |
246 | return -ENOTTY; | |
4234a6de | 247 | |
d320832f | 248 | if (length > me_cl->props.max_msg_length) |
86113500 | 249 | return -EFBIG; |
4234a6de | 250 | |
3e833295 SO |
251 | cb = mei_io_cb_init(cl, NULL); |
252 | if (!cb) | |
253 | return -ENOMEM; | |
254 | ||
4234a6de TW |
255 | rets = mei_io_cb_alloc_req_buf(cb, length); |
256 | if (rets < 0) { | |
3e833295 | 257 | mei_io_cb_free(cb); |
4234a6de | 258 | return rets; |
3e833295 SO |
259 | } |
260 | ||
261 | memcpy(cb->request_buffer.data, buf, length); | |
3e833295 SO |
262 | |
263 | mutex_lock(&dev->device_lock); | |
264 | ||
4234a6de | 265 | rets = mei_cl_write(cl, cb, blocking); |
3e833295 SO |
266 | |
267 | mutex_unlock(&dev->device_lock); | |
4234a6de TW |
268 | if (rets < 0) |
269 | mei_io_cb_free(cb); | |
3e833295 | 270 | |
4234a6de | 271 | return rets; |
3e833295 SO |
272 | } |
273 | ||
39db74ce | 274 | ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) |
3e833295 SO |
275 | { |
276 | struct mei_device *dev; | |
277 | struct mei_cl_cb *cb; | |
278 | size_t r_length; | |
39db74ce | 279 | ssize_t rets; |
3e833295 SO |
280 | |
281 | if (WARN_ON(!cl || !cl->dev)) | |
282 | return -ENODEV; | |
283 | ||
284 | dev = cl->dev; | |
285 | ||
286 | mutex_lock(&dev->device_lock); | |
287 | ||
288 | if (!cl->read_cb) { | |
39db74ce TW |
289 | rets = mei_cl_read_start(cl, length); |
290 | if (rets < 0) | |
291 | goto out; | |
3e833295 SO |
292 | } |
293 | ||
294 | if (cl->reading_state != MEI_READ_COMPLETE && | |
295 | !waitqueue_active(&cl->rx_wait)) { | |
e2b31644 | 296 | |
3e833295 SO |
297 | mutex_unlock(&dev->device_lock); |
298 | ||
299 | if (wait_event_interruptible(cl->rx_wait, | |
e2b31644 TW |
300 | cl->reading_state == MEI_READ_COMPLETE || |
301 | mei_cl_is_transitioning(cl))) { | |
302 | ||
3e833295 SO |
303 | if (signal_pending(current)) |
304 | return -EINTR; | |
305 | return -ERESTARTSYS; | |
306 | } | |
307 | ||
308 | mutex_lock(&dev->device_lock); | |
309 | } | |
310 | ||
311 | cb = cl->read_cb; | |
312 | ||
313 | if (cl->reading_state != MEI_READ_COMPLETE) { | |
39db74ce | 314 | rets = 0; |
3e833295 SO |
315 | goto out; |
316 | } | |
317 | ||
318 | r_length = min_t(size_t, length, cb->buf_idx); | |
3e833295 | 319 | memcpy(buf, cb->response_buffer.data, r_length); |
39db74ce | 320 | rets = r_length; |
3e833295 SO |
321 | |
322 | mei_io_cb_free(cb); | |
323 | cl->reading_state = MEI_IDLE; | |
324 | cl->read_cb = NULL; | |
325 | ||
326 | out: | |
327 | mutex_unlock(&dev->device_lock); | |
328 | ||
39db74ce | 329 | return rets; |
3e833295 SO |
330 | } |
331 | ||
39db74ce | 332 | inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) |
44d88d91 SO |
333 | { |
334 | return ___mei_cl_send(cl, buf, length, 0); | |
335 | } | |
336 | ||
39db74ce | 337 | inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) |
44d88d91 SO |
338 | { |
339 | return ___mei_cl_send(cl, buf, length, 1); | |
340 | } | |
341 | ||
39db74ce | 342 | ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) |
3e833295 | 343 | { |
a7b71bc0 | 344 | struct mei_cl *cl = device->cl; |
3e833295 | 345 | |
a7b71bc0 SO |
346 | if (cl == NULL) |
347 | return -ENODEV; | |
3e833295 SO |
348 | |
349 | if (device->ops && device->ops->send) | |
350 | return device->ops->send(device, buf, length); | |
351 | ||
352 | return __mei_cl_send(cl, buf, length); | |
353 | } | |
354 | EXPORT_SYMBOL_GPL(mei_cl_send); | |
355 | ||
39db74ce | 356 | ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) |
3e833295 | 357 | { |
a7b71bc0 | 358 | struct mei_cl *cl = device->cl; |
3e833295 | 359 | |
a7b71bc0 SO |
360 | if (cl == NULL) |
361 | return -ENODEV; | |
3e833295 SO |
362 | |
363 | if (device->ops && device->ops->recv) | |
364 | return device->ops->recv(device, buf, length); | |
365 | ||
366 | return __mei_cl_recv(cl, buf, length); | |
367 | } | |
368 | EXPORT_SYMBOL_GPL(mei_cl_recv); | |
369 | ||
370 | static void mei_bus_event_work(struct work_struct *work) | |
371 | { | |
372 | struct mei_cl_device *device; | |
373 | ||
374 | device = container_of(work, struct mei_cl_device, event_work); | |
375 | ||
376 | if (device->event_cb) | |
377 | device->event_cb(device, device->events, device->event_context); | |
378 | ||
379 | device->events = 0; | |
380 | ||
381 | /* Prepare for the next read */ | |
fcb136e1 | 382 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
383 | } |
384 | ||
385 | int mei_cl_register_event_cb(struct mei_cl_device *device, | |
386 | mei_cl_event_cb_t event_cb, void *context) | |
387 | { | |
388 | if (device->event_cb) | |
389 | return -EALREADY; | |
390 | ||
391 | device->events = 0; | |
392 | device->event_cb = event_cb; | |
393 | device->event_context = context; | |
394 | INIT_WORK(&device->event_work, mei_bus_event_work); | |
395 | ||
fcb136e1 | 396 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
397 | |
398 | return 0; | |
399 | } | |
400 | EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); | |
cf3baefb | 401 | |
aa6aef21 SO |
402 | void *mei_cl_get_drvdata(const struct mei_cl_device *device) |
403 | { | |
404 | return dev_get_drvdata(&device->dev); | |
405 | } | |
406 | EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); | |
407 | ||
408 | void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) | |
409 | { | |
410 | dev_set_drvdata(&device->dev, data); | |
411 | } | |
412 | EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); | |
413 | ||
e46980a1 SO |
414 | int mei_cl_enable_device(struct mei_cl_device *device) |
415 | { | |
416 | int err; | |
417 | struct mei_device *dev; | |
418 | struct mei_cl *cl = device->cl; | |
419 | ||
420 | if (cl == NULL) | |
421 | return -ENODEV; | |
422 | ||
423 | dev = cl->dev; | |
424 | ||
425 | mutex_lock(&dev->device_lock); | |
426 | ||
e46980a1 SO |
427 | err = mei_cl_connect(cl, NULL); |
428 | if (err < 0) { | |
429 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 430 | dev_err(dev->dev, "Could not connect to the ME client"); |
e46980a1 SO |
431 | |
432 | return err; | |
433 | } | |
434 | ||
435 | mutex_unlock(&dev->device_lock); | |
436 | ||
437 | if (device->event_cb && !cl->read_cb) | |
fcb136e1 | 438 | mei_cl_read_start(device->cl, 0); |
e46980a1 SO |
439 | |
440 | if (!device->ops || !device->ops->enable) | |
441 | return 0; | |
442 | ||
443 | return device->ops->enable(device); | |
444 | } | |
445 | EXPORT_SYMBOL_GPL(mei_cl_enable_device); | |
446 | ||
447 | int mei_cl_disable_device(struct mei_cl_device *device) | |
448 | { | |
449 | int err; | |
450 | struct mei_device *dev; | |
451 | struct mei_cl *cl = device->cl; | |
452 | ||
453 | if (cl == NULL) | |
454 | return -ENODEV; | |
455 | ||
456 | dev = cl->dev; | |
457 | ||
458 | mutex_lock(&dev->device_lock); | |
459 | ||
460 | if (cl->state != MEI_FILE_CONNECTED) { | |
461 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 462 | dev_err(dev->dev, "Already disconnected"); |
e46980a1 SO |
463 | |
464 | return 0; | |
465 | } | |
466 | ||
467 | cl->state = MEI_FILE_DISCONNECTING; | |
468 | ||
469 | err = mei_cl_disconnect(cl); | |
470 | if (err < 0) { | |
471 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 472 | dev_err(dev->dev, |
e46980a1 SO |
473 | "Could not disconnect from the ME client"); |
474 | ||
475 | return err; | |
476 | } | |
477 | ||
478 | /* Flush queues and remove any pending read */ | |
479 | mei_cl_flush_queues(cl); | |
480 | ||
481 | if (cl->read_cb) { | |
482 | struct mei_cl_cb *cb = NULL; | |
483 | ||
484 | cb = mei_cl_find_read_cb(cl); | |
485 | /* Remove entry from read list */ | |
486 | if (cb) | |
487 | list_del(&cb->list); | |
488 | ||
489 | cb = cl->read_cb; | |
490 | cl->read_cb = NULL; | |
491 | ||
492 | if (cb) { | |
493 | mei_io_cb_free(cb); | |
494 | cb = NULL; | |
495 | } | |
496 | } | |
497 | ||
bbedf2fc SO |
498 | device->event_cb = NULL; |
499 | ||
e46980a1 SO |
500 | mutex_unlock(&dev->device_lock); |
501 | ||
502 | if (!device->ops || !device->ops->disable) | |
503 | return 0; | |
504 | ||
505 | return device->ops->disable(device); | |
506 | } | |
507 | EXPORT_SYMBOL_GPL(mei_cl_disable_device); | |
508 | ||
cf3baefb SO |
509 | void mei_cl_bus_rx_event(struct mei_cl *cl) |
510 | { | |
511 | struct mei_cl_device *device = cl->device; | |
512 | ||
513 | if (!device || !device->event_cb) | |
514 | return; | |
515 | ||
516 | set_bit(MEI_CL_EVENT_RX, &device->events); | |
517 | ||
518 | schedule_work(&device->event_work); | |
519 | } | |
520 | ||
48705693 TW |
521 | void mei_cl_bus_remove_devices(struct mei_device *dev) |
522 | { | |
523 | struct mei_cl *cl, *next; | |
524 | ||
525 | mutex_lock(&dev->device_lock); | |
526 | list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { | |
527 | if (cl->device) | |
528 | mei_cl_remove_device(cl->device); | |
529 | ||
530 | list_del(&cl->device_link); | |
531 | mei_cl_unlink(cl); | |
532 | kfree(cl); | |
533 | } | |
534 | mutex_unlock(&dev->device_lock); | |
535 | } | |
536 | ||
cf3baefb SO |
537 | int __init mei_cl_bus_init(void) |
538 | { | |
539 | return bus_register(&mei_cl_bus_type); | |
540 | } | |
541 | ||
542 | void __exit mei_cl_bus_exit(void) | |
543 | { | |
544 | bus_unregister(&mei_cl_bus_type); | |
545 | } |