]>
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]) { | |
50 | if (!strcmp(dev_name(dev), id->name)) | |
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 | ||
74 | strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); | |
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 | } | |
111 | ||
112 | static struct device_attribute mei_cl_dev_attrs[] = { | |
113 | __ATTR_RO(modalias), | |
114 | __ATTR_NULL, | |
115 | }; | |
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", | |
127 | .dev_attrs = mei_cl_dev_attrs, | |
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 | ||
a7b71bc0 SO |
143 | static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, |
144 | uuid_le uuid) | |
145 | { | |
146 | struct mei_cl *cl, *next; | |
147 | ||
148 | list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { | |
149 | if (!uuid_le_cmp(uuid, cl->device_uuid)) | |
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 | ||
a7b71bc0 SO |
163 | cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); |
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 SO |
173 | |
174 | device->dev.parent = &dev->pdev->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 SO |
181 | if (status) { |
182 | dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); | |
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 | |
44d88d91 SO |
227 | static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, |
228 | bool blocking) | |
3e833295 SO |
229 | { |
230 | struct mei_device *dev; | |
3e833295 | 231 | struct mei_cl_cb *cb; |
4234a6de TW |
232 | int id; |
233 | int 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 TW |
243 | /* Check if we have an ME client device */ |
244 | id = mei_me_cl_by_id(dev, cl->me_client_id); | |
245 | if (id < 0) | |
246 | return -ENODEV; | |
247 | ||
248 | if (length > dev->me_clients[id].props.max_msg_length) | |
249 | return -EINVAL; | |
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 | ||
274 | int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) | |
275 | { | |
276 | struct mei_device *dev; | |
277 | struct mei_cl_cb *cb; | |
278 | size_t r_length; | |
279 | int err; | |
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) { | |
fcb136e1 | 289 | err = mei_cl_read_start(cl, length); |
3e833295 SO |
290 | if (err < 0) { |
291 | mutex_unlock(&dev->device_lock); | |
292 | return err; | |
293 | } | |
294 | } | |
295 | ||
296 | if (cl->reading_state != MEI_READ_COMPLETE && | |
297 | !waitqueue_active(&cl->rx_wait)) { | |
298 | mutex_unlock(&dev->device_lock); | |
299 | ||
300 | if (wait_event_interruptible(cl->rx_wait, | |
301 | (MEI_READ_COMPLETE == cl->reading_state))) { | |
302 | if (signal_pending(current)) | |
303 | return -EINTR; | |
304 | return -ERESTARTSYS; | |
305 | } | |
306 | ||
307 | mutex_lock(&dev->device_lock); | |
308 | } | |
309 | ||
310 | cb = cl->read_cb; | |
311 | ||
312 | if (cl->reading_state != MEI_READ_COMPLETE) { | |
313 | r_length = 0; | |
314 | goto out; | |
315 | } | |
316 | ||
317 | r_length = min_t(size_t, length, cb->buf_idx); | |
318 | ||
319 | memcpy(buf, cb->response_buffer.data, r_length); | |
320 | ||
321 | mei_io_cb_free(cb); | |
322 | cl->reading_state = MEI_IDLE; | |
323 | cl->read_cb = NULL; | |
324 | ||
325 | out: | |
326 | mutex_unlock(&dev->device_lock); | |
327 | ||
328 | return r_length; | |
329 | } | |
330 | ||
44d88d91 SO |
331 | inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) |
332 | { | |
333 | return ___mei_cl_send(cl, buf, length, 0); | |
334 | } | |
335 | ||
336 | inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) | |
337 | { | |
338 | return ___mei_cl_send(cl, buf, length, 1); | |
339 | } | |
340 | ||
3e833295 SO |
341 | int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) |
342 | { | |
a7b71bc0 | 343 | struct mei_cl *cl = device->cl; |
3e833295 | 344 | |
a7b71bc0 SO |
345 | if (cl == NULL) |
346 | return -ENODEV; | |
3e833295 SO |
347 | |
348 | if (device->ops && device->ops->send) | |
349 | return device->ops->send(device, buf, length); | |
350 | ||
351 | return __mei_cl_send(cl, buf, length); | |
352 | } | |
353 | EXPORT_SYMBOL_GPL(mei_cl_send); | |
354 | ||
355 | int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) | |
356 | { | |
a7b71bc0 | 357 | struct mei_cl *cl = device->cl; |
3e833295 | 358 | |
a7b71bc0 SO |
359 | if (cl == NULL) |
360 | return -ENODEV; | |
3e833295 SO |
361 | |
362 | if (device->ops && device->ops->recv) | |
363 | return device->ops->recv(device, buf, length); | |
364 | ||
365 | return __mei_cl_recv(cl, buf, length); | |
366 | } | |
367 | EXPORT_SYMBOL_GPL(mei_cl_recv); | |
368 | ||
369 | static void mei_bus_event_work(struct work_struct *work) | |
370 | { | |
371 | struct mei_cl_device *device; | |
372 | ||
373 | device = container_of(work, struct mei_cl_device, event_work); | |
374 | ||
375 | if (device->event_cb) | |
376 | device->event_cb(device, device->events, device->event_context); | |
377 | ||
378 | device->events = 0; | |
379 | ||
380 | /* Prepare for the next read */ | |
fcb136e1 | 381 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
382 | } |
383 | ||
384 | int mei_cl_register_event_cb(struct mei_cl_device *device, | |
385 | mei_cl_event_cb_t event_cb, void *context) | |
386 | { | |
387 | if (device->event_cb) | |
388 | return -EALREADY; | |
389 | ||
390 | device->events = 0; | |
391 | device->event_cb = event_cb; | |
392 | device->event_context = context; | |
393 | INIT_WORK(&device->event_work, mei_bus_event_work); | |
394 | ||
fcb136e1 | 395 | mei_cl_read_start(device->cl, 0); |
3e833295 SO |
396 | |
397 | return 0; | |
398 | } | |
399 | EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); | |
cf3baefb | 400 | |
aa6aef21 SO |
401 | void *mei_cl_get_drvdata(const struct mei_cl_device *device) |
402 | { | |
403 | return dev_get_drvdata(&device->dev); | |
404 | } | |
405 | EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); | |
406 | ||
407 | void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) | |
408 | { | |
409 | dev_set_drvdata(&device->dev, data); | |
410 | } | |
411 | EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); | |
412 | ||
e46980a1 SO |
413 | int mei_cl_enable_device(struct mei_cl_device *device) |
414 | { | |
415 | int err; | |
416 | struct mei_device *dev; | |
417 | struct mei_cl *cl = device->cl; | |
418 | ||
419 | if (cl == NULL) | |
420 | return -ENODEV; | |
421 | ||
422 | dev = cl->dev; | |
423 | ||
424 | mutex_lock(&dev->device_lock); | |
425 | ||
426 | cl->state = MEI_FILE_CONNECTING; | |
427 | ||
428 | err = mei_cl_connect(cl, NULL); | |
429 | if (err < 0) { | |
430 | mutex_unlock(&dev->device_lock); | |
431 | dev_err(&dev->pdev->dev, "Could not connect to the ME client"); | |
432 | ||
433 | return err; | |
434 | } | |
435 | ||
436 | mutex_unlock(&dev->device_lock); | |
437 | ||
438 | if (device->event_cb && !cl->read_cb) | |
fcb136e1 | 439 | mei_cl_read_start(device->cl, 0); |
e46980a1 SO |
440 | |
441 | if (!device->ops || !device->ops->enable) | |
442 | return 0; | |
443 | ||
444 | return device->ops->enable(device); | |
445 | } | |
446 | EXPORT_SYMBOL_GPL(mei_cl_enable_device); | |
447 | ||
448 | int mei_cl_disable_device(struct mei_cl_device *device) | |
449 | { | |
450 | int err; | |
451 | struct mei_device *dev; | |
452 | struct mei_cl *cl = device->cl; | |
453 | ||
454 | if (cl == NULL) | |
455 | return -ENODEV; | |
456 | ||
457 | dev = cl->dev; | |
458 | ||
459 | mutex_lock(&dev->device_lock); | |
460 | ||
461 | if (cl->state != MEI_FILE_CONNECTED) { | |
462 | mutex_unlock(&dev->device_lock); | |
463 | dev_err(&dev->pdev->dev, "Already disconnected"); | |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | cl->state = MEI_FILE_DISCONNECTING; | |
469 | ||
470 | err = mei_cl_disconnect(cl); | |
471 | if (err < 0) { | |
472 | mutex_unlock(&dev->device_lock); | |
473 | dev_err(&dev->pdev->dev, | |
474 | "Could not disconnect from the ME client"); | |
475 | ||
476 | return err; | |
477 | } | |
478 | ||
479 | /* Flush queues and remove any pending read */ | |
480 | mei_cl_flush_queues(cl); | |
481 | ||
482 | if (cl->read_cb) { | |
483 | struct mei_cl_cb *cb = NULL; | |
484 | ||
485 | cb = mei_cl_find_read_cb(cl); | |
486 | /* Remove entry from read list */ | |
487 | if (cb) | |
488 | list_del(&cb->list); | |
489 | ||
490 | cb = cl->read_cb; | |
491 | cl->read_cb = NULL; | |
492 | ||
493 | if (cb) { | |
494 | mei_io_cb_free(cb); | |
495 | cb = NULL; | |
496 | } | |
497 | } | |
498 | ||
bbedf2fc SO |
499 | device->event_cb = NULL; |
500 | ||
e46980a1 SO |
501 | mutex_unlock(&dev->device_lock); |
502 | ||
503 | if (!device->ops || !device->ops->disable) | |
504 | return 0; | |
505 | ||
506 | return device->ops->disable(device); | |
507 | } | |
508 | EXPORT_SYMBOL_GPL(mei_cl_disable_device); | |
509 | ||
cf3baefb SO |
510 | void mei_cl_bus_rx_event(struct mei_cl *cl) |
511 | { | |
512 | struct mei_cl_device *device = cl->device; | |
513 | ||
514 | if (!device || !device->event_cb) | |
515 | return; | |
516 | ||
517 | set_bit(MEI_CL_EVENT_RX, &device->events); | |
518 | ||
519 | schedule_work(&device->event_work); | |
520 | } | |
521 | ||
522 | int __init mei_cl_bus_init(void) | |
523 | { | |
524 | return bus_register(&mei_cl_bus_type); | |
525 | } | |
526 | ||
527 | void __exit mei_cl_bus_exit(void) | |
528 | { | |
529 | bus_unregister(&mei_cl_bus_type); | |
530 | } |