blob: 68b7756bf384683693bad94dad7f5e96c5e7e4a4 [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 Winkler62382992015-07-23 15:08:35 +030033/**
34 * __mei_cl_send - internal client send (write)
35 *
36 * @cl: host client
37 * @buf: buffer to send
38 * @length: buffer length
39 * @blocking: wait for write completion
40 *
41 * Return: written size bytes or < 0 on error
42 */
43ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
44 bool blocking)
45{
46 struct mei_device *bus;
47 struct mei_cl_cb *cb = NULL;
48 ssize_t rets;
49
50 if (WARN_ON(!cl || !cl->dev))
51 return -ENODEV;
52
53 bus = cl->dev;
54
55 mutex_lock(&bus->device_lock);
56 if (!mei_cl_is_connected(cl)) {
57 rets = -ENODEV;
58 goto out;
59 }
60
61 /* Check if we have an ME client device */
62 if (!mei_me_cl_is_active(cl->me_cl)) {
63 rets = -ENOTTY;
64 goto out;
65 }
66
67 if (length > mei_cl_mtu(cl)) {
68 rets = -EFBIG;
69 goto out;
70 }
71
72 cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
73 if (!cb) {
74 rets = -ENOMEM;
75 goto out;
76 }
77
78 memcpy(cb->buf.data, buf, length);
79
80 rets = mei_cl_write(cl, cb, blocking);
81
82out:
83 mutex_unlock(&bus->device_lock);
84 if (rets < 0)
85 mei_io_cb_free(cb);
86
87 return rets;
88}
89
90/**
91 * __mei_cl_recv - internal client receive (read)
92 *
93 * @cl: host client
94 * @buf: buffer to send
95 * @length: buffer length
96 *
97 * Return: read size in bytes of < 0 on error
98 */
99ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
100{
101 struct mei_device *bus;
102 struct mei_cl_cb *cb;
103 size_t r_length;
104 ssize_t rets;
105
106 if (WARN_ON(!cl || !cl->dev))
107 return -ENODEV;
108
109 bus = cl->dev;
110
111 mutex_lock(&bus->device_lock);
112
113 cb = mei_cl_read_cb(cl, NULL);
114 if (cb)
115 goto copy;
116
117 rets = mei_cl_read_start(cl, length, NULL);
118 if (rets && rets != -EBUSY)
119 goto out;
120
121 /* wait on event only if there is no other waiter */
122 if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
123
124 mutex_unlock(&bus->device_lock);
125
126 if (wait_event_interruptible(cl->rx_wait,
127 (!list_empty(&cl->rd_completed)) ||
128 (!mei_cl_is_connected(cl)))) {
129
130 if (signal_pending(current))
131 return -EINTR;
132 return -ERESTARTSYS;
133 }
134
135 mutex_lock(&bus->device_lock);
136
137 if (!mei_cl_is_connected(cl)) {
138 rets = -EBUSY;
139 goto out;
140 }
141 }
142
143 cb = mei_cl_read_cb(cl, NULL);
144 if (!cb) {
145 rets = 0;
146 goto out;
147 }
148
149copy:
150 if (cb->status) {
151 rets = cb->status;
152 goto free;
153 }
154
155 r_length = min_t(size_t, length, cb->buf_idx);
156 memcpy(buf, cb->buf.data, r_length);
157 rets = r_length;
158
159free:
160 mei_io_cb_free(cb);
161out:
162 mutex_unlock(&bus->device_lock);
163
164 return rets;
165}
166
167/**
168 * mei_cl_send - me device send (write)
169 *
170 * @cldev: me client device
171 * @buf: buffer to send
172 * @length: buffer length
173 *
174 * Return: written size in bytes or < 0 on error
175 */
176ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
177{
178 struct mei_cl *cl = cldev->cl;
179
180 if (cl == NULL)
181 return -ENODEV;
182
183 return __mei_cl_send(cl, buf, length, 1);
184}
185EXPORT_SYMBOL_GPL(mei_cl_send);
186
187/**
188 * mei_cl_recv - client receive (read)
189 *
190 * @cldev: me client device
191 * @buf: buffer to send
192 * @length: buffer length
193 *
194 * Return: read size in bytes of < 0 on error
195 */
196ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
197{
198 struct mei_cl *cl = cldev->cl;
199
200 if (cl == NULL)
201 return -ENODEV;
202
203 return __mei_cl_recv(cl, buf, length);
204}
205EXPORT_SYMBOL_GPL(mei_cl_recv);
206
207/**
208 * mei_bus_event_work - dispatch rx event for a bus device
209 * and schedule new work
210 *
211 * @work: work
212 */
213static void mei_bus_event_work(struct work_struct *work)
214{
215 struct mei_cl_device *cldev;
216
217 cldev = container_of(work, struct mei_cl_device, event_work);
218
219 if (cldev->event_cb)
220 cldev->event_cb(cldev, cldev->events, cldev->event_context);
221
222 cldev->events = 0;
223
224 /* Prepare for the next read */
225 mei_cl_read_start(cldev->cl, 0, NULL);
226}
227
228/**
229 * mei_cl_bus_rx_event - schedule rx evenet
230 *
231 * @cl: host client
232 */
233void mei_cl_bus_rx_event(struct mei_cl *cl)
234{
235 struct mei_cl_device *cldev = cl->cldev;
236
237 if (!cldev || !cldev->event_cb)
238 return;
239
240 set_bit(MEI_CL_EVENT_RX, &cldev->events);
241
242 schedule_work(&cldev->event_work);
243}
244
245/**
246 * mei_cl_register_event_cb - register event callback
247 *
248 * @cldev: me client devices
249 * @event_cb: callback function
250 * @context: driver context data
251 *
252 * Return: 0 on success
253 * -EALREADY if an callback is already registered
254 * <0 on other errors
255 */
256int mei_cl_register_event_cb(struct mei_cl_device *cldev,
257 mei_cl_event_cb_t event_cb, void *context)
258{
Tomas Winkler48168f42015-07-23 15:08:38 +0300259 int ret;
260
Tomas Winkler62382992015-07-23 15:08:35 +0300261 if (cldev->event_cb)
262 return -EALREADY;
263
264 cldev->events = 0;
265 cldev->event_cb = event_cb;
266 cldev->event_context = context;
267 INIT_WORK(&cldev->event_work, mei_bus_event_work);
268
Tomas Winkler48168f42015-07-23 15:08:38 +0300269 ret = mei_cl_read_start(cldev->cl, 0, NULL);
270 if (ret && ret != -EBUSY)
271 return ret;
Tomas Winkler62382992015-07-23 15:08:35 +0300272
273 return 0;
274}
275EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
276
277/**
278 * mei_cl_get_drvdata - driver data getter
279 *
280 * @cldev: mei client device
281 *
282 * Return: driver private data
283 */
284void *mei_cl_get_drvdata(const struct mei_cl_device *cldev)
285{
286 return dev_get_drvdata(&cldev->dev);
287}
288EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
289
290/**
291 * mei_cl_set_drvdata - driver data setter
292 *
293 * @cldev: mei client device
294 * @data: data to store
295 */
296void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data)
297{
298 dev_set_drvdata(&cldev->dev, data);
299}
300EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
301
302/**
303 * mei_cl_enable_device - enable me client device
304 * create connection with me client
305 *
306 * @cldev: me client device
307 *
308 * Return: 0 on success and < 0 on error
309 */
310int mei_cl_enable_device(struct mei_cl_device *cldev)
311{
312 int err;
313 struct mei_device *bus;
314 struct mei_cl *cl = cldev->cl;
315
316 if (cl == NULL)
317 return -ENODEV;
318
319 bus = cl->dev;
320
321 mutex_lock(&bus->device_lock);
322
323 if (mei_cl_is_connected(cl)) {
324 mutex_unlock(&bus->device_lock);
325 dev_warn(bus->dev, "Already connected");
326 return -EBUSY;
327 }
328
329 err = mei_cl_connect(cl, cldev->me_cl, NULL);
330 if (err < 0) {
331 mutex_unlock(&bus->device_lock);
332 dev_err(bus->dev, "Could not connect to the ME client");
333
334 return err;
335 }
336
337 mutex_unlock(&bus->device_lock);
338
Tomas Winkler62382992015-07-23 15:08:35 +0300339 return 0;
340}
341EXPORT_SYMBOL_GPL(mei_cl_enable_device);
342
343/**
344 * mei_cl_disable_device - disable me client device
345 * disconnect form the me client
346 *
347 * @cldev: me client device
348 *
349 * Return: 0 on success and < 0 on error
350 */
351int mei_cl_disable_device(struct mei_cl_device *cldev)
352{
353 int err;
354 struct mei_device *bus;
355 struct mei_cl *cl = cldev->cl;
356
357 if (cl == NULL)
358 return -ENODEV;
359
360 bus = cl->dev;
361
362 cldev->event_cb = NULL;
363
364 mutex_lock(&bus->device_lock);
365
366 if (!mei_cl_is_connected(cl)) {
367 dev_err(bus->dev, "Already disconnected");
368 err = 0;
369 goto out;
370 }
371
372 err = mei_cl_disconnect(cl);
373 if (err < 0) {
374 dev_err(bus->dev, "Could not disconnect from the ME client");
375 goto out;
376 }
377
378 /* Flush queues and remove any pending read */
379 mei_cl_flush_queues(cl, NULL);
380
381out:
382 mutex_unlock(&bus->device_lock);
383 return err;
384
385}
386EXPORT_SYMBOL_GPL(mei_cl_disable_device);
387
Tomas Winkler688a9cc2015-07-23 15:08:39 +0300388/**
389 * mei_cl_device_find - find matching entry in the driver id table
390 *
391 * @cldev: me client device
392 * @cldrv: me client driver
393 *
394 * Return: id on success; NULL if no id is matching
395 */
396static const
397struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev,
398 struct mei_cl_driver *cldrv)
Samuel Ortize5354102013-03-27 17:29:53 +0200399{
Samuel Ortize5354102013-03-27 17:29:53 +0200400 const struct mei_cl_device_id *id;
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300401 const uuid_le *uuid;
Samuel Ortize5354102013-03-27 17:29:53 +0200402
Tomas Winklerb37719c32015-07-23 15:08:33 +0300403 uuid = mei_me_cl_uuid(cldev->me_cl);
Samuel Ortize5354102013-03-27 17:29:53 +0200404
Tomas Winklerb37719c32015-07-23 15:08:33 +0300405 id = cldrv->id_table;
Greg Kroah-Hartmanb144ce22015-05-27 17:17:27 -0700406 while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) {
Greg Kroah-Hartmanb144ce22015-05-27 17:17:27 -0700407 if (!uuid_le_cmp(*uuid, id->uuid)) {
Tomas Winkler688a9cc2015-07-23 15:08:39 +0300408
409 if (!cldev->name[0])
410 return id;
411
412 if (!strncmp(cldev->name, id->name, sizeof(id->name)))
413 return id;
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300414 }
Samuel Ortize5354102013-03-27 17:29:53 +0200415
416 id++;
417 }
418
Tomas Winkler688a9cc2015-07-23 15:08:39 +0300419 return NULL;
420}
421
422/**
423 * mei_cl_device_match - device match function
424 *
425 * @dev: device
426 * @drv: driver
427 *
428 * Return: 1 if matching device was found 0 otherwise
429 */
430static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
431{
432 struct mei_cl_device *cldev = to_mei_cl_device(dev);
433 struct mei_cl_driver *cldrv = to_mei_cl_driver(drv);
434 const struct mei_cl_device_id *found_id;
435
436 if (!cldev)
437 return 0;
438
Tomas Winkler71ce7892015-07-23 15:08:43 +0300439 if (!cldev->do_match)
440 return 0;
441
Tomas Winkler688a9cc2015-07-23 15:08:39 +0300442 if (!cldrv || !cldrv->id_table)
443 return 0;
444
445 found_id = mei_cl_device_find(cldev, cldrv);
446 if (found_id)
447 return 1;
448
Samuel Ortize5354102013-03-27 17:29:53 +0200449 return 0;
450}
451
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300452/**
453 * mei_cl_device_probe - bus probe function
454 *
455 * @dev: device
456 *
457 * Return: 0 on success; < 0 otherwise
458 */
Samuel Ortize5354102013-03-27 17:29:53 +0200459static int mei_cl_device_probe(struct device *dev)
460{
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300461 struct mei_cl_device *cldev;
Tomas Winklerb37719c32015-07-23 15:08:33 +0300462 struct mei_cl_driver *cldrv;
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300463 const struct mei_cl_device_id *id;
464
465 cldev = to_mei_cl_device(dev);
466 cldrv = to_mei_cl_driver(dev->driver);
Samuel Ortize5354102013-03-27 17:29:53 +0200467
Tomas Winklerb37719c32015-07-23 15:08:33 +0300468 if (!cldev)
Samuel Ortize5354102013-03-27 17:29:53 +0200469 return 0;
470
Tomas Winklerb37719c32015-07-23 15:08:33 +0300471 if (!cldrv || !cldrv->probe)
Samuel Ortize5354102013-03-27 17:29:53 +0200472 return -ENODEV;
473
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300474 id = mei_cl_device_find(cldev, cldrv);
475 if (!id)
476 return -ENODEV;
Samuel Ortize5354102013-03-27 17:29:53 +0200477
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300478 __module_get(THIS_MODULE);
Samuel Ortize5354102013-03-27 17:29:53 +0200479
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300480 return cldrv->probe(cldev, id);
Samuel Ortize5354102013-03-27 17:29:53 +0200481}
482
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300483/**
484 * mei_cl_device_remove - remove device from the bus
485 *
486 * @dev: device
487 *
488 * Return: 0 on success; < 0 otherwise
489 */
Samuel Ortize5354102013-03-27 17:29:53 +0200490static int mei_cl_device_remove(struct device *dev)
491{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300492 struct mei_cl_device *cldev = to_mei_cl_device(dev);
493 struct mei_cl_driver *cldrv;
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300494 int ret = 0;
Samuel Ortize5354102013-03-27 17:29:53 +0200495
Tomas Winklerb37719c32015-07-23 15:08:33 +0300496 if (!cldev || !dev->driver)
Samuel Ortize5354102013-03-27 17:29:53 +0200497 return 0;
498
Tomas Winklerb37719c32015-07-23 15:08:33 +0300499 if (cldev->event_cb) {
500 cldev->event_cb = NULL;
501 cancel_work_sync(&cldev->event_work);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200502 }
503
Tomas Winklerb37719c32015-07-23 15:08:33 +0300504 cldrv = to_mei_cl_driver(dev->driver);
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300505 if (cldrv->remove)
506 ret = cldrv->remove(cldev);
Samuel Ortize5354102013-03-27 17:29:53 +0200507
Tomas Winklerfeb8cd02015-07-23 15:08:40 +0300508 module_put(THIS_MODULE);
509 dev->driver = NULL;
510 return ret;
Samuel Ortize5354102013-03-27 17:29:53 +0200511
Samuel Ortize5354102013-03-27 17:29:53 +0200512}
513
Tomas Winkler007d64e2015-05-07 15:54:03 +0300514static ssize_t name_show(struct device *dev, struct device_attribute *a,
515 char *buf)
516{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300517 struct mei_cl_device *cldev = to_mei_cl_device(dev);
Tomas Winkler007d64e2015-05-07 15:54:03 +0300518 size_t len;
519
Tomas Winklerb37719c32015-07-23 15:08:33 +0300520 len = snprintf(buf, PAGE_SIZE, "%s", cldev->name);
Tomas Winkler007d64e2015-05-07 15:54:03 +0300521
522 return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
523}
524static DEVICE_ATTR_RO(name);
525
526static ssize_t uuid_show(struct device *dev, struct device_attribute *a,
527 char *buf)
528{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300529 struct mei_cl_device *cldev = to_mei_cl_device(dev);
530 const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
Tomas Winkler007d64e2015-05-07 15:54:03 +0300531 size_t len;
532
533 len = snprintf(buf, PAGE_SIZE, "%pUl", uuid);
534
535 return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
536}
537static DEVICE_ATTR_RO(uuid);
538
Samuel Ortize5354102013-03-27 17:29:53 +0200539static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
540 char *buf)
541{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300542 struct mei_cl_device *cldev = to_mei_cl_device(dev);
543 const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300544 size_t len;
Samuel Ortize5354102013-03-27 17:29:53 +0200545
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300546 len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":",
Tomas Winklerb37719c32015-07-23 15:08:33 +0300547 cldev->name, MEI_CL_UUID_ARGS(uuid->b));
Samuel Ortize5354102013-03-27 17:29:53 +0200548
549 return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
550}
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700551static DEVICE_ATTR_RO(modalias);
Samuel Ortize5354102013-03-27 17:29:53 +0200552
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700553static struct attribute *mei_cl_dev_attrs[] = {
Tomas Winkler007d64e2015-05-07 15:54:03 +0300554 &dev_attr_name.attr,
555 &dev_attr_uuid.attr,
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700556 &dev_attr_modalias.attr,
557 NULL,
Samuel Ortize5354102013-03-27 17:29:53 +0200558};
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700559ATTRIBUTE_GROUPS(mei_cl_dev);
Samuel Ortize5354102013-03-27 17:29:53 +0200560
Tomas Winkler38d3c002015-07-23 15:08:36 +0300561/**
562 * mei_cl_device_uevent - me client bus uevent handler
563 *
564 * @dev: device
565 * @env: uevent kobject
566 *
567 * Return: 0 on success -ENOMEM on when add_uevent_var fails
568 */
569static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env)
Samuel Ortize5354102013-03-27 17:29:53 +0200570{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300571 struct mei_cl_device *cldev = to_mei_cl_device(dev);
572 const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl);
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300573
Tomas Winkler007d64e2015-05-07 15:54:03 +0300574 if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid))
575 return -ENOMEM;
576
Tomas Winklerb37719c32015-07-23 15:08:33 +0300577 if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name))
Tomas Winkler007d64e2015-05-07 15:54:03 +0300578 return -ENOMEM;
579
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300580 if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":",
Tomas Winklerb37719c32015-07-23 15:08:33 +0300581 cldev->name, MEI_CL_UUID_ARGS(uuid->b)))
Samuel Ortize5354102013-03-27 17:29:53 +0200582 return -ENOMEM;
583
584 return 0;
585}
586
587static struct bus_type mei_cl_bus_type = {
588 .name = "mei",
Greg Kroah-Hartman32f389ec2013-08-23 14:24:39 -0700589 .dev_groups = mei_cl_dev_groups,
Samuel Ortize5354102013-03-27 17:29:53 +0200590 .match = mei_cl_device_match,
591 .probe = mei_cl_device_probe,
592 .remove = mei_cl_device_remove,
Tomas Winkler38d3c002015-07-23 15:08:36 +0300593 .uevent = mei_cl_device_uevent,
Samuel Ortize5354102013-03-27 17:29:53 +0200594};
595
Tomas Winkler512f64d2015-07-23 15:08:41 +0300596static struct mei_device *mei_dev_bus_get(struct mei_device *bus)
597{
598 if (bus)
599 get_device(bus->dev);
600
601 return bus;
602}
603
604static void mei_dev_bus_put(struct mei_device *bus)
605{
606 if (bus)
607 put_device(bus->dev);
608}
609
Samuel Ortize5354102013-03-27 17:29:53 +0200610static void mei_cl_dev_release(struct device *dev)
611{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300612 struct mei_cl_device *cldev = to_mei_cl_device(dev);
Alexander Usyskind49ed642015-05-04 09:43:54 +0300613
Tomas Winklerb37719c32015-07-23 15:08:33 +0300614 if (!cldev)
Alexander Usyskind49ed642015-05-04 09:43:54 +0300615 return;
616
Tomas Winklerb37719c32015-07-23 15:08:33 +0300617 mei_me_cl_put(cldev->me_cl);
Tomas Winkler512f64d2015-07-23 15:08:41 +0300618 mei_dev_bus_put(cldev->bus);
Tomas Winklerb37719c32015-07-23 15:08:33 +0300619 kfree(cldev);
Samuel Ortize5354102013-03-27 17:29:53 +0200620}
621
622static struct device_type mei_cl_device_type = {
623 .release = mei_cl_dev_release,
624};
625
Tomas Winklerb37719c32015-07-23 15:08:33 +0300626struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *bus,
Alexander Usyskind49ed642015-05-04 09:43:54 +0300627 uuid_le uuid)
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200628{
Tomas Winkler31f88f52014-02-17 15:13:25 +0200629 struct mei_cl *cl;
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200630
Tomas Winklerb37719c32015-07-23 15:08:33 +0300631 list_for_each_entry(cl, &bus->device_list, device_link) {
632 if (cl->cldev && cl->cldev->me_cl &&
633 !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->cldev->me_cl)))
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200634 return cl;
635 }
636
637 return NULL;
638}
Alexander Usyskind49ed642015-05-04 09:43:54 +0300639
Tomas Winkler71ce7892015-07-23 15:08:43 +0300640/**
641 * mei_cl_dev_alloc - initialize and allocate mei client device
642 *
643 * @bus: mei device
644 * @me_cl: me client
645 *
646 * Return: allocated device structur or NULL on allocation failure
647 */
648static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus,
649 struct mei_me_client *me_cl)
650{
651 struct mei_cl_device *cldev;
652
653 cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
654 if (!cldev)
655 return NULL;
656
657 device_initialize(&cldev->dev);
658 cldev->dev.parent = bus->dev;
659 cldev->dev.bus = &mei_cl_bus_type;
660 cldev->dev.type = &mei_cl_device_type;
661 cldev->bus = mei_dev_bus_get(bus);
662 cldev->me_cl = mei_me_cl_get(me_cl);
663 cldev->is_added = 0;
664 INIT_LIST_HEAD(&cldev->bus_list);
665
666 return cldev;
667}
668
669/**
670 * mei_cl_dev_setup - setup me client device
671 * run fix up routines and set the device name
672 *
673 * @bus: mei device
674 * @cldev: me client device
675 *
676 * Return: true if the device is eligible for enumeration
677 */
678static bool mei_cl_dev_setup(struct mei_device *bus,
679 struct mei_cl_device *cldev)
680{
681 cldev->do_match = 1;
682 mei_cl_dev_fixup(cldev);
683
684 if (cldev->do_match)
685 dev_set_name(&cldev->dev, "mei:%s:%pUl",
686 cldev->name, mei_me_cl_uuid(cldev->me_cl));
687
688 return cldev->do_match == 1;
689}
690
691/**
692 * mei_cl_bus_dev_add - add me client devices
693 *
694 * @cldev: me client device
695 *
696 * Return: 0 on success; < 0 on failre
697 */
698static int mei_cl_bus_dev_add(struct mei_cl_device *cldev)
699{
700 int ret;
701
702 dev_dbg(cldev->bus->dev, "adding %pUL\n", mei_me_cl_uuid(cldev->me_cl));
703 ret = device_add(&cldev->dev);
704 if (!ret)
705 cldev->is_added = 1;
706
707 return ret;
708}
709
Tomas Winklerb37719c32015-07-23 15:08:33 +0300710struct mei_cl_device *mei_cl_add_device(struct mei_device *bus,
Alexander Usyskind49ed642015-05-04 09:43:54 +0300711 struct mei_me_client *me_cl,
712 struct mei_cl *cl,
Tomas Winklerbe9b7202015-05-07 15:54:04 +0300713 char *name)
Samuel Ortize5354102013-03-27 17:29:53 +0200714{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300715 struct mei_cl_device *cldev;
Samuel Ortize5354102013-03-27 17:29:53 +0200716 int status;
717
Tomas Winkler71ce7892015-07-23 15:08:43 +0300718 cldev = mei_cl_dev_alloc(bus, me_cl);
Tomas Winklerb37719c32015-07-23 15:08:33 +0300719 if (!cldev)
Samuel Ortize5354102013-03-27 17:29:53 +0200720 return NULL;
721
Tomas Winklerb37719c32015-07-23 15:08:33 +0300722 cldev->cl = cl;
Tomas Winklerb37719c32015-07-23 15:08:33 +0300723 strlcpy(cldev->name, name, sizeof(cldev->name));
Tomas Winklerc93b76b2015-05-07 15:54:02 +0300724
Tomas Winkler71ce7892015-07-23 15:08:43 +0300725 mei_cl_dev_setup(bus, cldev);
Samuel Ortize5354102013-03-27 17:29:53 +0200726
Tomas Winkler71ce7892015-07-23 15:08:43 +0300727 status = mei_cl_bus_dev_add(cldev);
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200728 if (status) {
Tomas Winklerb37719c32015-07-23 15:08:33 +0300729 dev_err(bus->dev, "Failed to register MEI device\n");
730 mei_me_cl_put(cldev->me_cl);
Tomas Winkler512f64d2015-07-23 15:08:41 +0300731 mei_dev_bus_put(bus);
Tomas Winklerb37719c32015-07-23 15:08:33 +0300732 kfree(cldev);
Samuel Ortiza7b71bc2013-03-27 17:29:56 +0200733 return NULL;
734 }
735
Tomas Winklerb37719c32015-07-23 15:08:33 +0300736 cl->cldev = cldev;
Samuel Ortize5354102013-03-27 17:29:53 +0200737
Tomas Winklerb37719c32015-07-23 15:08:33 +0300738 dev_dbg(&cldev->dev, "client %s registered\n", name);
Samuel Ortize5354102013-03-27 17:29:53 +0200739
Tomas Winklerb37719c32015-07-23 15:08:33 +0300740 return cldev;
Samuel Ortize5354102013-03-27 17:29:53 +0200741}
742EXPORT_SYMBOL_GPL(mei_cl_add_device);
743
Tomas Winklerb37719c32015-07-23 15:08:33 +0300744void mei_cl_remove_device(struct mei_cl_device *cldev)
Samuel Ortize5354102013-03-27 17:29:53 +0200745{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300746 device_unregister(&cldev->dev);
Samuel Ortize5354102013-03-27 17:29:53 +0200747}
748EXPORT_SYMBOL_GPL(mei_cl_remove_device);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200749
Tomas Winklerb37719c32015-07-23 15:08:33 +0300750int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner)
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200751{
752 int err;
753
Tomas Winklerb37719c32015-07-23 15:08:33 +0300754 cldrv->driver.name = cldrv->name;
755 cldrv->driver.owner = owner;
756 cldrv->driver.bus = &mei_cl_bus_type;
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200757
Tomas Winklerb37719c32015-07-23 15:08:33 +0300758 err = driver_register(&cldrv->driver);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200759 if (err)
760 return err;
761
Tomas Winklerb37719c32015-07-23 15:08:33 +0300762 pr_debug("mei: driver [%s] registered\n", cldrv->driver.name);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200763
764 return 0;
765}
766EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
767
Tomas Winklerb37719c32015-07-23 15:08:33 +0300768void mei_cl_driver_unregister(struct mei_cl_driver *cldrv)
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200769{
Tomas Winklerb37719c32015-07-23 15:08:33 +0300770 driver_unregister(&cldrv->driver);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200771
Tomas Winklerb37719c32015-07-23 15:08:33 +0300772 pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name);
Samuel Ortiz333e4ee2013-03-27 17:29:54 +0200773}
774EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
Samuel Ortiz3e833292013-03-27 17:29:55 +0200775
Samuel Ortizcf3baef2013-03-27 17:29:57 +0200776int __init mei_cl_bus_init(void)
777{
778 return bus_register(&mei_cl_bus_type);
779}
780
781void __exit mei_cl_bus_exit(void)
782{
783 bus_unregister(&mei_cl_bus_type);
784}