aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei/bus.c
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2013-03-27 11:29:53 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-29 11:35:47 -0400
commite5354107e14755991da82e0d2a4791db92908d9d (patch)
treeadb8491f5d2a37cbd9304aafb1f762fed4d6c4a5 /drivers/misc/mei/bus.c
parent40e0b67be099175d069b0cf46f1102f352d46c61 (diff)
mei: bus: Initial MEI Client bus type implementation
mei client bus will present some of the mei clients as devices for other standard subsystems Implement the probe, remove, match, device addtion routines, along with the sysfs and uevent ones. mei_cl_device_id is also added to mod_devicetable.h A mei-cleint-bus.txt document describing the rationale and the API usage is also added while ABI/testing/sysfs-bus-mei describeis the modalias ABI. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/bus.c')
-rw-r--r--drivers/misc/mei/bus.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
new file mode 100644
index 000000000000..78c876af2676
--- /dev/null
+++ b/drivers/misc/mei/bus.c
@@ -0,0 +1,172 @@
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>
19#include <linux/init.h>
20#include <linux/errno.h>
21#include <linux/slab.h>
22#include <linux/mutex.h>
23#include <linux/interrupt.h>
24#include <linux/pci.h>
25#include <linux/mei_cl_bus.h>
26
27#include "mei_dev.h"
28
29#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
30#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
31
32static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
33{
34 struct mei_cl_device *device = to_mei_cl_device(dev);
35 struct mei_cl_driver *driver = to_mei_cl_driver(drv);
36 const struct mei_cl_device_id *id;
37
38 if (!device)
39 return 0;
40
41 if (!driver || !driver->id_table)
42 return 0;
43
44 id = driver->id_table;
45
46 while (id->name[0]) {
47 if (!strcmp(dev_name(dev), id->name))
48 return 1;
49
50 id++;
51 }
52
53 return 0;
54}
55
56static int mei_cl_device_probe(struct device *dev)
57{
58 struct mei_cl_device *device = to_mei_cl_device(dev);
59 struct mei_cl_driver *driver;
60 struct mei_cl_device_id id;
61
62 if (!device)
63 return 0;
64
65 driver = to_mei_cl_driver(dev->driver);
66 if (!driver || !driver->probe)
67 return -ENODEV;
68
69 dev_dbg(dev, "Device probe\n");
70
71 strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE);
72
73 return driver->probe(device, &id);
74}
75
76static int mei_cl_device_remove(struct device *dev)
77{
78 struct mei_cl_device *device = to_mei_cl_device(dev);
79 struct mei_cl_driver *driver;
80
81 if (!device || !dev->driver)
82 return 0;
83
84 driver = to_mei_cl_driver(dev->driver);
85 if (!driver->remove) {
86 dev->driver = NULL;
87
88 return 0;
89 }
90
91 return driver->remove(device);
92}
93
94static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
95 char *buf)
96{
97 int len;
98
99 len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev));
100
101 return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
102}
103
104static struct device_attribute mei_cl_dev_attrs[] = {
105 __ATTR_RO(modalias),
106 __ATTR_NULL,
107};
108
109static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
110{
111 if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
112 return -ENOMEM;
113
114 return 0;
115}
116
117static struct bus_type mei_cl_bus_type = {
118 .name = "mei",
119 .dev_attrs = mei_cl_dev_attrs,
120 .match = mei_cl_device_match,
121 .probe = mei_cl_device_probe,
122 .remove = mei_cl_device_remove,
123 .uevent = mei_cl_uevent,
124};
125
126static void mei_cl_dev_release(struct device *dev)
127{
128 kfree(to_mei_cl_device(dev));
129}
130
131static struct device_type mei_cl_device_type = {
132 .release = mei_cl_dev_release,
133};
134
135struct mei_cl_device *mei_cl_add_device(struct mei_device *mei_device,
136 uuid_le uuid, char *name)
137{
138 struct mei_cl_device *device;
139 int status;
140
141 device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
142 if (!device)
143 return NULL;
144
145 device->dev.parent = &mei_device->pdev->dev;
146 device->dev.bus = &mei_cl_bus_type;
147 device->dev.type = &mei_cl_device_type;
148
149 dev_set_name(&device->dev, "%s", name);
150
151 status = device_register(&device->dev);
152 if (status)
153 goto out_err;
154
155 dev_dbg(&device->dev, "client %s registered\n", name);
156
157 return device;
158
159out_err:
160 dev_err(device->dev.parent, "Failed to register MEI client\n");
161
162 kfree(device);
163
164 return NULL;
165}
166EXPORT_SYMBOL_GPL(mei_cl_add_device);
167
168void mei_cl_remove_device(struct mei_cl_device *device)
169{
170 device_unregister(&device->dev);
171}
172EXPORT_SYMBOL_GPL(mei_cl_remove_device);