diff options
-rw-r--r-- | fs/char_dev.c | 86 | ||||
-rw-r--r-- | include/linux/cdev.h | 5 |
2 files changed, 91 insertions, 0 deletions
diff --git a/fs/char_dev.c b/fs/char_dev.c index 44a240c4bb65..fb8507f521b2 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c | |||
@@ -471,6 +471,85 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count) | |||
471 | return 0; | 471 | return 0; |
472 | } | 472 | } |
473 | 473 | ||
474 | /** | ||
475 | * cdev_set_parent() - set the parent kobject for a char device | ||
476 | * @p: the cdev structure | ||
477 | * @kobj: the kobject to take a reference to | ||
478 | * | ||
479 | * cdev_set_parent() sets a parent kobject which will be referenced | ||
480 | * appropriately so the parent is not freed before the cdev. This | ||
481 | * should be called before cdev_add. | ||
482 | */ | ||
483 | void cdev_set_parent(struct cdev *p, struct kobject *kobj) | ||
484 | { | ||
485 | WARN_ON(!kobj->state_initialized); | ||
486 | p->kobj.parent = kobj; | ||
487 | } | ||
488 | |||
489 | /** | ||
490 | * cdev_device_add() - add a char device and it's corresponding | ||
491 | * struct device, linkink | ||
492 | * @dev: the device structure | ||
493 | * @cdev: the cdev structure | ||
494 | * | ||
495 | * cdev_device_add() adds the char device represented by @cdev to the system, | ||
496 | * just as cdev_add does. It then adds @dev to the system using device_add | ||
497 | * The dev_t for the char device will be taken from the struct device which | ||
498 | * needs to be initialized first. This helper function correctly takes a | ||
499 | * reference to the parent device so the parent will not get released until | ||
500 | * all references to the cdev are released. | ||
501 | * | ||
502 | * This helper uses dev->devt for the device number. If it is not set | ||
503 | * it will not add the cdev and it will be equivalent to device_add. | ||
504 | * | ||
505 | * This function should be used whenever the struct cdev and the | ||
506 | * struct device are members of the same structure whose lifetime is | ||
507 | * managed by the struct device. | ||
508 | * | ||
509 | * NOTE: Callers must assume that userspace was able to open the cdev and | ||
510 | * can call cdev fops callbacks at any time, even if this function fails. | ||
511 | */ | ||
512 | int cdev_device_add(struct cdev *cdev, struct device *dev) | ||
513 | { | ||
514 | int rc = 0; | ||
515 | |||
516 | if (dev->devt) { | ||
517 | cdev_set_parent(cdev, &dev->kobj); | ||
518 | |||
519 | rc = cdev_add(cdev, dev->devt, 1); | ||
520 | if (rc) | ||
521 | return rc; | ||
522 | } | ||
523 | |||
524 | rc = device_add(dev); | ||
525 | if (rc) | ||
526 | cdev_del(cdev); | ||
527 | |||
528 | return rc; | ||
529 | } | ||
530 | |||
531 | /** | ||
532 | * cdev_device_del() - inverse of cdev_device_add | ||
533 | * @dev: the device structure | ||
534 | * @cdev: the cdev structure | ||
535 | * | ||
536 | * cdev_device_del() is a helper function to call cdev_del and device_del. | ||
537 | * It should be used whenever cdev_device_add is used. | ||
538 | * | ||
539 | * If dev->devt is not set it will not remove the cdev and will be equivalent | ||
540 | * to device_del. | ||
541 | * | ||
542 | * NOTE: This guarantees that associated sysfs callbacks are not running | ||
543 | * or runnable, however any cdevs already open will remain and their fops | ||
544 | * will still be callable even after this function returns. | ||
545 | */ | ||
546 | void cdev_device_del(struct cdev *cdev, struct device *dev) | ||
547 | { | ||
548 | device_del(dev); | ||
549 | if (dev->devt) | ||
550 | cdev_del(cdev); | ||
551 | } | ||
552 | |||
474 | static void cdev_unmap(dev_t dev, unsigned count) | 553 | static void cdev_unmap(dev_t dev, unsigned count) |
475 | { | 554 | { |
476 | kobj_unmap(cdev_map, dev, count); | 555 | kobj_unmap(cdev_map, dev, count); |
@@ -482,6 +561,10 @@ static void cdev_unmap(dev_t dev, unsigned count) | |||
482 | * | 561 | * |
483 | * cdev_del() removes @p from the system, possibly freeing the structure | 562 | * cdev_del() removes @p from the system, possibly freeing the structure |
484 | * itself. | 563 | * itself. |
564 | * | ||
565 | * NOTE: This guarantees that cdev device will no longer be able to be | ||
566 | * opened, however any cdevs already open will remain and their fops will | ||
567 | * still be callable even after cdev_del returns. | ||
485 | */ | 568 | */ |
486 | void cdev_del(struct cdev *p) | 569 | void cdev_del(struct cdev *p) |
487 | { | 570 | { |
@@ -570,5 +653,8 @@ EXPORT_SYMBOL(cdev_init); | |||
570 | EXPORT_SYMBOL(cdev_alloc); | 653 | EXPORT_SYMBOL(cdev_alloc); |
571 | EXPORT_SYMBOL(cdev_del); | 654 | EXPORT_SYMBOL(cdev_del); |
572 | EXPORT_SYMBOL(cdev_add); | 655 | EXPORT_SYMBOL(cdev_add); |
656 | EXPORT_SYMBOL(cdev_set_parent); | ||
657 | EXPORT_SYMBOL(cdev_device_add); | ||
658 | EXPORT_SYMBOL(cdev_device_del); | ||
573 | EXPORT_SYMBOL(__register_chrdev); | 659 | EXPORT_SYMBOL(__register_chrdev); |
574 | EXPORT_SYMBOL(__unregister_chrdev); | 660 | EXPORT_SYMBOL(__unregister_chrdev); |
diff --git a/include/linux/cdev.h b/include/linux/cdev.h index f8763615a5f2..408bc09ce497 100644 --- a/include/linux/cdev.h +++ b/include/linux/cdev.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/kobject.h> | 4 | #include <linux/kobject.h> |
5 | #include <linux/kdev_t.h> | 5 | #include <linux/kdev_t.h> |
6 | #include <linux/list.h> | 6 | #include <linux/list.h> |
7 | #include <linux/device.h> | ||
7 | 8 | ||
8 | struct file_operations; | 9 | struct file_operations; |
9 | struct inode; | 10 | struct inode; |
@@ -26,6 +27,10 @@ void cdev_put(struct cdev *p); | |||
26 | 27 | ||
27 | int cdev_add(struct cdev *, dev_t, unsigned); | 28 | int cdev_add(struct cdev *, dev_t, unsigned); |
28 | 29 | ||
30 | void cdev_set_parent(struct cdev *p, struct kobject *kobj); | ||
31 | int cdev_device_add(struct cdev *cdev, struct device *dev); | ||
32 | void cdev_device_del(struct cdev *cdev, struct device *dev); | ||
33 | |||
29 | void cdev_del(struct cdev *); | 34 | void cdev_del(struct cdev *); |
30 | 35 | ||
31 | void cd_forget(struct inode *); | 36 | void cd_forget(struct inode *); |