diff options
author | Dmitry Torokhov <dtor@insightbb.com> | 2006-12-08 01:07:56 -0500 |
---|---|---|
committer | Dmitry Torokhov <dtor@insightbb.com> | 2006-12-08 01:07:56 -0500 |
commit | bef986502fa398b1785a3979b1aa17cd902d3527 (patch) | |
tree | b59c1afe7b1dfcc001b86e54863f550d7ddc8c34 /drivers/base/core.c | |
parent | 4bdbd2807deeccc0793d57fb5120d7a53f2c0b3c (diff) | |
parent | c99767974ebd2a719d849fdeaaa1674456f5283f (diff) |
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts:
drivers/usb/input/hid.h
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r-- | drivers/base/core.c | 242 |
1 files changed, 222 insertions, 20 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 68ad11af22b4..67b79a7592a9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/string.h> | 18 | #include <linux/string.h> |
19 | #include <linux/kdev_t.h> | 19 | #include <linux/kdev_t.h> |
20 | #include <linux/notifier.h> | ||
20 | 21 | ||
21 | #include <asm/semaphore.h> | 22 | #include <asm/semaphore.h> |
22 | 23 | ||
@@ -153,20 +154,24 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, | |||
153 | "MINOR=%u", MINOR(dev->devt)); | 154 | "MINOR=%u", MINOR(dev->devt)); |
154 | } | 155 | } |
155 | 156 | ||
157 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
156 | /* add bus name (same as SUBSYSTEM, deprecated) */ | 158 | /* add bus name (same as SUBSYSTEM, deprecated) */ |
157 | if (dev->bus) | 159 | if (dev->bus) |
158 | add_uevent_var(envp, num_envp, &i, | 160 | add_uevent_var(envp, num_envp, &i, |
159 | buffer, buffer_size, &length, | 161 | buffer, buffer_size, &length, |
160 | "PHYSDEVBUS=%s", dev->bus->name); | 162 | "PHYSDEVBUS=%s", dev->bus->name); |
163 | #endif | ||
161 | 164 | ||
162 | /* add driver name (PHYSDEV* values are deprecated)*/ | 165 | /* add driver name (PHYSDEV* values are deprecated)*/ |
163 | if (dev->driver) { | 166 | if (dev->driver) { |
164 | add_uevent_var(envp, num_envp, &i, | 167 | add_uevent_var(envp, num_envp, &i, |
165 | buffer, buffer_size, &length, | 168 | buffer, buffer_size, &length, |
166 | "DRIVER=%s", dev->driver->name); | 169 | "DRIVER=%s", dev->driver->name); |
170 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
167 | add_uevent_var(envp, num_envp, &i, | 171 | add_uevent_var(envp, num_envp, &i, |
168 | buffer, buffer_size, &length, | 172 | buffer, buffer_size, &length, |
169 | "PHYSDEVDRIVER=%s", dev->driver->name); | 173 | "PHYSDEVDRIVER=%s", dev->driver->name); |
174 | #endif | ||
170 | } | 175 | } |
171 | 176 | ||
172 | /* terminate, set to next free slot, shrink available space */ | 177 | /* terminate, set to next free slot, shrink available space */ |
@@ -381,8 +386,55 @@ void device_initialize(struct device *dev) | |||
381 | INIT_LIST_HEAD(&dev->node); | 386 | INIT_LIST_HEAD(&dev->node); |
382 | init_MUTEX(&dev->sem); | 387 | init_MUTEX(&dev->sem); |
383 | device_init_wakeup(dev, 0); | 388 | device_init_wakeup(dev, 0); |
389 | set_dev_node(dev, -1); | ||
384 | } | 390 | } |
385 | 391 | ||
392 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
393 | static int setup_parent(struct device *dev, struct device *parent) | ||
394 | { | ||
395 | /* Set the parent to the class, not the parent device */ | ||
396 | /* this keeps sysfs from having a symlink to make old udevs happy */ | ||
397 | if (dev->class) | ||
398 | dev->kobj.parent = &dev->class->subsys.kset.kobj; | ||
399 | else if (parent) | ||
400 | dev->kobj.parent = &parent->kobj; | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | #else | ||
405 | static int virtual_device_parent(struct device *dev) | ||
406 | { | ||
407 | if (!dev->class) | ||
408 | return -ENODEV; | ||
409 | |||
410 | if (!dev->class->virtual_dir) { | ||
411 | static struct kobject *virtual_dir = NULL; | ||
412 | |||
413 | if (!virtual_dir) | ||
414 | virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); | ||
415 | dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); | ||
416 | } | ||
417 | |||
418 | dev->kobj.parent = dev->class->virtual_dir; | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int setup_parent(struct device *dev, struct device *parent) | ||
423 | { | ||
424 | int error; | ||
425 | |||
426 | /* if this is a class device, and has no parent, create one */ | ||
427 | if ((dev->class) && (parent == NULL)) { | ||
428 | error = virtual_device_parent(dev); | ||
429 | if (error) | ||
430 | return error; | ||
431 | } else if (parent) | ||
432 | dev->kobj.parent = &parent->kobj; | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | #endif | ||
437 | |||
386 | /** | 438 | /** |
387 | * device_add - add device to device hierarchy. | 439 | * device_add - add device to device hierarchy. |
388 | * @dev: device. | 440 | * @dev: device. |
@@ -405,29 +457,29 @@ int device_add(struct device *dev) | |||
405 | if (!dev || !strlen(dev->bus_id)) | 457 | if (!dev || !strlen(dev->bus_id)) |
406 | goto Error; | 458 | goto Error; |
407 | 459 | ||
408 | /* if this is a class device, and has no parent, create one */ | 460 | pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); |
409 | if ((dev->class) && (dev->parent == NULL)) { | ||
410 | error = virtual_device_parent(dev); | ||
411 | if (error) | ||
412 | goto Error; | ||
413 | } | ||
414 | 461 | ||
415 | parent = get_device(dev->parent); | 462 | parent = get_device(dev->parent); |
416 | 463 | ||
417 | pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); | 464 | error = setup_parent(dev, parent); |
465 | if (error) | ||
466 | goto Error; | ||
418 | 467 | ||
419 | /* first, register with generic layer. */ | 468 | /* first, register with generic layer. */ |
420 | kobject_set_name(&dev->kobj, "%s", dev->bus_id); | 469 | kobject_set_name(&dev->kobj, "%s", dev->bus_id); |
421 | if (parent) | 470 | error = kobject_add(&dev->kobj); |
422 | dev->kobj.parent = &parent->kobj; | 471 | if (error) |
423 | |||
424 | if ((error = kobject_add(&dev->kobj))) | ||
425 | goto Error; | 472 | goto Error; |
426 | 473 | ||
427 | /* notify platform of device entry */ | 474 | /* notify platform of device entry */ |
428 | if (platform_notify) | 475 | if (platform_notify) |
429 | platform_notify(dev); | 476 | platform_notify(dev); |
430 | 477 | ||
478 | /* notify clients of device entry (new way) */ | ||
479 | if (dev->bus) | ||
480 | blocking_notifier_call_chain(&dev->bus->bus_notifier, | ||
481 | BUS_NOTIFY_ADD_DEVICE, dev); | ||
482 | |||
431 | dev->uevent_attr.attr.name = "uevent"; | 483 | dev->uevent_attr.attr.name = "uevent"; |
432 | dev->uevent_attr.attr.mode = S_IWUSR; | 484 | dev->uevent_attr.attr.mode = S_IWUSR; |
433 | if (dev->driver) | 485 | if (dev->driver) |
@@ -461,13 +513,18 @@ int device_add(struct device *dev) | |||
461 | if (dev->class) { | 513 | if (dev->class) { |
462 | sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, | 514 | sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, |
463 | "subsystem"); | 515 | "subsystem"); |
464 | sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, | 516 | /* If this is not a "fake" compatible device, then create the |
465 | dev->bus_id); | 517 | * symlink from the class to the device. */ |
518 | if (dev->kobj.parent != &dev->class->subsys.kset.kobj) | ||
519 | sysfs_create_link(&dev->class->subsys.kset.kobj, | ||
520 | &dev->kobj, dev->bus_id); | ||
521 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
466 | if (parent) { | 522 | if (parent) { |
467 | sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); | 523 | sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); |
468 | class_name = make_class_name(dev->class->name, &dev->kobj); | 524 | class_name = make_class_name(dev->class->name, &dev->kobj); |
469 | sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); | 525 | sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); |
470 | } | 526 | } |
527 | #endif | ||
471 | } | 528 | } |
472 | 529 | ||
473 | if ((error = device_add_attrs(dev))) | 530 | if ((error = device_add_attrs(dev))) |
@@ -504,6 +561,9 @@ int device_add(struct device *dev) | |||
504 | BusError: | 561 | BusError: |
505 | device_pm_remove(dev); | 562 | device_pm_remove(dev); |
506 | PMError: | 563 | PMError: |
564 | if (dev->bus) | ||
565 | blocking_notifier_call_chain(&dev->bus->bus_notifier, | ||
566 | BUS_NOTIFY_DEL_DEVICE, dev); | ||
507 | device_remove_groups(dev); | 567 | device_remove_groups(dev); |
508 | GroupError: | 568 | GroupError: |
509 | device_remove_attrs(dev); | 569 | device_remove_attrs(dev); |
@@ -586,22 +646,31 @@ void put_device(struct device * dev) | |||
586 | void device_del(struct device * dev) | 646 | void device_del(struct device * dev) |
587 | { | 647 | { |
588 | struct device * parent = dev->parent; | 648 | struct device * parent = dev->parent; |
589 | char *class_name = NULL; | ||
590 | struct class_interface *class_intf; | 649 | struct class_interface *class_intf; |
591 | 650 | ||
592 | if (parent) | 651 | if (parent) |
593 | klist_del(&dev->knode_parent); | 652 | klist_del(&dev->knode_parent); |
594 | if (dev->devt_attr) | 653 | if (dev->devt_attr) { |
595 | device_remove_file(dev, dev->devt_attr); | 654 | device_remove_file(dev, dev->devt_attr); |
655 | kfree(dev->devt_attr); | ||
656 | } | ||
596 | if (dev->class) { | 657 | if (dev->class) { |
597 | sysfs_remove_link(&dev->kobj, "subsystem"); | 658 | sysfs_remove_link(&dev->kobj, "subsystem"); |
598 | sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); | 659 | /* If this is not a "fake" compatible device, remove the |
599 | class_name = make_class_name(dev->class->name, &dev->kobj); | 660 | * symlink from the class to the device. */ |
661 | if (dev->kobj.parent != &dev->class->subsys.kset.kobj) | ||
662 | sysfs_remove_link(&dev->class->subsys.kset.kobj, | ||
663 | dev->bus_id); | ||
664 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
600 | if (parent) { | 665 | if (parent) { |
601 | sysfs_remove_link(&dev->kobj, "device"); | 666 | char *class_name = make_class_name(dev->class->name, |
667 | &dev->kobj); | ||
602 | sysfs_remove_link(&dev->parent->kobj, class_name); | 668 | sysfs_remove_link(&dev->parent->kobj, class_name); |
669 | kfree(class_name); | ||
670 | sysfs_remove_link(&dev->kobj, "device"); | ||
603 | } | 671 | } |
604 | kfree(class_name); | 672 | #endif |
673 | |||
605 | down(&dev->class->sem); | 674 | down(&dev->class->sem); |
606 | /* notify any interfaces that the device is now gone */ | 675 | /* notify any interfaces that the device is now gone */ |
607 | list_for_each_entry(class_intf, &dev->class->interfaces, node) | 676 | list_for_each_entry(class_intf, &dev->class->interfaces, node) |
@@ -614,13 +683,16 @@ void device_del(struct device * dev) | |||
614 | device_remove_file(dev, &dev->uevent_attr); | 683 | device_remove_file(dev, &dev->uevent_attr); |
615 | device_remove_groups(dev); | 684 | device_remove_groups(dev); |
616 | device_remove_attrs(dev); | 685 | device_remove_attrs(dev); |
686 | bus_remove_device(dev); | ||
617 | 687 | ||
618 | /* Notify the platform of the removal, in case they | 688 | /* Notify the platform of the removal, in case they |
619 | * need to do anything... | 689 | * need to do anything... |
620 | */ | 690 | */ |
621 | if (platform_notify_remove) | 691 | if (platform_notify_remove) |
622 | platform_notify_remove(dev); | 692 | platform_notify_remove(dev); |
623 | bus_remove_device(dev); | 693 | if (dev->bus) |
694 | blocking_notifier_call_chain(&dev->bus->bus_notifier, | ||
695 | BUS_NOTIFY_DEL_DEVICE, dev); | ||
624 | device_pm_remove(dev); | 696 | device_pm_remove(dev); |
625 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); | 697 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); |
626 | kobject_del(&dev->kobj); | 698 | kobject_del(&dev->kobj); |
@@ -679,12 +751,45 @@ int device_for_each_child(struct device * parent, void * data, | |||
679 | return error; | 751 | return error; |
680 | } | 752 | } |
681 | 753 | ||
754 | /** | ||
755 | * device_find_child - device iterator for locating a particular device. | ||
756 | * @parent: parent struct device | ||
757 | * @data: Data to pass to match function | ||
758 | * @match: Callback function to check device | ||
759 | * | ||
760 | * This is similar to the device_for_each_child() function above, but it | ||
761 | * returns a reference to a device that is 'found' for later use, as | ||
762 | * determined by the @match callback. | ||
763 | * | ||
764 | * The callback should return 0 if the device doesn't match and non-zero | ||
765 | * if it does. If the callback returns non-zero and a reference to the | ||
766 | * current device can be obtained, this function will return to the caller | ||
767 | * and not iterate over any more devices. | ||
768 | */ | ||
769 | struct device * device_find_child(struct device *parent, void *data, | ||
770 | int (*match)(struct device *, void *)) | ||
771 | { | ||
772 | struct klist_iter i; | ||
773 | struct device *child; | ||
774 | |||
775 | if (!parent) | ||
776 | return NULL; | ||
777 | |||
778 | klist_iter_init(&parent->klist_children, &i); | ||
779 | while ((child = next_device(&i))) | ||
780 | if (match(child, data) && get_device(child)) | ||
781 | break; | ||
782 | klist_iter_exit(&i); | ||
783 | return child; | ||
784 | } | ||
785 | |||
682 | int __init devices_init(void) | 786 | int __init devices_init(void) |
683 | { | 787 | { |
684 | return subsystem_register(&devices_subsys); | 788 | return subsystem_register(&devices_subsys); |
685 | } | 789 | } |
686 | 790 | ||
687 | EXPORT_SYMBOL_GPL(device_for_each_child); | 791 | EXPORT_SYMBOL_GPL(device_for_each_child); |
792 | EXPORT_SYMBOL_GPL(device_find_child); | ||
688 | 793 | ||
689 | EXPORT_SYMBOL_GPL(device_initialize); | 794 | EXPORT_SYMBOL_GPL(device_initialize); |
690 | EXPORT_SYMBOL_GPL(device_add); | 795 | EXPORT_SYMBOL_GPL(device_add); |
@@ -807,8 +912,10 @@ int device_rename(struct device *dev, char *new_name) | |||
807 | 912 | ||
808 | pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name); | 913 | pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name); |
809 | 914 | ||
915 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
810 | if ((dev->class) && (dev->parent)) | 916 | if ((dev->class) && (dev->parent)) |
811 | old_class_name = make_class_name(dev->class->name, &dev->kobj); | 917 | old_class_name = make_class_name(dev->class->name, &dev->kobj); |
918 | #endif | ||
812 | 919 | ||
813 | if (dev->class) { | 920 | if (dev->class) { |
814 | old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); | 921 | old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); |
@@ -823,6 +930,7 @@ int device_rename(struct device *dev, char *new_name) | |||
823 | 930 | ||
824 | error = kobject_rename(&dev->kobj, new_name); | 931 | error = kobject_rename(&dev->kobj, new_name); |
825 | 932 | ||
933 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
826 | if (old_class_name) { | 934 | if (old_class_name) { |
827 | new_class_name = make_class_name(dev->class->name, &dev->kobj); | 935 | new_class_name = make_class_name(dev->class->name, &dev->kobj); |
828 | if (new_class_name) { | 936 | if (new_class_name) { |
@@ -831,6 +939,8 @@ int device_rename(struct device *dev, char *new_name) | |||
831 | sysfs_remove_link(&dev->parent->kobj, old_class_name); | 939 | sysfs_remove_link(&dev->parent->kobj, old_class_name); |
832 | } | 940 | } |
833 | } | 941 | } |
942 | #endif | ||
943 | |||
834 | if (dev->class) { | 944 | if (dev->class) { |
835 | sysfs_remove_link(&dev->class->subsys.kset.kobj, | 945 | sysfs_remove_link(&dev->class->subsys.kset.kobj, |
836 | old_symlink_name); | 946 | old_symlink_name); |
@@ -846,3 +956,95 @@ int device_rename(struct device *dev, char *new_name) | |||
846 | 956 | ||
847 | return error; | 957 | return error; |
848 | } | 958 | } |
959 | |||
960 | |||
961 | static int device_move_class_links(struct device *dev, | ||
962 | struct device *old_parent, | ||
963 | struct device *new_parent) | ||
964 | { | ||
965 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
966 | int error; | ||
967 | char *class_name; | ||
968 | |||
969 | class_name = make_class_name(dev->class->name, &dev->kobj); | ||
970 | if (!class_name) { | ||
971 | error = PTR_ERR(class_name); | ||
972 | class_name = NULL; | ||
973 | goto out; | ||
974 | } | ||
975 | if (old_parent) { | ||
976 | sysfs_remove_link(&dev->kobj, "device"); | ||
977 | sysfs_remove_link(&old_parent->kobj, class_name); | ||
978 | } | ||
979 | error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device"); | ||
980 | if (error) | ||
981 | goto out; | ||
982 | error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name); | ||
983 | if (error) | ||
984 | sysfs_remove_link(&dev->kobj, "device"); | ||
985 | out: | ||
986 | kfree(class_name); | ||
987 | return error; | ||
988 | #else | ||
989 | return 0; | ||
990 | #endif | ||
991 | } | ||
992 | |||
993 | /** | ||
994 | * device_move - moves a device to a new parent | ||
995 | * @dev: the pointer to the struct device to be moved | ||
996 | * @new_parent: the new parent of the device | ||
997 | */ | ||
998 | int device_move(struct device *dev, struct device *new_parent) | ||
999 | { | ||
1000 | int error; | ||
1001 | struct device *old_parent; | ||
1002 | |||
1003 | dev = get_device(dev); | ||
1004 | if (!dev) | ||
1005 | return -EINVAL; | ||
1006 | |||
1007 | if (!device_is_registered(dev)) { | ||
1008 | error = -EINVAL; | ||
1009 | goto out; | ||
1010 | } | ||
1011 | new_parent = get_device(new_parent); | ||
1012 | if (!new_parent) { | ||
1013 | error = -EINVAL; | ||
1014 | goto out; | ||
1015 | } | ||
1016 | pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, | ||
1017 | new_parent->bus_id); | ||
1018 | error = kobject_move(&dev->kobj, &new_parent->kobj); | ||
1019 | if (error) { | ||
1020 | put_device(new_parent); | ||
1021 | goto out; | ||
1022 | } | ||
1023 | old_parent = dev->parent; | ||
1024 | dev->parent = new_parent; | ||
1025 | if (old_parent) | ||
1026 | klist_remove(&dev->knode_parent); | ||
1027 | klist_add_tail(&dev->knode_parent, &new_parent->klist_children); | ||
1028 | if (!dev->class) | ||
1029 | goto out_put; | ||
1030 | error = device_move_class_links(dev, old_parent, new_parent); | ||
1031 | if (error) { | ||
1032 | /* We ignore errors on cleanup since we're hosed anyway... */ | ||
1033 | device_move_class_links(dev, new_parent, old_parent); | ||
1034 | if (!kobject_move(&dev->kobj, &old_parent->kobj)) { | ||
1035 | klist_remove(&dev->knode_parent); | ||
1036 | if (old_parent) | ||
1037 | klist_add_tail(&dev->knode_parent, | ||
1038 | &old_parent->klist_children); | ||
1039 | } | ||
1040 | put_device(new_parent); | ||
1041 | goto out; | ||
1042 | } | ||
1043 | out_put: | ||
1044 | put_device(old_parent); | ||
1045 | out: | ||
1046 | put_device(dev); | ||
1047 | return error; | ||
1048 | } | ||
1049 | |||
1050 | EXPORT_SYMBOL_GPL(device_move); | ||