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