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; | |
79563db9 TW |
231 | struct mei_me_client *me_cl = NULL; |
232 | struct mei_cl_cb *cb = NULL; | |
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 | ||
79563db9 TW |
240 | mutex_lock(&dev->device_lock); |
241 | if (cl->state != MEI_FILE_CONNECTED) { | |
242 | rets = -ENODEV; | |
243 | goto out; | |
244 | } | |
3e833295 | 245 | |
4234a6de | 246 | /* Check if we have an ME client device */ |
d880f329 | 247 | me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); |
79563db9 TW |
248 | if (!me_cl) { |
249 | rets = -ENOTTY; | |
250 | goto out; | |
251 | } | |
4234a6de | 252 | |
79563db9 TW |
253 | if (length > me_cl->props.max_msg_length) { |
254 | rets = -EFBIG; | |
255 | goto out; | |
256 | } | |
4234a6de | 257 | |
3e833295 | 258 | cb = mei_io_cb_init(cl, NULL); |
79563db9 TW |
259 | if (!cb) { |
260 | rets = -ENOMEM; | |
261 | goto out; | |
262 | } | |
3e833295 | 263 | |
4234a6de | 264 | rets = mei_io_cb_alloc_req_buf(cb, length); |
79563db9 TW |
265 | if (rets < 0) |
266 | goto out; | |
3e833295 SO |
267 | |
268 | memcpy(cb->request_buffer.data, buf, length); | |
3e833295 | 269 | |
4234a6de | 270 | rets = mei_cl_write(cl, cb, blocking); |
3e833295 | 271 | |
79563db9 TW |
272 | out: |
273 | mei_me_cl_put(me_cl); | |
3e833295 | 274 | mutex_unlock(&dev->device_lock); |
4234a6de TW |
275 | if (rets < 0) |
276 | mei_io_cb_free(cb); | |
3e833295 | 277 | |
4234a6de | 278 | return rets; |
3e833295 SO |
279 | } |
280 | ||
39db74ce | 281 | ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) |
3e833295 SO |
282 | { |
283 | struct mei_device *dev; | |
284 | struct mei_cl_cb *cb; | |
285 | size_t r_length; | |
39db74ce | 286 | ssize_t rets; |
3e833295 SO |
287 | |
288 | if (WARN_ON(!cl || !cl->dev)) | |
289 | return -ENODEV; | |
290 | ||
291 | dev = cl->dev; | |
292 | ||
293 | mutex_lock(&dev->device_lock); | |
294 | ||
295 | if (!cl->read_cb) { | |
39db74ce TW |
296 | rets = mei_cl_read_start(cl, length); |
297 | if (rets < 0) | |
298 | goto out; | |
3e833295 SO |
299 | } |
300 | ||
301 | if (cl->reading_state != MEI_READ_COMPLETE && | |
302 | !waitqueue_active(&cl->rx_wait)) { | |
e2b31644 | 303 | |
3e833295 SO |
304 | mutex_unlock(&dev->device_lock); |
305 | ||
306 | if (wait_event_interruptible(cl->rx_wait, | |
e2b31644 TW |
307 | cl->reading_state == MEI_READ_COMPLETE || |
308 | mei_cl_is_transitioning(cl))) { | |
309 | ||
3e833295 SO |
310 | if (signal_pending(current)) |
311 | return -EINTR; | |
312 | return -ERESTARTSYS; | |
313 | } | |
314 | ||
315 | mutex_lock(&dev->device_lock); | |
316 | } | |
317 | ||
318 | cb = cl->read_cb; | |
319 | ||
320 | if (cl->reading_state != MEI_READ_COMPLETE) { | |
39db74ce | 321 | rets = 0; |
3e833295 SO |
322 | goto out; |
323 | } | |
324 | ||
325 | r_length = min_t(size_t, length, cb->buf_idx); | |
3e833295 | 326 | memcpy(buf, cb->response_buffer.data, r_length); |
39db74ce | 327 | rets = r_length; |
3e833295 SO |
328 | |
329 | mei_io_cb_free(cb); | |
330 | cl->reading_state = MEI_IDLE; | |
331 | cl->read_cb = NULL; | |
332 | ||
333 | out: | |
334 | mutex_unlock(&dev->device_lock); | |
335 | ||
39db74ce | 336 | return rets; |
3e833295 SO |
337 | } |
338 | ||
39db74ce | 339 | inline ssize_t __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) |
44d88d91 SO |
340 | { |
341 | return ___mei_cl_send(cl, buf, length, 0); | |
342 | } | |
343 | ||
39db74ce | 344 | inline ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) |
44d88d91 SO |
345 | { |
346 | return ___mei_cl_send(cl, buf, length, 1); | |
347 | } | |
348 | ||
39db74ce | 349 | ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) |
3e833295 | 350 | { |
a7b71bc0 | 351 | struct mei_cl *cl = device->cl; |
3e833295 | 352 | |
a7b71bc0 SO |
353 | if (cl == NULL) |
354 | return -ENODEV; | |
3e833295 SO |
355 | |
356 | if (device->ops && device->ops->send) | |
357 | return device->ops->send(device, buf, length); | |
358 | ||
359 | return __mei_cl_send(cl, buf, length); | |
360 | } | |
361 | EXPORT_SYMBOL_GPL(mei_cl_send); | |
362 | ||
39db74ce | 363 | ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) |
3e833295 | 364 | { |
a7b71bc0 | 365 | struct mei_cl *cl = device->cl; |
3e833295 | 366 | |
a7b71bc0 SO |
367 | if (cl == NULL) |
368 | return -ENODEV; | |
3e833295 SO |
369 | |
370 | if (device->ops && device->ops->recv) | |
371 | return device->ops->recv(device, buf, length); | |
372 | ||
373 | return __mei_cl_recv(cl, buf, length); | |
374 | } | |
375 | EXPORT_SYMBOL_GPL(mei_cl_recv); | |
376 | ||
377 | static void mei_bus_event_work(struct work_struct *work) | |
378 | { | |
379 | struct mei_cl_device *device; | |
380 | ||
381 | device = container_of(work, struct mei_cl_device, event_work); | |
382 | ||
383 | if (device->event_cb) | |
384 | device->event_cb(device, device->events, device->event_context); | |
385 | ||
386 | device->events = 0; | |
387 | ||
388 | /* Prepare for the next read */ | |
fcb136e1 | 389 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
390 | } |
391 | ||
392 | int mei_cl_register_event_cb(struct mei_cl_device *device, | |
393 | mei_cl_event_cb_t event_cb, void *context) | |
394 | { | |
395 | if (device->event_cb) | |
396 | return -EALREADY; | |
397 | ||
398 | device->events = 0; | |
399 | device->event_cb = event_cb; | |
400 | device->event_context = context; | |
401 | INIT_WORK(&device->event_work, mei_bus_event_work); | |
402 | ||
fcb136e1 | 403 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
404 | |
405 | return 0; | |
406 | } | |
407 | EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); | |
cf3baefb | 408 | |
aa6aef21 SO |
409 | void *mei_cl_get_drvdata(const struct mei_cl_device *device) |
410 | { | |
411 | return dev_get_drvdata(&device->dev); | |
412 | } | |
413 | EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); | |
414 | ||
415 | void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) | |
416 | { | |
417 | dev_set_drvdata(&device->dev, data); | |
418 | } | |
419 | EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); | |
420 | ||
e46980a1 SO |
421 | int mei_cl_enable_device(struct mei_cl_device *device) |
422 | { | |
423 | int err; | |
424 | struct mei_device *dev; | |
425 | struct mei_cl *cl = device->cl; | |
426 | ||
427 | if (cl == NULL) | |
428 | return -ENODEV; | |
429 | ||
430 | dev = cl->dev; | |
431 | ||
432 | mutex_lock(&dev->device_lock); | |
433 | ||
e46980a1 SO |
434 | err = mei_cl_connect(cl, NULL); |
435 | if (err < 0) { | |
436 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 437 | dev_err(dev->dev, "Could not connect to the ME client"); |
e46980a1 SO |
438 | |
439 | return err; | |
440 | } | |
441 | ||
442 | mutex_unlock(&dev->device_lock); | |
443 | ||
444 | if (device->event_cb && !cl->read_cb) | |
fcb136e1 | 445 | mei_cl_read_start(device->cl, 0); |
e46980a1 SO |
446 | |
447 | if (!device->ops || !device->ops->enable) | |
448 | return 0; | |
449 | ||
450 | return device->ops->enable(device); | |
451 | } | |
452 | EXPORT_SYMBOL_GPL(mei_cl_enable_device); | |
453 | ||
454 | int mei_cl_disable_device(struct mei_cl_device *device) | |
455 | { | |
456 | int err; | |
457 | struct mei_device *dev; | |
458 | struct mei_cl *cl = device->cl; | |
459 | ||
460 | if (cl == NULL) | |
461 | return -ENODEV; | |
462 | ||
463 | dev = cl->dev; | |
464 | ||
465 | mutex_lock(&dev->device_lock); | |
466 | ||
467 | if (cl->state != MEI_FILE_CONNECTED) { | |
468 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 469 | dev_err(dev->dev, "Already disconnected"); |
e46980a1 SO |
470 | |
471 | return 0; | |
472 | } | |
473 | ||
474 | cl->state = MEI_FILE_DISCONNECTING; | |
475 | ||
476 | err = mei_cl_disconnect(cl); | |
477 | if (err < 0) { | |
478 | mutex_unlock(&dev->device_lock); | |
2bf94cab | 479 | dev_err(dev->dev, |
e46980a1 SO |
480 | "Could not disconnect from the ME client"); |
481 | ||
482 | return err; | |
483 | } | |
484 | ||
485 | /* Flush queues and remove any pending read */ | |
486 | mei_cl_flush_queues(cl); | |
487 | ||
488 | if (cl->read_cb) { | |
489 | struct mei_cl_cb *cb = NULL; | |
490 | ||
491 | cb = mei_cl_find_read_cb(cl); | |
492 | /* Remove entry from read list */ | |
493 | if (cb) | |
494 | list_del(&cb->list); | |
495 | ||
496 | cb = cl->read_cb; | |
497 | cl->read_cb = NULL; | |
498 | ||
499 | if (cb) { | |
500 | mei_io_cb_free(cb); | |
501 | cb = NULL; | |
502 | } | |
503 | } | |
504 | ||
bbedf2fc SO |
505 | device->event_cb = NULL; |
506 | ||
e46980a1 SO |
507 | mutex_unlock(&dev->device_lock); |
508 | ||
509 | if (!device->ops || !device->ops->disable) | |
510 | return 0; | |
511 | ||
512 | return device->ops->disable(device); | |
513 | } | |
514 | EXPORT_SYMBOL_GPL(mei_cl_disable_device); | |
515 | ||
cf3baefb SO |
516 | void mei_cl_bus_rx_event(struct mei_cl *cl) |
517 | { | |
518 | struct mei_cl_device *device = cl->device; | |
519 | ||
520 | if (!device || !device->event_cb) | |
521 | return; | |
522 | ||
523 | set_bit(MEI_CL_EVENT_RX, &device->events); | |
524 | ||
525 | schedule_work(&device->event_work); | |
526 | } | |
527 | ||
48705693 TW |
528 | void mei_cl_bus_remove_devices(struct mei_device *dev) |
529 | { | |
530 | struct mei_cl *cl, *next; | |
531 | ||
532 | mutex_lock(&dev->device_lock); | |
533 | list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { | |
534 | if (cl->device) | |
535 | mei_cl_remove_device(cl->device); | |
536 | ||
537 | list_del(&cl->device_link); | |
538 | mei_cl_unlink(cl); | |
539 | kfree(cl); | |
540 | } | |
541 | mutex_unlock(&dev->device_lock); | |
542 | } | |
543 | ||
cf3baefb SO |
544 | int __init mei_cl_bus_init(void) |
545 | { | |
546 | return bus_register(&mei_cl_bus_type); | |
547 | } | |
548 | ||
549 | void __exit mei_cl_bus_exit(void) | |
550 | { | |
551 | bus_unregister(&mei_cl_bus_type); | |
552 | } |