Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
462 | giacomo | 1 | /* |
2 | * class.c - basic device class management |
||
3 | * |
||
4 | * Copyright (c) 2002-3 Patrick Mochel |
||
5 | * Copyright (c) 2002-3 Open Source Development Labs |
||
6 | * Copyright (c) 2003 Greg Kroah-Hartman |
||
7 | * Copyright (c) 2003 IBM Corp. |
||
8 | * |
||
9 | * This file is released under the GPLv2 |
||
10 | * |
||
11 | */ |
||
12 | |||
13 | #undef DEBUG |
||
14 | |||
15 | #include <linuxcomp.h> |
||
16 | |||
17 | #include <linux/device.h> |
||
18 | #include <linux/module.h> |
||
19 | #include <linux/init.h> |
||
20 | #include <linux/string.h> |
||
21 | #include "base.h" |
||
22 | |||
23 | #define to_class_attr(_attr) container_of(_attr,struct class_attribute,attr) |
||
24 | #define to_class(obj) container_of(obj,struct class,subsys.kset.kobj) |
||
25 | |||
26 | static ssize_t |
||
27 | class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) |
||
28 | { |
||
29 | struct class_attribute * class_attr = to_class_attr(attr); |
||
30 | struct class * dc = to_class(kobj); |
||
31 | ssize_t ret = 0; |
||
32 | |||
33 | if (class_attr->show) |
||
34 | ret = class_attr->show(dc,buf); |
||
35 | return ret; |
||
36 | } |
||
37 | |||
38 | static ssize_t |
||
39 | class_attr_store(struct kobject * kobj, struct attribute * attr, |
||
40 | const char * buf, size_t count) |
||
41 | { |
||
42 | struct class_attribute * class_attr = to_class_attr(attr); |
||
43 | struct class * dc = to_class(kobj); |
||
44 | ssize_t ret = 0; |
||
45 | |||
46 | if (class_attr->store) |
||
47 | ret = class_attr->store(dc,buf,count); |
||
48 | return ret; |
||
49 | } |
||
50 | |||
51 | static struct sysfs_ops class_sysfs_ops = { |
||
52 | .show = class_attr_show, |
||
53 | .store = class_attr_store, |
||
54 | }; |
||
55 | |||
56 | static struct kobj_type ktype_class = { |
||
57 | .sysfs_ops = &class_sysfs_ops, |
||
58 | }; |
||
59 | |||
60 | /* Hotplug events for classes go to the class_obj subsys */ |
||
61 | static decl_subsys(class,&ktype_class,NULL); |
||
62 | |||
63 | |||
64 | int class_create_file(struct class * cls, const struct class_attribute * attr) |
||
65 | { |
||
66 | int error; |
||
67 | if (cls) { |
||
68 | error = 0;//sysfs_create_file(&cls->subsys.kset.kobj,&attr->attr); |
||
69 | } else |
||
70 | error = -EINVAL; |
||
71 | return error; |
||
72 | } |
||
73 | |||
74 | void class_remove_file(struct class * cls, const struct class_attribute * attr) |
||
75 | { |
||
76 | //if (cls) |
||
77 | // sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr); |
||
78 | } |
||
79 | |||
80 | struct class * class_get(struct class * cls) |
||
81 | { |
||
82 | if (cls) |
||
83 | return container_of(subsys_get(&cls->subsys),struct class,subsys); |
||
84 | return NULL; |
||
85 | } |
||
86 | |||
87 | void class_put(struct class * cls) |
||
88 | { |
||
89 | subsys_put(&cls->subsys); |
||
90 | } |
||
91 | |||
92 | int class_register(struct class * cls) |
||
93 | { |
||
94 | pr_debug("device class '%s': registering\n",cls->name); |
||
95 | |||
96 | INIT_LIST_HEAD(&cls->children); |
||
97 | INIT_LIST_HEAD(&cls->interfaces); |
||
98 | kobject_set_name(&cls->subsys.kset.kobj,cls->name); |
||
99 | subsys_set_kset(cls,class_subsys); |
||
100 | subsystem_register(&cls->subsys); |
||
101 | |||
102 | return 0; |
||
103 | } |
||
104 | |||
105 | void class_unregister(struct class * cls) |
||
106 | { |
||
107 | pr_debug("device class '%s': unregistering\n",cls->name); |
||
108 | subsystem_unregister(&cls->subsys); |
||
109 | } |
||
110 | |||
111 | /* Class Device Stuff */ |
||
112 | |||
113 | int class_device_create_file(struct class_device * class_dev, |
||
114 | const struct class_device_attribute * attr) |
||
115 | { |
||
116 | int error = -EINVAL; |
||
117 | if (class_dev) |
||
118 | error = 0;//sysfs_create_file(&class_dev->kobj, &attr->attr); |
||
119 | return error; |
||
120 | } |
||
121 | |||
122 | void class_device_remove_file(struct class_device * class_dev, |
||
123 | const struct class_device_attribute * attr) |
||
124 | { |
||
125 | //if (class_dev) |
||
126 | // sysfs_remove_file(&class_dev->kobj, &attr->attr); |
||
127 | } |
||
128 | |||
129 | static int class_device_dev_link(struct class_device * class_dev) |
||
130 | { |
||
131 | //if (class_dev->dev) |
||
132 | // return sysfs_create_link(&class_dev->kobj, |
||
133 | // &class_dev->dev->kobj, "device"); |
||
134 | return 0; |
||
135 | } |
||
136 | |||
137 | static void class_device_dev_unlink(struct class_device * class_dev) |
||
138 | { |
||
139 | //if (class_dev->dev) |
||
140 | // sysfs_remove_link(&class_dev->kobj, "device"); |
||
141 | } |
||
142 | |||
143 | static int class_device_driver_link(struct class_device * class_dev) |
||
144 | { |
||
145 | //if ((class_dev->dev) && (class_dev->dev->driver)) |
||
146 | // return sysfs_create_link(&class_dev->kobj, |
||
147 | // &class_dev->dev->driver->kobj, "driver"); |
||
148 | return 0; |
||
149 | } |
||
150 | |||
151 | static void class_device_driver_unlink(struct class_device * class_dev) |
||
152 | { |
||
153 | //if ((class_dev->dev) && (class_dev->dev->driver)) |
||
154 | // sysfs_remove_link(&class_dev->kobj, "driver"); |
||
155 | } |
||
156 | |||
157 | |||
158 | static ssize_t |
||
159 | class_device_attr_show(struct kobject * kobj, struct attribute * attr, |
||
160 | char * buf) |
||
161 | { |
||
162 | struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); |
||
163 | struct class_device * cd = to_class_dev(kobj); |
||
164 | ssize_t ret = 0; |
||
165 | |||
166 | if (class_dev_attr->show) |
||
167 | ret = class_dev_attr->show(cd,buf); |
||
168 | return ret; |
||
169 | } |
||
170 | |||
171 | static ssize_t |
||
172 | class_device_attr_store(struct kobject * kobj, struct attribute * attr, |
||
173 | const char * buf, size_t count) |
||
174 | { |
||
175 | struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); |
||
176 | struct class_device * cd = to_class_dev(kobj); |
||
177 | ssize_t ret = 0; |
||
178 | |||
179 | if (class_dev_attr->store) |
||
180 | ret = class_dev_attr->store(cd,buf,count); |
||
181 | return ret; |
||
182 | } |
||
183 | |||
184 | static struct sysfs_ops class_dev_sysfs_ops = { |
||
185 | .show = class_device_attr_show, |
||
186 | .store = class_device_attr_store, |
||
187 | }; |
||
188 | |||
189 | static void class_dev_release(struct kobject * kobj) |
||
190 | { |
||
191 | struct class_device *cd = to_class_dev(kobj); |
||
192 | struct class * cls = cd->class; |
||
193 | |||
194 | pr_debug("device class '%s': release.\n",cd->class_id); |
||
195 | |||
196 | if (cls->release) |
||
197 | cls->release(cd); |
||
198 | else { |
||
199 | printk(KERN_ERR "Device class '%s' does not have a release() function, " |
||
200 | "it is broken and must be fixed.\n", |
||
201 | cd->class_id); |
||
202 | WARN_ON(1); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | static struct kobj_type ktype_class_device = { |
||
207 | .sysfs_ops = &class_dev_sysfs_ops, |
||
208 | .release = class_dev_release, |
||
209 | }; |
||
210 | |||
211 | static int class_hotplug_filter(struct kset *kset, struct kobject *kobj) |
||
212 | { |
||
213 | struct kobj_type *ktype = get_ktype(kobj); |
||
214 | |||
215 | if (ktype == &ktype_class_device) { |
||
216 | struct class_device *class_dev = to_class_dev(kobj); |
||
217 | if (class_dev->class) |
||
218 | return 1; |
||
219 | } |
||
220 | return 0; |
||
221 | } |
||
222 | |||
223 | static char *class_hotplug_name(struct kset *kset, struct kobject *kobj) |
||
224 | { |
||
225 | struct class_device *class_dev = to_class_dev(kobj); |
||
226 | |||
227 | return class_dev->class->name; |
||
228 | } |
||
229 | |||
230 | static int class_hotplug(struct kset *kset, struct kobject *kobj, char **envp, |
||
231 | int num_envp, char *buffer, int buffer_size) |
||
232 | { |
||
233 | struct class_device *class_dev = to_class_dev(kobj); |
||
234 | int retval = 0; |
||
235 | |||
236 | pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); |
||
237 | if (class_dev->class->hotplug) { |
||
238 | /* have the bus specific function add its stuff */ |
||
239 | retval = class_dev->class->hotplug (class_dev, envp, num_envp, |
||
240 | buffer, buffer_size); |
||
241 | if (retval) { |
||
242 | pr_debug ("%s - hotplug() returned %d\n", |
||
243 | __FUNCTION__, retval); |
||
244 | } |
||
245 | } |
||
246 | |||
247 | return retval; |
||
248 | } |
||
249 | |||
250 | static struct kset_hotplug_ops class_hotplug_ops = { |
||
251 | .filter = class_hotplug_filter, |
||
252 | .name = class_hotplug_name, |
||
253 | .hotplug = class_hotplug, |
||
254 | }; |
||
255 | |||
256 | static decl_subsys(class_obj, &ktype_class_device, &class_hotplug_ops); |
||
257 | |||
258 | void class_device_initialize(struct class_device *class_dev) |
||
259 | { |
||
260 | kobj_set_kset_s(class_dev, class_obj_subsys); |
||
261 | kobject_init(&class_dev->kobj); |
||
262 | INIT_LIST_HEAD(&class_dev->node); |
||
263 | } |
||
264 | |||
265 | int class_device_add(struct class_device *class_dev) |
||
266 | { |
||
267 | struct class * parent; |
||
268 | struct class_interface * class_intf; |
||
269 | struct list_head * entry; |
||
270 | int error; |
||
271 | |||
272 | class_dev = class_device_get(class_dev); |
||
273 | if (!class_dev || !strlen(class_dev->class_id)) |
||
274 | return -EINVAL; |
||
275 | |||
276 | parent = class_get(class_dev->class); |
||
277 | |||
278 | pr_debug("CLASS: registering class device: ID = '%s'\n", |
||
279 | class_dev->class_id); |
||
280 | |||
281 | /* first, register with generic layer. */ |
||
282 | kobject_set_name(&class_dev->kobj, class_dev->class_id); |
||
283 | if (parent) |
||
284 | class_dev->kobj.parent = &parent->subsys.kset.kobj; |
||
285 | |||
286 | if ((error = kobject_add(&class_dev->kobj))) |
||
287 | goto register_done; |
||
288 | |||
289 | /* now take care of our own registration */ |
||
290 | if (parent) { |
||
291 | //down_write(&parent->subsys.rwsem); |
||
292 | list_add_tail(&class_dev->node, &parent->children); |
||
293 | list_for_each(entry, &parent->interfaces) { |
||
294 | class_intf = container_of(entry, struct class_interface, node); |
||
295 | if (class_intf->add) |
||
296 | class_intf->add(class_dev); |
||
297 | } |
||
298 | //up_write(&parent->subsys.rwsem); |
||
299 | } |
||
300 | |||
301 | class_device_dev_link(class_dev); |
||
302 | class_device_driver_link(class_dev); |
||
303 | |||
304 | register_done: |
||
305 | if (error && parent) |
||
306 | class_put(parent); |
||
307 | class_device_put(class_dev); |
||
308 | return error; |
||
309 | } |
||
310 | |||
311 | int class_device_register(struct class_device *class_dev) |
||
312 | { |
||
313 | class_device_initialize(class_dev); |
||
314 | return class_device_add(class_dev); |
||
315 | } |
||
316 | |||
317 | void class_device_del(struct class_device *class_dev) |
||
318 | { |
||
319 | struct class * parent = class_dev->class; |
||
320 | struct class_interface * class_intf; |
||
321 | struct list_head * entry; |
||
322 | |||
323 | if (parent) { |
||
324 | //down_write(&parent->subsys.rwsem); |
||
325 | list_del_init(&class_dev->node); |
||
326 | list_for_each(entry, &parent->interfaces) { |
||
327 | class_intf = container_of(entry, struct class_interface, node); |
||
328 | if (class_intf->remove) |
||
329 | class_intf->remove(class_dev); |
||
330 | } |
||
331 | //up_write(&parent->subsys.rwsem); |
||
332 | } |
||
333 | |||
334 | class_device_dev_unlink(class_dev); |
||
335 | class_device_driver_unlink(class_dev); |
||
336 | |||
337 | kobject_del(&class_dev->kobj); |
||
338 | |||
339 | if (parent) |
||
340 | class_put(parent); |
||
341 | } |
||
342 | |||
343 | void class_device_unregister(struct class_device *class_dev) |
||
344 | { |
||
345 | pr_debug("CLASS: Unregistering class device. ID = '%s'\n", |
||
346 | class_dev->class_id); |
||
347 | class_device_del(class_dev); |
||
348 | class_device_put(class_dev); |
||
349 | } |
||
350 | |||
351 | int class_device_rename(struct class_device *class_dev, char *new_name) |
||
352 | { |
||
353 | class_dev = class_device_get(class_dev); |
||
354 | if (!class_dev) |
||
355 | return -EINVAL; |
||
356 | |||
357 | pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id, |
||
358 | new_name); |
||
359 | |||
360 | strncpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); |
||
361 | |||
362 | kobject_rename(&class_dev->kobj, new_name); |
||
363 | |||
364 | class_device_put(class_dev); |
||
365 | |||
366 | return 0; |
||
367 | } |
||
368 | |||
369 | struct class_device * class_device_get(struct class_device *class_dev) |
||
370 | { |
||
371 | if (class_dev) |
||
372 | return to_class_dev(kobject_get(&class_dev->kobj)); |
||
373 | return NULL; |
||
374 | } |
||
375 | |||
376 | void class_device_put(struct class_device *class_dev) |
||
377 | { |
||
378 | kobject_put(&class_dev->kobj); |
||
379 | } |
||
380 | |||
381 | |||
382 | int class_interface_register(struct class_interface *class_intf) |
||
383 | { |
||
384 | struct class * parent; |
||
385 | struct class_device * class_dev; |
||
386 | struct list_head * entry; |
||
387 | |||
388 | if (!class_intf || !class_intf->class) |
||
389 | return -ENODEV; |
||
390 | |||
391 | parent = class_get(class_intf->class); |
||
392 | if (!parent) |
||
393 | return -EINVAL; |
||
394 | |||
395 | //down_write(&parent->subsys.rwsem); |
||
396 | list_add_tail(&class_intf->node, &parent->interfaces); |
||
397 | |||
398 | if (class_intf->add) { |
||
399 | list_for_each(entry, &parent->children) { |
||
400 | class_dev = container_of(entry, struct class_device, node); |
||
401 | class_intf->add(class_dev); |
||
402 | } |
||
403 | } |
||
404 | //up_write(&parent->subsys.rwsem); |
||
405 | |||
406 | return 0; |
||
407 | } |
||
408 | |||
409 | void class_interface_unregister(struct class_interface *class_intf) |
||
410 | { |
||
411 | struct class * parent = class_intf->class; |
||
412 | struct list_head * entry; |
||
413 | |||
414 | if (!parent) |
||
415 | return; |
||
416 | |||
417 | //down_write(&parent->subsys.rwsem); |
||
418 | list_del_init(&class_intf->node); |
||
419 | |||
420 | if (class_intf->remove) { |
||
421 | list_for_each(entry, &parent->children) { |
||
422 | struct class_device *class_dev = container_of(entry, struct class_device, node); |
||
423 | class_intf->remove(class_dev); |
||
424 | } |
||
425 | } |
||
426 | //up_write(&parent->subsys.rwsem); |
||
427 | |||
428 | class_put(parent); |
||
429 | } |
||
430 | |||
431 | |||
432 | |||
433 | int __init classes_init(void) |
||
434 | { |
||
435 | int retval; |
||
436 | |||
437 | retval = subsystem_register(&class_subsys); |
||
438 | if (retval) |
||
439 | return retval; |
||
440 | |||
441 | /* ick, this is ugly, the things we go through to keep from showing up |
||
442 | * in sysfs... */ |
||
443 | subsystem_init(&class_obj_subsys); |
||
444 | if (!class_obj_subsys.kset.subsys) |
||
445 | class_obj_subsys.kset.subsys = &class_obj_subsys; |
||
446 | return 0; |
||
447 | } |
||
448 | |||
449 | EXPORT_SYMBOL(class_create_file); |
||
450 | EXPORT_SYMBOL(class_remove_file); |
||
451 | EXPORT_SYMBOL(class_register); |
||
452 | EXPORT_SYMBOL(class_unregister); |
||
453 | EXPORT_SYMBOL(class_get); |
||
454 | EXPORT_SYMBOL(class_put); |
||
455 | |||
456 | EXPORT_SYMBOL(class_device_register); |
||
457 | EXPORT_SYMBOL(class_device_unregister); |
||
458 | EXPORT_SYMBOL(class_device_initialize); |
||
459 | EXPORT_SYMBOL(class_device_add); |
||
460 | EXPORT_SYMBOL(class_device_del); |
||
461 | EXPORT_SYMBOL(class_device_get); |
||
462 | EXPORT_SYMBOL(class_device_put); |
||
463 | EXPORT_SYMBOL(class_device_create_file); |
||
464 | EXPORT_SYMBOL(class_device_remove_file); |
||
465 | |||
466 | EXPORT_SYMBOL(class_interface_register); |
||
467 | EXPORT_SYMBOL(class_interface_unregister); |