diff options
Diffstat (limited to 'drivers/base/class.c')
-rw-r--r-- | drivers/base/class.c | 145 |
1 files changed, 134 insertions, 11 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index 344b8cd73901..a3b006b6f2ba 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/string.h> | 17 | #include <linux/string.h> |
18 | #include <linux/kdev_t.h> | 18 | #include <linux/kdev_t.h> |
19 | #include <linux/err.h> | ||
19 | #include "base.h" | 20 | #include "base.h" |
20 | 21 | ||
21 | #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) | 22 | #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) |
@@ -162,6 +163,51 @@ void class_unregister(struct class * cls) | |||
162 | subsystem_unregister(&cls->subsys); | 163 | subsystem_unregister(&cls->subsys); |
163 | } | 164 | } |
164 | 165 | ||
166 | static void class_create_release(struct class *cls) | ||
167 | { | ||
168 | kfree(cls); | ||
169 | } | ||
170 | |||
171 | static void class_device_create_release(struct class_device *class_dev) | ||
172 | { | ||
173 | kfree(class_dev); | ||
174 | } | ||
175 | |||
176 | struct class *class_create(struct module *owner, char *name) | ||
177 | { | ||
178 | struct class *cls; | ||
179 | int retval; | ||
180 | |||
181 | cls = kmalloc(sizeof(struct class), GFP_KERNEL); | ||
182 | if (!cls) { | ||
183 | retval = -ENOMEM; | ||
184 | goto error; | ||
185 | } | ||
186 | memset(cls, 0x00, sizeof(struct class)); | ||
187 | |||
188 | cls->name = name; | ||
189 | cls->owner = owner; | ||
190 | cls->class_release = class_create_release; | ||
191 | cls->release = class_device_create_release; | ||
192 | |||
193 | retval = class_register(cls); | ||
194 | if (retval) | ||
195 | goto error; | ||
196 | |||
197 | return cls; | ||
198 | |||
199 | error: | ||
200 | kfree(cls); | ||
201 | return ERR_PTR(retval); | ||
202 | } | ||
203 | |||
204 | void class_destroy(struct class *cls) | ||
205 | { | ||
206 | if ((cls == NULL) || (IS_ERR(cls))) | ||
207 | return; | ||
208 | |||
209 | class_unregister(cls); | ||
210 | } | ||
165 | 211 | ||
166 | /* Class Device Stuff */ | 212 | /* Class Device Stuff */ |
167 | 213 | ||
@@ -375,7 +421,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) | |||
375 | { | 421 | { |
376 | return print_dev_t(buf, class_dev->devt); | 422 | return print_dev_t(buf, class_dev->devt); |
377 | } | 423 | } |
378 | static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); | ||
379 | 424 | ||
380 | void class_device_initialize(struct class_device *class_dev) | 425 | void class_device_initialize(struct class_device *class_dev) |
381 | { | 426 | { |
@@ -412,7 +457,31 @@ int class_device_add(struct class_device *class_dev) | |||
412 | if ((error = kobject_add(&class_dev->kobj))) | 457 | if ((error = kobject_add(&class_dev->kobj))) |
413 | goto register_done; | 458 | goto register_done; |
414 | 459 | ||
415 | /* now take care of our own registration */ | 460 | /* add the needed attributes to this device */ |
461 | if (MAJOR(class_dev->devt)) { | ||
462 | struct class_device_attribute *attr; | ||
463 | attr = kmalloc(sizeof(*attr), GFP_KERNEL); | ||
464 | if (!attr) { | ||
465 | error = -ENOMEM; | ||
466 | kobject_del(&class_dev->kobj); | ||
467 | goto register_done; | ||
468 | } | ||
469 | memset(attr, sizeof(*attr), 0x00); | ||
470 | attr->attr.name = "dev"; | ||
471 | attr->attr.mode = S_IRUGO; | ||
472 | attr->attr.owner = parent->owner; | ||
473 | attr->show = show_dev; | ||
474 | attr->store = NULL; | ||
475 | class_device_create_file(class_dev, attr); | ||
476 | class_dev->devt_attr = attr; | ||
477 | } | ||
478 | |||
479 | class_device_add_attrs(class_dev); | ||
480 | if (class_dev->dev) | ||
481 | sysfs_create_link(&class_dev->kobj, | ||
482 | &class_dev->dev->kobj, "device"); | ||
483 | |||
484 | /* notify any interfaces this device is now here */ | ||
416 | if (parent) { | 485 | if (parent) { |
417 | down(&parent->sem); | 486 | down(&parent->sem); |
418 | list_add_tail(&class_dev->node, &parent->children); | 487 | list_add_tail(&class_dev->node, &parent->children); |
@@ -421,16 +490,8 @@ int class_device_add(struct class_device *class_dev) | |||
421 | class_intf->add(class_dev); | 490 | class_intf->add(class_dev); |
422 | up(&parent->sem); | 491 | up(&parent->sem); |
423 | } | 492 | } |
424 | |||
425 | if (MAJOR(class_dev->devt)) | ||
426 | class_device_create_file(class_dev, &class_device_attr_dev); | ||
427 | |||
428 | class_device_add_attrs(class_dev); | ||
429 | if (class_dev->dev) | ||
430 | sysfs_create_link(&class_dev->kobj, | ||
431 | &class_dev->dev->kobj, "device"); | ||
432 | |||
433 | kobject_hotplug(&class_dev->kobj, KOBJ_ADD); | 493 | kobject_hotplug(&class_dev->kobj, KOBJ_ADD); |
494 | |||
434 | register_done: | 495 | register_done: |
435 | if (error && parent) | 496 | if (error && parent) |
436 | class_put(parent); | 497 | class_put(parent); |
@@ -444,6 +505,41 @@ int class_device_register(struct class_device *class_dev) | |||
444 | return class_device_add(class_dev); | 505 | return class_device_add(class_dev); |
445 | } | 506 | } |
446 | 507 | ||
508 | struct class_device *class_device_create(struct class *cls, dev_t devt, | ||
509 | struct device *device, char *fmt, ...) | ||
510 | { | ||
511 | va_list args; | ||
512 | struct class_device *class_dev = NULL; | ||
513 | int retval = -ENODEV; | ||
514 | |||
515 | if (cls == NULL || IS_ERR(cls)) | ||
516 | goto error; | ||
517 | |||
518 | class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); | ||
519 | if (!class_dev) { | ||
520 | retval = -ENOMEM; | ||
521 | goto error; | ||
522 | } | ||
523 | memset(class_dev, 0x00, sizeof(struct class_device)); | ||
524 | |||
525 | class_dev->devt = devt; | ||
526 | class_dev->dev = device; | ||
527 | class_dev->class = cls; | ||
528 | |||
529 | va_start(args, fmt); | ||
530 | vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); | ||
531 | va_end(args); | ||
532 | retval = class_device_register(class_dev); | ||
533 | if (retval) | ||
534 | goto error; | ||
535 | |||
536 | return class_dev; | ||
537 | |||
538 | error: | ||
539 | kfree(class_dev); | ||
540 | return ERR_PTR(retval); | ||
541 | } | ||
542 | |||
447 | void class_device_del(struct class_device *class_dev) | 543 | void class_device_del(struct class_device *class_dev) |
448 | { | 544 | { |
449 | struct class * parent = class_dev->class; | 545 | struct class * parent = class_dev->class; |
@@ -460,6 +556,11 @@ void class_device_del(struct class_device *class_dev) | |||
460 | 556 | ||
461 | if (class_dev->dev) | 557 | if (class_dev->dev) |
462 | sysfs_remove_link(&class_dev->kobj, "device"); | 558 | sysfs_remove_link(&class_dev->kobj, "device"); |
559 | if (class_dev->devt_attr) { | ||
560 | class_device_remove_file(class_dev, class_dev->devt_attr); | ||
561 | kfree(class_dev->devt_attr); | ||
562 | class_dev->devt_attr = NULL; | ||
563 | } | ||
463 | class_device_remove_attrs(class_dev); | 564 | class_device_remove_attrs(class_dev); |
464 | 565 | ||
465 | kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); | 566 | kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); |
@@ -477,6 +578,24 @@ void class_device_unregister(struct class_device *class_dev) | |||
477 | class_device_put(class_dev); | 578 | class_device_put(class_dev); |
478 | } | 579 | } |
479 | 580 | ||
581 | void class_device_destroy(struct class *cls, dev_t devt) | ||
582 | { | ||
583 | struct class_device *class_dev = NULL; | ||
584 | struct class_device *class_dev_tmp; | ||
585 | |||
586 | down(&cls->sem); | ||
587 | list_for_each_entry(class_dev_tmp, &cls->children, node) { | ||
588 | if (class_dev_tmp->devt == devt) { | ||
589 | class_dev = class_dev_tmp; | ||
590 | break; | ||
591 | } | ||
592 | } | ||
593 | up(&cls->sem); | ||
594 | |||
595 | if (class_dev) | ||
596 | class_device_unregister(class_dev); | ||
597 | } | ||
598 | |||
480 | int class_device_rename(struct class_device *class_dev, char *new_name) | 599 | int class_device_rename(struct class_device *class_dev, char *new_name) |
481 | { | 600 | { |
482 | int error = 0; | 601 | int error = 0; |
@@ -576,6 +695,8 @@ EXPORT_SYMBOL_GPL(class_register); | |||
576 | EXPORT_SYMBOL_GPL(class_unregister); | 695 | EXPORT_SYMBOL_GPL(class_unregister); |
577 | EXPORT_SYMBOL_GPL(class_get); | 696 | EXPORT_SYMBOL_GPL(class_get); |
578 | EXPORT_SYMBOL_GPL(class_put); | 697 | EXPORT_SYMBOL_GPL(class_put); |
698 | EXPORT_SYMBOL_GPL(class_create); | ||
699 | EXPORT_SYMBOL_GPL(class_destroy); | ||
579 | 700 | ||
580 | EXPORT_SYMBOL_GPL(class_device_register); | 701 | EXPORT_SYMBOL_GPL(class_device_register); |
581 | EXPORT_SYMBOL_GPL(class_device_unregister); | 702 | EXPORT_SYMBOL_GPL(class_device_unregister); |
@@ -584,6 +705,8 @@ EXPORT_SYMBOL_GPL(class_device_add); | |||
584 | EXPORT_SYMBOL_GPL(class_device_del); | 705 | EXPORT_SYMBOL_GPL(class_device_del); |
585 | EXPORT_SYMBOL_GPL(class_device_get); | 706 | EXPORT_SYMBOL_GPL(class_device_get); |
586 | EXPORT_SYMBOL_GPL(class_device_put); | 707 | EXPORT_SYMBOL_GPL(class_device_put); |
708 | EXPORT_SYMBOL_GPL(class_device_create); | ||
709 | EXPORT_SYMBOL_GPL(class_device_destroy); | ||
587 | EXPORT_SYMBOL_GPL(class_device_create_file); | 710 | EXPORT_SYMBOL_GPL(class_device_create_file); |
588 | EXPORT_SYMBOL_GPL(class_device_remove_file); | 711 | EXPORT_SYMBOL_GPL(class_device_remove_file); |
589 | EXPORT_SYMBOL_GPL(class_device_create_bin_file); | 712 | EXPORT_SYMBOL_GPL(class_device_create_bin_file); |