]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * driver.c - centralized device driver management | |
3 | * | |
4 | * Copyright (c) 2002-3 Patrick Mochel | |
5 | * Copyright (c) 2002-3 Open Source Development Labs | |
e5dd1278 GKH |
6 | * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> |
7 | * Copyright (c) 2007 Novell Inc. | |
1da177e4 LT |
8 | * |
9 | * This file is released under the GPLv2 | |
10 | * | |
11 | */ | |
12 | ||
1da177e4 LT |
13 | #include <linux/device.h> |
14 | #include <linux/module.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/string.h> | |
17 | #include "base.h" | |
18 | ||
19 | #define to_dev(node) container_of(node, struct device, driver_list) | |
1da177e4 | 20 | |
fae3cd00 | 21 | |
94e7b1c5 PM |
22 | static struct device * next_device(struct klist_iter * i) |
23 | { | |
24 | struct klist_node * n = klist_next(i); | |
25 | return n ? container_of(n, struct device, knode_driver) : NULL; | |
26 | } | |
27 | ||
fae3cd00 PM |
28 | /** |
29 | * driver_for_each_device - Iterator for devices bound to a driver. | |
30 | * @drv: Driver we're iterating. | |
c41455fb | 31 | * @start: Device to begin with |
fae3cd00 PM |
32 | * @data: Data to pass to the callback. |
33 | * @fn: Function to call for each device. | |
34 | * | |
4d12d2d9 | 35 | * Iterate over the @drv's list of devices calling @fn for each one. |
fae3cd00 PM |
36 | */ |
37 | ||
38 | int driver_for_each_device(struct device_driver * drv, struct device * start, | |
39 | void * data, int (*fn)(struct device *, void *)) | |
40 | { | |
94e7b1c5 | 41 | struct klist_iter i; |
fae3cd00 PM |
42 | struct device * dev; |
43 | int error = 0; | |
44 | ||
94e7b1c5 PM |
45 | if (!drv) |
46 | return -EINVAL; | |
47 | ||
e5dd1278 | 48 | klist_iter_init_node(&drv->p->klist_devices, &i, |
94e7b1c5 PM |
49 | start ? &start->knode_driver : NULL); |
50 | while ((dev = next_device(&i)) && !error) | |
fae3cd00 | 51 | error = fn(dev, data); |
94e7b1c5 | 52 | klist_iter_exit(&i); |
fae3cd00 PM |
53 | return error; |
54 | } | |
55 | ||
126eddfb | 56 | EXPORT_SYMBOL_GPL(driver_for_each_device); |
fae3cd00 PM |
57 | |
58 | ||
0edb5860 CH |
59 | /** |
60 | * driver_find_device - device iterator for locating a particular device. | |
c41455fb | 61 | * @drv: The device's driver |
0edb5860 CH |
62 | * @start: Device to begin with |
63 | * @data: Data to pass to match function | |
64 | * @match: Callback function to check device | |
65 | * | |
66 | * This is similar to the driver_for_each_device() function above, but | |
67 | * it returns a reference to a device that is 'found' for later use, as | |
68 | * determined by the @match callback. | |
69 | * | |
70 | * The callback should return 0 if the device doesn't match and non-zero | |
71 | * if it does. If the callback returns non-zero, this function will | |
72 | * return to the caller and not iterate over any more devices. | |
73 | */ | |
74 | struct device * driver_find_device(struct device_driver *drv, | |
75 | struct device * start, void * data, | |
76 | int (*match)(struct device *, void *)) | |
77 | { | |
78 | struct klist_iter i; | |
79 | struct device *dev; | |
80 | ||
81 | if (!drv) | |
82 | return NULL; | |
83 | ||
e5dd1278 | 84 | klist_iter_init_node(&drv->p->klist_devices, &i, |
0edb5860 CH |
85 | (start ? &start->knode_driver : NULL)); |
86 | while ((dev = next_device(&i))) | |
87 | if (match(dev, data) && get_device(dev)) | |
88 | break; | |
89 | klist_iter_exit(&i); | |
90 | return dev; | |
91 | } | |
92 | EXPORT_SYMBOL_GPL(driver_find_device); | |
93 | ||
1da177e4 LT |
94 | /** |
95 | * driver_create_file - create sysfs file for driver. | |
96 | * @drv: driver. | |
97 | * @attr: driver attribute descriptor. | |
98 | */ | |
99 | ||
100 | int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) | |
101 | { | |
102 | int error; | |
103 | if (get_driver(drv)) { | |
e5dd1278 | 104 | error = sysfs_create_file(&drv->p->kobj, &attr->attr); |
1da177e4 LT |
105 | put_driver(drv); |
106 | } else | |
107 | error = -EINVAL; | |
108 | return error; | |
109 | } | |
110 | ||
111 | ||
112 | /** | |
113 | * driver_remove_file - remove sysfs file for driver. | |
114 | * @drv: driver. | |
115 | * @attr: driver attribute descriptor. | |
116 | */ | |
117 | ||
118 | void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr) | |
119 | { | |
120 | if (get_driver(drv)) { | |
e5dd1278 | 121 | sysfs_remove_file(&drv->p->kobj, &attr->attr); |
1da177e4 LT |
122 | put_driver(drv); |
123 | } | |
124 | } | |
125 | ||
126 | ||
cbe9c595 GKH |
127 | /** |
128 | * driver_add_kobj - add a kobject below the specified driver | |
129 | * | |
130 | * You really don't want to do this, this is only here due to one looney | |
131 | * iseries driver, go poke those developers if you are annoyed about | |
132 | * this... | |
133 | */ | |
134 | int driver_add_kobj(struct device_driver *drv, struct kobject *kobj, | |
135 | const char *fmt, ...) | |
136 | { | |
137 | va_list args; | |
138 | char *name; | |
139 | ||
140 | va_start(args, fmt); | |
141 | name = kvasprintf(GFP_KERNEL, fmt, args); | |
142 | va_end(args); | |
143 | ||
144 | if (!name) | |
145 | return -ENOMEM; | |
146 | ||
b2d6db58 | 147 | return kobject_add(kobj, &drv->p->kobj, "%s", name); |
cbe9c595 GKH |
148 | } |
149 | EXPORT_SYMBOL_GPL(driver_add_kobj); | |
150 | ||
1da177e4 LT |
151 | /** |
152 | * get_driver - increment driver reference count. | |
153 | * @drv: driver. | |
154 | */ | |
155 | struct device_driver * get_driver(struct device_driver * drv) | |
156 | { | |
e5dd1278 GKH |
157 | if (drv) { |
158 | struct driver_private *priv; | |
159 | struct kobject *kobj; | |
160 | ||
161 | kobj = kobject_get(&drv->p->kobj); | |
162 | priv = to_driver(kobj); | |
163 | return priv->driver; | |
164 | } | |
165 | return NULL; | |
1da177e4 LT |
166 | } |
167 | ||
168 | ||
169 | /** | |
170 | * put_driver - decrement driver's refcount. | |
171 | * @drv: driver. | |
172 | */ | |
173 | void put_driver(struct device_driver * drv) | |
174 | { | |
e5dd1278 | 175 | kobject_put(&drv->p->kobj); |
1da177e4 LT |
176 | } |
177 | ||
57c74534 CH |
178 | static int driver_add_groups(struct device_driver *drv, |
179 | struct attribute_group **groups) | |
180 | { | |
181 | int error = 0; | |
182 | int i; | |
183 | ||
184 | if (groups) { | |
185 | for (i = 0; groups[i]; i++) { | |
e5dd1278 | 186 | error = sysfs_create_group(&drv->p->kobj, groups[i]); |
57c74534 CH |
187 | if (error) { |
188 | while (--i >= 0) | |
e5dd1278 | 189 | sysfs_remove_group(&drv->p->kobj, |
57c74534 CH |
190 | groups[i]); |
191 | break; | |
192 | } | |
193 | } | |
194 | } | |
195 | return error; | |
196 | } | |
197 | ||
198 | static void driver_remove_groups(struct device_driver *drv, | |
199 | struct attribute_group **groups) | |
200 | { | |
201 | int i; | |
202 | ||
203 | if (groups) | |
204 | for (i = 0; groups[i]; i++) | |
e5dd1278 | 205 | sysfs_remove_group(&drv->p->kobj, groups[i]); |
57c74534 CH |
206 | } |
207 | ||
208 | ||
1da177e4 LT |
209 | /** |
210 | * driver_register - register driver with bus | |
211 | * @drv: driver to register | |
212 | * | |
213 | * We pass off most of the work to the bus_add_driver() call, | |
214 | * since most of the things we have to do deal with the bus | |
215 | * structures. | |
1da177e4 LT |
216 | */ |
217 | int driver_register(struct device_driver * drv) | |
218 | { | |
57c74534 CH |
219 | int ret; |
220 | ||
594c8281 RK |
221 | if ((drv->bus->probe && drv->probe) || |
222 | (drv->bus->remove && drv->remove) || | |
223 | (drv->bus->shutdown && drv->shutdown)) { | |
224 | printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); | |
225 | } | |
57c74534 CH |
226 | ret = bus_add_driver(drv); |
227 | if (ret) | |
228 | return ret; | |
229 | ret = driver_add_groups(drv, drv->groups); | |
230 | if (ret) | |
231 | bus_remove_driver(drv); | |
232 | return ret; | |
1da177e4 LT |
233 | } |
234 | ||
1da177e4 LT |
235 | /** |
236 | * driver_unregister - remove driver from system. | |
237 | * @drv: driver. | |
238 | * | |
239 | * Again, we pass off most of the work to the bus-level call. | |
1da177e4 LT |
240 | */ |
241 | ||
242 | void driver_unregister(struct device_driver * drv) | |
243 | { | |
57c74534 | 244 | driver_remove_groups(drv, drv->groups); |
1da177e4 | 245 | bus_remove_driver(drv); |
1da177e4 LT |
246 | } |
247 | ||
248 | /** | |
249 | * driver_find - locate driver on a bus by its name. | |
250 | * @name: name of the driver. | |
251 | * @bus: bus to scan for the driver. | |
252 | * | |
253 | * Call kset_find_obj() to iterate over list of drivers on | |
254 | * a bus to find driver by name. Return driver if found. | |
255 | * | |
256 | * Note that kset_find_obj increments driver's reference count. | |
257 | */ | |
258 | struct device_driver *driver_find(const char *name, struct bus_type *bus) | |
259 | { | |
c6f7e72a | 260 | struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); |
e5dd1278 GKH |
261 | struct driver_private *priv; |
262 | ||
263 | if (k) { | |
264 | priv = to_driver(k); | |
265 | return priv->driver; | |
266 | } | |
1da177e4 LT |
267 | return NULL; |
268 | } | |
269 | ||
270 | EXPORT_SYMBOL_GPL(driver_register); | |
271 | EXPORT_SYMBOL_GPL(driver_unregister); | |
272 | EXPORT_SYMBOL_GPL(get_driver); | |
273 | EXPORT_SYMBOL_GPL(put_driver); | |
274 | EXPORT_SYMBOL_GPL(driver_find); | |
275 | ||
276 | EXPORT_SYMBOL_GPL(driver_create_file); | |
277 | EXPORT_SYMBOL_GPL(driver_remove_file); |