diff options
Diffstat (limited to 'drivers/base/class.c')
| -rw-r--r-- | drivers/base/class.c | 194 |
1 files changed, 180 insertions, 14 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index d2a2f8f2b4ed..479c12570881 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) |
| @@ -26,7 +27,7 @@ class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) | |||
| 26 | { | 27 | { |
| 27 | struct class_attribute * class_attr = to_class_attr(attr); | 28 | struct class_attribute * class_attr = to_class_attr(attr); |
| 28 | struct class * dc = to_class(kobj); | 29 | struct class * dc = to_class(kobj); |
| 29 | ssize_t ret = 0; | 30 | ssize_t ret = -EIO; |
| 30 | 31 | ||
| 31 | if (class_attr->show) | 32 | if (class_attr->show) |
| 32 | ret = class_attr->show(dc, buf); | 33 | ret = class_attr->show(dc, buf); |
| @@ -39,7 +40,7 @@ class_attr_store(struct kobject * kobj, struct attribute * attr, | |||
| 39 | { | 40 | { |
| 40 | struct class_attribute * class_attr = to_class_attr(attr); | 41 | struct class_attribute * class_attr = to_class_attr(attr); |
| 41 | struct class * dc = to_class(kobj); | 42 | struct class * dc = to_class(kobj); |
| 42 | ssize_t ret = 0; | 43 | ssize_t ret = -EIO; |
| 43 | 44 | ||
| 44 | if (class_attr->store) | 45 | if (class_attr->store) |
| 45 | ret = class_attr->store(dc, buf, count); | 46 | ret = class_attr->store(dc, buf, count); |
| @@ -162,6 +163,69 @@ 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 | /** | ||
| 177 | * class_create - create a struct class structure | ||
| 178 | * @owner: pointer to the module that is to "own" this struct class | ||
| 179 | * @name: pointer to a string for the name of this class. | ||
| 180 | * | ||
| 181 | * This is used to create a struct class pointer that can then be used | ||
| 182 | * in calls to class_device_create(). | ||
| 183 | * | ||
| 184 | * Note, the pointer created here is to be destroyed when finished by | ||
| 185 | * making a call to class_destroy(). | ||
| 186 | */ | ||
| 187 | struct class *class_create(struct module *owner, char *name) | ||
| 188 | { | ||
| 189 | struct class *cls; | ||
| 190 | int retval; | ||
| 191 | |||
| 192 | cls = kmalloc(sizeof(struct class), GFP_KERNEL); | ||
| 193 | if (!cls) { | ||
| 194 | retval = -ENOMEM; | ||
| 195 | goto error; | ||
| 196 | } | ||
| 197 | memset(cls, 0x00, sizeof(struct class)); | ||
| 198 | |||
| 199 | cls->name = name; | ||
| 200 | cls->owner = owner; | ||
| 201 | cls->class_release = class_create_release; | ||
| 202 | cls->release = class_device_create_release; | ||
| 203 | |||
| 204 | retval = class_register(cls); | ||
| 205 | if (retval) | ||
| 206 | goto error; | ||
| 207 | |||
| 208 | return cls; | ||
| 209 | |||
| 210 | error: | ||
| 211 | kfree(cls); | ||
| 212 | return ERR_PTR(retval); | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * class_destroy - destroys a struct class structure | ||
| 217 | * @cs: pointer to the struct class that is to be destroyed | ||
| 218 | * | ||
| 219 | * Note, the pointer to be destroyed must have been created with a call | ||
| 220 | * to class_create(). | ||
| 221 | */ | ||
| 222 | void class_destroy(struct class *cls) | ||
| 223 | { | ||
| 224 | if ((cls == NULL) || (IS_ERR(cls))) | ||
| 225 | return; | ||
| 226 | |||
| 227 | class_unregister(cls); | ||
| 228 | } | ||
| 165 | 229 | ||
| 166 | /* Class Device Stuff */ | 230 | /* Class Device Stuff */ |
| 167 | 231 | ||
| @@ -262,7 +326,7 @@ static int class_hotplug_filter(struct kset *kset, struct kobject *kobj) | |||
| 262 | return 0; | 326 | return 0; |
| 263 | } | 327 | } |
| 264 | 328 | ||
| 265 | static char *class_hotplug_name(struct kset *kset, struct kobject *kobj) | 329 | static const char *class_hotplug_name(struct kset *kset, struct kobject *kobj) |
| 266 | { | 330 | { |
| 267 | struct class_device *class_dev = to_class_dev(kobj); | 331 | struct class_device *class_dev = to_class_dev(kobj); |
| 268 | 332 | ||
| @@ -375,7 +439,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) | |||
| 375 | { | 439 | { |
| 376 | return print_dev_t(buf, class_dev->devt); | 440 | return print_dev_t(buf, class_dev->devt); |
| 377 | } | 441 | } |
| 378 | static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); | ||
| 379 | 442 | ||
| 380 | void class_device_initialize(struct class_device *class_dev) | 443 | void class_device_initialize(struct class_device *class_dev) |
| 381 | { | 444 | { |
| @@ -412,7 +475,31 @@ int class_device_add(struct class_device *class_dev) | |||
| 412 | if ((error = kobject_add(&class_dev->kobj))) | 475 | if ((error = kobject_add(&class_dev->kobj))) |
| 413 | goto register_done; | 476 | goto register_done; |
| 414 | 477 | ||
| 415 | /* now take care of our own registration */ | 478 | /* add the needed attributes to this device */ |
| 479 | if (MAJOR(class_dev->devt)) { | ||
| 480 | struct class_device_attribute *attr; | ||
| 481 | attr = kmalloc(sizeof(*attr), GFP_KERNEL); | ||
| 482 | if (!attr) { | ||
| 483 | error = -ENOMEM; | ||
| 484 | kobject_del(&class_dev->kobj); | ||
| 485 | goto register_done; | ||
| 486 | } | ||
| 487 | memset(attr, sizeof(*attr), 0x00); | ||
| 488 | attr->attr.name = "dev"; | ||
| 489 | attr->attr.mode = S_IRUGO; | ||
| 490 | attr->attr.owner = parent->owner; | ||
| 491 | attr->show = show_dev; | ||
| 492 | attr->store = NULL; | ||
| 493 | class_device_create_file(class_dev, attr); | ||
| 494 | class_dev->devt_attr = attr; | ||
| 495 | } | ||
| 496 | |||
| 497 | class_device_add_attrs(class_dev); | ||
| 498 | if (class_dev->dev) | ||
| 499 | sysfs_create_link(&class_dev->kobj, | ||
| 500 | &class_dev->dev->kobj, "device"); | ||
| 501 | |||
| 502 | /* notify any interfaces this device is now here */ | ||
| 416 | if (parent) { | 503 | if (parent) { |
| 417 | down(&parent->sem); | 504 | down(&parent->sem); |
| 418 | list_add_tail(&class_dev->node, &parent->children); | 505 | list_add_tail(&class_dev->node, &parent->children); |
| @@ -421,16 +508,8 @@ int class_device_add(struct class_device *class_dev) | |||
| 421 | class_intf->add(class_dev); | 508 | class_intf->add(class_dev); |
| 422 | up(&parent->sem); | 509 | up(&parent->sem); |
| 423 | } | 510 | } |
| 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); | 511 | kobject_hotplug(&class_dev->kobj, KOBJ_ADD); |
| 512 | |||
| 434 | register_done: | 513 | register_done: |
| 435 | if (error && parent) | 514 | if (error && parent) |
| 436 | class_put(parent); | 515 | class_put(parent); |
| @@ -444,6 +523,58 @@ int class_device_register(struct class_device *class_dev) | |||
| 444 | return class_device_add(class_dev); | 523 | return class_device_add(class_dev); |
| 445 | } | 524 | } |
| 446 | 525 | ||
| 526 | /** | ||
| 527 | * class_device_create - creates a class device and registers it with sysfs | ||
| 528 | * @cs: pointer to the struct class that this device should be registered to. | ||
| 529 | * @dev: the dev_t for the char device to be added. | ||
| 530 | * @device: a pointer to a struct device that is assiociated with this class device. | ||
| 531 | * @fmt: string for the class device's name | ||
| 532 | * | ||
| 533 | * This function can be used by char device classes. A struct | ||
| 534 | * class_device will be created in sysfs, registered to the specified | ||
| 535 | * class. A "dev" file will be created, showing the dev_t for the | ||
| 536 | * device. The pointer to the struct class_device will be returned from | ||
| 537 | * the call. Any further sysfs files that might be required can be | ||
| 538 | * created using this pointer. | ||
| 539 | * | ||
| 540 | * Note: the struct class passed to this function must have previously | ||
| 541 | * been created with a call to class_create(). | ||
| 542 | */ | ||
| 543 | struct class_device *class_device_create(struct class *cls, dev_t devt, | ||
| 544 | struct device *device, char *fmt, ...) | ||
| 545 | { | ||
| 546 | va_list args; | ||
| 547 | struct class_device *class_dev = NULL; | ||
| 548 | int retval = -ENODEV; | ||
| 549 | |||
| 550 | if (cls == NULL || IS_ERR(cls)) | ||
| 551 | goto error; | ||
| 552 | |||
| 553 | class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); | ||
| 554 | if (!class_dev) { | ||
| 555 | retval = -ENOMEM; | ||
| 556 | goto error; | ||
| 557 | } | ||
| 558 | memset(class_dev, 0x00, sizeof(struct class_device)); | ||
| 559 | |||
| 560 | class_dev->devt = devt; | ||
| 561 | class_dev->dev = device; | ||
| 562 | class_dev->class = cls; | ||
| 563 | |||
| 564 | va_start(args, fmt); | ||
| 565 | vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); | ||
| 566 | va_end(args); | ||
| 567 | retval = class_device_register(class_dev); | ||
| 568 | if (retval) | ||
| 569 | goto error; | ||
| 570 | |||
| 571 | return class_dev; | ||
| 572 | |||
| 573 | error: | ||
| 574 | kfree(class_dev); | ||
| 575 | return ERR_PTR(retval); | ||
| 576 | } | ||
| 577 | |||
| 447 | void class_device_del(struct class_device *class_dev) | 578 | void class_device_del(struct class_device *class_dev) |
| 448 | { | 579 | { |
| 449 | struct class * parent = class_dev->class; | 580 | struct class * parent = class_dev->class; |
| @@ -460,6 +591,11 @@ void class_device_del(struct class_device *class_dev) | |||
| 460 | 591 | ||
| 461 | if (class_dev->dev) | 592 | if (class_dev->dev) |
| 462 | sysfs_remove_link(&class_dev->kobj, "device"); | 593 | sysfs_remove_link(&class_dev->kobj, "device"); |
| 594 | if (class_dev->devt_attr) { | ||
| 595 | class_device_remove_file(class_dev, class_dev->devt_attr); | ||
| 596 | kfree(class_dev->devt_attr); | ||
| 597 | class_dev->devt_attr = NULL; | ||
| 598 | } | ||
| 463 | class_device_remove_attrs(class_dev); | 599 | class_device_remove_attrs(class_dev); |
| 464 | 600 | ||
| 465 | kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); | 601 | kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); |
| @@ -477,6 +613,32 @@ void class_device_unregister(struct class_device *class_dev) | |||
| 477 | class_device_put(class_dev); | 613 | class_device_put(class_dev); |
| 478 | } | 614 | } |
| 479 | 615 | ||
| 616 | /** | ||
| 617 | * class_device_destroy - removes a class device that was created with class_device_create() | ||
| 618 | * @cls: the pointer to the struct class that this device was registered * with. | ||
| 619 | * @dev: the dev_t of the device that was previously registered. | ||
| 620 | * | ||
| 621 | * This call unregisters and cleans up a class device that was created with a | ||
| 622 | * call to class_device_create() | ||
| 623 | */ | ||
| 624 | void class_device_destroy(struct class *cls, dev_t devt) | ||
| 625 | { | ||
| 626 | struct class_device *class_dev = NULL; | ||
| 627 | struct class_device *class_dev_tmp; | ||
| 628 | |||
| 629 | down(&cls->sem); | ||
| 630 | list_for_each_entry(class_dev_tmp, &cls->children, node) { | ||
| 631 | if (class_dev_tmp->devt == devt) { | ||
| 632 | class_dev = class_dev_tmp; | ||
| 633 | break; | ||
| 634 | } | ||
| 635 | } | ||
| 636 | up(&cls->sem); | ||
| 637 | |||
| 638 | if (class_dev) | ||
| 639 | class_device_unregister(class_dev); | ||
| 640 | } | ||
| 641 | |||
| 480 | int class_device_rename(struct class_device *class_dev, char *new_name) | 642 | int class_device_rename(struct class_device *class_dev, char *new_name) |
| 481 | { | 643 | { |
| 482 | int error = 0; | 644 | int error = 0; |
| @@ -576,6 +738,8 @@ EXPORT_SYMBOL_GPL(class_register); | |||
| 576 | EXPORT_SYMBOL_GPL(class_unregister); | 738 | EXPORT_SYMBOL_GPL(class_unregister); |
| 577 | EXPORT_SYMBOL_GPL(class_get); | 739 | EXPORT_SYMBOL_GPL(class_get); |
| 578 | EXPORT_SYMBOL_GPL(class_put); | 740 | EXPORT_SYMBOL_GPL(class_put); |
| 741 | EXPORT_SYMBOL_GPL(class_create); | ||
| 742 | EXPORT_SYMBOL_GPL(class_destroy); | ||
| 579 | 743 | ||
| 580 | EXPORT_SYMBOL_GPL(class_device_register); | 744 | EXPORT_SYMBOL_GPL(class_device_register); |
| 581 | EXPORT_SYMBOL_GPL(class_device_unregister); | 745 | EXPORT_SYMBOL_GPL(class_device_unregister); |
| @@ -584,6 +748,8 @@ EXPORT_SYMBOL_GPL(class_device_add); | |||
| 584 | EXPORT_SYMBOL_GPL(class_device_del); | 748 | EXPORT_SYMBOL_GPL(class_device_del); |
| 585 | EXPORT_SYMBOL_GPL(class_device_get); | 749 | EXPORT_SYMBOL_GPL(class_device_get); |
| 586 | EXPORT_SYMBOL_GPL(class_device_put); | 750 | EXPORT_SYMBOL_GPL(class_device_put); |
| 751 | EXPORT_SYMBOL_GPL(class_device_create); | ||
| 752 | EXPORT_SYMBOL_GPL(class_device_destroy); | ||
| 587 | EXPORT_SYMBOL_GPL(class_device_create_file); | 753 | EXPORT_SYMBOL_GPL(class_device_create_file); |
| 588 | EXPORT_SYMBOL_GPL(class_device_remove_file); | 754 | EXPORT_SYMBOL_GPL(class_device_remove_file); |
| 589 | EXPORT_SYMBOL_GPL(class_device_create_bin_file); | 755 | EXPORT_SYMBOL_GPL(class_device_create_bin_file); |
