diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-04-30 15:37:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-05-14 13:00:26 -0400 |
commit | 2e5f10e4f0a9649186d8a8c793822b2e0dae8373 (patch) | |
tree | b05f66b353cd9537fc031b387c03040d3b92dcc6 | |
parent | 6986a978eec70c867717fe6bee736f0bd1db1508 (diff) |
USB: create attributes before sending uevent
This patch (as1087d) fixes a long-standing problem in usbcore: Device,
interface, and endpoint attributes aren't added until _after_ the
creation uevent has already been broadcast.
Unfortunately there are a few attributes which cannot be created that
early. The "descriptors" attribute is binary and so must be created
separately. The power-management attributes can't be created until
the dev/power/ group exists. And the interface string can vary from
one altsetting to another, so it has to be created dynamically.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/endpoint.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 137 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 4 |
5 files changed, 96 insertions, 58 deletions
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 99e5a68a3f12..fae55a31e26d 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c | |||
@@ -156,6 +156,10 @@ static struct attribute *ep_dev_attrs[] = { | |||
156 | static struct attribute_group ep_dev_attr_grp = { | 156 | static struct attribute_group ep_dev_attr_grp = { |
157 | .attrs = ep_dev_attrs, | 157 | .attrs = ep_dev_attrs, |
158 | }; | 158 | }; |
159 | static struct attribute_group *ep_dev_groups[] = { | ||
160 | &ep_dev_attr_grp, | ||
161 | NULL | ||
162 | }; | ||
159 | 163 | ||
160 | static int usb_endpoint_major_init(void) | 164 | static int usb_endpoint_major_init(void) |
161 | { | 165 | { |
@@ -298,6 +302,7 @@ int usb_create_ep_files(struct device *parent, | |||
298 | 302 | ||
299 | ep_dev->desc = &endpoint->desc; | 303 | ep_dev->desc = &endpoint->desc; |
300 | ep_dev->udev = udev; | 304 | ep_dev->udev = udev; |
305 | ep_dev->dev.groups = ep_dev_groups; | ||
301 | ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor); | 306 | ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor); |
302 | ep_dev->dev.class = ep_class->class; | 307 | ep_dev->dev.class = ep_class->class; |
303 | ep_dev->dev.parent = parent; | 308 | ep_dev->dev.parent = parent; |
@@ -309,9 +314,6 @@ int usb_create_ep_files(struct device *parent, | |||
309 | retval = device_register(&ep_dev->dev); | 314 | retval = device_register(&ep_dev->dev); |
310 | if (retval) | 315 | if (retval) |
311 | goto error_chrdev; | 316 | goto error_chrdev; |
312 | retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | ||
313 | if (retval) | ||
314 | goto error_group; | ||
315 | 317 | ||
316 | /* create the symlink to the old-style "ep_XX" directory */ | 318 | /* create the symlink to the old-style "ep_XX" directory */ |
317 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); | 319 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); |
@@ -322,8 +324,6 @@ int usb_create_ep_files(struct device *parent, | |||
322 | return retval; | 324 | return retval; |
323 | 325 | ||
324 | error_link: | 326 | error_link: |
325 | sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | ||
326 | error_group: | ||
327 | device_unregister(&ep_dev->dev); | 327 | device_unregister(&ep_dev->dev); |
328 | destroy_endpoint_class(); | 328 | destroy_endpoint_class(); |
329 | return retval; | 329 | return retval; |
@@ -348,7 +348,6 @@ void usb_remove_ep_files(struct usb_host_endpoint *endpoint) | |||
348 | 348 | ||
349 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); | 349 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); |
350 | sysfs_remove_link(&ep_dev->dev.parent->kobj, name); | 350 | sysfs_remove_link(&ep_dev->dev.parent->kobj, name); |
351 | sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | ||
352 | device_unregister(&ep_dev->dev); | 351 | device_unregister(&ep_dev->dev); |
353 | endpoint->ep_dev = NULL; | 352 | endpoint->ep_dev = NULL; |
354 | destroy_endpoint_class(); | 353 | destroy_endpoint_class(); |
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 3e69266e1f4d..fe47d145255a 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -1607,6 +1607,7 @@ free_interfaces: | |||
1607 | intf->dev.driver = NULL; | 1607 | intf->dev.driver = NULL; |
1608 | intf->dev.bus = &usb_bus_type; | 1608 | intf->dev.bus = &usb_bus_type; |
1609 | intf->dev.type = &usb_if_device_type; | 1609 | intf->dev.type = &usb_if_device_type; |
1610 | intf->dev.groups = usb_interface_groups; | ||
1610 | intf->dev.dma_mask = dev->dev.dma_mask; | 1611 | intf->dev.dma_mask = dev->dev.dma_mask; |
1611 | device_initialize(&intf->dev); | 1612 | device_initialize(&intf->dev); |
1612 | mark_quiesced(intf); | 1613 | mark_quiesced(intf); |
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5b20a60de8ba..c783cb111847 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -538,6 +538,46 @@ static struct attribute_group dev_attr_grp = { | |||
538 | .attrs = dev_attrs, | 538 | .attrs = dev_attrs, |
539 | }; | 539 | }; |
540 | 540 | ||
541 | /* When modifying this list, be sure to modify dev_string_attrs_are_visible() | ||
542 | * accordingly. | ||
543 | */ | ||
544 | static struct attribute *dev_string_attrs[] = { | ||
545 | &dev_attr_manufacturer.attr, | ||
546 | &dev_attr_product.attr, | ||
547 | &dev_attr_serial.attr, | ||
548 | NULL | ||
549 | }; | ||
550 | |||
551 | static mode_t dev_string_attrs_are_visible(struct kobject *kobj, | ||
552 | struct attribute *a, int n) | ||
553 | { | ||
554 | struct usb_device *udev = to_usb_device( | ||
555 | container_of(kobj, struct device, kobj)); | ||
556 | |||
557 | if (a == &dev_attr_manufacturer.attr) { | ||
558 | if (udev->manufacturer == NULL) | ||
559 | return 0; | ||
560 | } else if (a == &dev_attr_product.attr) { | ||
561 | if (udev->product == NULL) | ||
562 | return 0; | ||
563 | } else if (a == &dev_attr_serial.attr) { | ||
564 | if (udev->serial == NULL) | ||
565 | return 0; | ||
566 | } | ||
567 | return a->mode; | ||
568 | } | ||
569 | |||
570 | static struct attribute_group dev_string_attr_grp = { | ||
571 | .attrs = dev_string_attrs, | ||
572 | .is_visible = dev_string_attrs_are_visible, | ||
573 | }; | ||
574 | |||
575 | struct attribute_group *usb_device_groups[] = { | ||
576 | &dev_attr_grp, | ||
577 | &dev_string_attr_grp, | ||
578 | NULL | ||
579 | }; | ||
580 | |||
541 | /* Binary descriptors */ | 581 | /* Binary descriptors */ |
542 | 582 | ||
543 | static ssize_t | 583 | static ssize_t |
@@ -591,10 +631,9 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) | |||
591 | struct device *dev = &udev->dev; | 631 | struct device *dev = &udev->dev; |
592 | int retval; | 632 | int retval; |
593 | 633 | ||
594 | retval = sysfs_create_group(&dev->kobj, &dev_attr_grp); | 634 | /* Unforunately these attributes cannot be created before |
595 | if (retval) | 635 | * the uevent is broadcast. |
596 | return retval; | 636 | */ |
597 | |||
598 | retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); | 637 | retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); |
599 | if (retval) | 638 | if (retval) |
600 | goto error; | 639 | goto error; |
@@ -607,21 +646,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) | |||
607 | if (retval) | 646 | if (retval) |
608 | goto error; | 647 | goto error; |
609 | 648 | ||
610 | if (udev->manufacturer) { | ||
611 | retval = device_create_file(dev, &dev_attr_manufacturer); | ||
612 | if (retval) | ||
613 | goto error; | ||
614 | } | ||
615 | if (udev->product) { | ||
616 | retval = device_create_file(dev, &dev_attr_product); | ||
617 | if (retval) | ||
618 | goto error; | ||
619 | } | ||
620 | if (udev->serial) { | ||
621 | retval = device_create_file(dev, &dev_attr_serial); | ||
622 | if (retval) | ||
623 | goto error; | ||
624 | } | ||
625 | retval = usb_create_ep_files(dev, &udev->ep0, udev); | 649 | retval = usb_create_ep_files(dev, &udev->ep0, udev); |
626 | if (retval) | 650 | if (retval) |
627 | goto error; | 651 | goto error; |
@@ -636,13 +660,9 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) | |||
636 | struct device *dev = &udev->dev; | 660 | struct device *dev = &udev->dev; |
637 | 661 | ||
638 | usb_remove_ep_files(&udev->ep0); | 662 | usb_remove_ep_files(&udev->ep0); |
639 | device_remove_file(dev, &dev_attr_manufacturer); | ||
640 | device_remove_file(dev, &dev_attr_product); | ||
641 | device_remove_file(dev, &dev_attr_serial); | ||
642 | remove_power_attributes(dev); | 663 | remove_power_attributes(dev); |
643 | remove_persist_attributes(dev); | 664 | remove_persist_attributes(dev); |
644 | device_remove_bin_file(dev, &dev_bin_attr_descriptors); | 665 | device_remove_bin_file(dev, &dev_bin_attr_descriptors); |
645 | sysfs_remove_group(&dev->kobj, &dev_attr_grp); | ||
646 | } | 666 | } |
647 | 667 | ||
648 | /* Interface Accociation Descriptor fields */ | 668 | /* Interface Accociation Descriptor fields */ |
@@ -688,17 +708,15 @@ static ssize_t show_interface_string(struct device *dev, | |||
688 | struct device_attribute *attr, char *buf) | 708 | struct device_attribute *attr, char *buf) |
689 | { | 709 | { |
690 | struct usb_interface *intf; | 710 | struct usb_interface *intf; |
691 | struct usb_device *udev; | 711 | char *string; |
692 | int len; | ||
693 | 712 | ||
694 | intf = to_usb_interface(dev); | 713 | intf = to_usb_interface(dev); |
695 | udev = interface_to_usbdev(intf); | 714 | string = intf->cur_altsetting->string; |
696 | len = snprintf(buf, 256, "%s", intf->cur_altsetting->string); | 715 | barrier(); /* The altsetting might change! */ |
697 | if (len < 0) | 716 | |
717 | if (!string) | ||
698 | return 0; | 718 | return 0; |
699 | buf[len] = '\n'; | 719 | return sprintf(buf, "%s\n", string); |
700 | buf[len+1] = 0; | ||
701 | return len+1; | ||
702 | } | 720 | } |
703 | static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); | 721 | static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); |
704 | 722 | ||
@@ -727,18 +745,6 @@ static ssize_t show_modalias(struct device *dev, | |||
727 | } | 745 | } |
728 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | 746 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); |
729 | 747 | ||
730 | static struct attribute *intf_assoc_attrs[] = { | ||
731 | &dev_attr_iad_bFirstInterface.attr, | ||
732 | &dev_attr_iad_bInterfaceCount.attr, | ||
733 | &dev_attr_iad_bFunctionClass.attr, | ||
734 | &dev_attr_iad_bFunctionSubClass.attr, | ||
735 | &dev_attr_iad_bFunctionProtocol.attr, | ||
736 | NULL, | ||
737 | }; | ||
738 | static struct attribute_group intf_assoc_attr_grp = { | ||
739 | .attrs = intf_assoc_attrs, | ||
740 | }; | ||
741 | |||
742 | static struct attribute *intf_attrs[] = { | 748 | static struct attribute *intf_attrs[] = { |
743 | &dev_attr_bInterfaceNumber.attr, | 749 | &dev_attr_bInterfaceNumber.attr, |
744 | &dev_attr_bAlternateSetting.attr, | 750 | &dev_attr_bAlternateSetting.attr, |
@@ -753,6 +759,37 @@ static struct attribute_group intf_attr_grp = { | |||
753 | .attrs = intf_attrs, | 759 | .attrs = intf_attrs, |
754 | }; | 760 | }; |
755 | 761 | ||
762 | static struct attribute *intf_assoc_attrs[] = { | ||
763 | &dev_attr_iad_bFirstInterface.attr, | ||
764 | &dev_attr_iad_bInterfaceCount.attr, | ||
765 | &dev_attr_iad_bFunctionClass.attr, | ||
766 | &dev_attr_iad_bFunctionSubClass.attr, | ||
767 | &dev_attr_iad_bFunctionProtocol.attr, | ||
768 | NULL, | ||
769 | }; | ||
770 | |||
771 | static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj, | ||
772 | struct attribute *a, int n) | ||
773 | { | ||
774 | struct usb_interface *intf = to_usb_interface( | ||
775 | container_of(kobj, struct device, kobj)); | ||
776 | |||
777 | if (intf->intf_assoc == NULL) | ||
778 | return 0; | ||
779 | return a->mode; | ||
780 | } | ||
781 | |||
782 | static struct attribute_group intf_assoc_attr_grp = { | ||
783 | .attrs = intf_assoc_attrs, | ||
784 | .is_visible = intf_assoc_attrs_are_visible, | ||
785 | }; | ||
786 | |||
787 | struct attribute_group *usb_interface_groups[] = { | ||
788 | &intf_attr_grp, | ||
789 | &intf_assoc_attr_grp, | ||
790 | NULL | ||
791 | }; | ||
792 | |||
756 | static inline void usb_create_intf_ep_files(struct usb_interface *intf, | 793 | static inline void usb_create_intf_ep_files(struct usb_interface *intf, |
757 | struct usb_device *udev) | 794 | struct usb_device *udev) |
758 | { | 795 | { |
@@ -777,23 +814,21 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf) | |||
777 | 814 | ||
778 | int usb_create_sysfs_intf_files(struct usb_interface *intf) | 815 | int usb_create_sysfs_intf_files(struct usb_interface *intf) |
779 | { | 816 | { |
780 | struct device *dev = &intf->dev; | ||
781 | struct usb_device *udev = interface_to_usbdev(intf); | 817 | struct usb_device *udev = interface_to_usbdev(intf); |
782 | struct usb_host_interface *alt = intf->cur_altsetting; | 818 | struct usb_host_interface *alt = intf->cur_altsetting; |
783 | int retval; | 819 | int retval; |
784 | 820 | ||
785 | if (intf->sysfs_files_created) | 821 | if (intf->sysfs_files_created) |
786 | return 0; | 822 | return 0; |
787 | retval = sysfs_create_group(&dev->kobj, &intf_attr_grp); | ||
788 | if (retval) | ||
789 | return retval; | ||
790 | 823 | ||
824 | /* The interface string may be present in some altsettings | ||
825 | * and missing in others. Hence its attribute cannot be created | ||
826 | * before the uevent is broadcast. | ||
827 | */ | ||
791 | if (alt->string == NULL) | 828 | if (alt->string == NULL) |
792 | alt->string = usb_cache_string(udev, alt->desc.iInterface); | 829 | alt->string = usb_cache_string(udev, alt->desc.iInterface); |
793 | if (alt->string) | 830 | if (alt->string) |
794 | retval = device_create_file(dev, &dev_attr_interface); | 831 | retval = device_create_file(&intf->dev, &dev_attr_interface); |
795 | if (intf->intf_assoc) | ||
796 | retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp); | ||
797 | usb_create_intf_ep_files(intf, udev); | 832 | usb_create_intf_ep_files(intf, udev); |
798 | intf->sysfs_files_created = 1; | 833 | intf->sysfs_files_created = 1; |
799 | return 0; | 834 | return 0; |
@@ -807,7 +842,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf) | |||
807 | return; | 842 | return; |
808 | usb_remove_intf_ep_files(intf); | 843 | usb_remove_intf_ep_files(intf); |
809 | device_remove_file(dev, &dev_attr_interface); | 844 | device_remove_file(dev, &dev_attr_interface); |
810 | sysfs_remove_group(&dev->kobj, &intf_attr_grp); | ||
811 | sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp); | ||
812 | intf->sysfs_files_created = 0; | 845 | intf->sysfs_files_created = 0; |
813 | } | 846 | } |
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 1f0db51190cc..325774375837 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
@@ -291,6 +291,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, | |||
291 | device_initialize(&dev->dev); | 291 | device_initialize(&dev->dev); |
292 | dev->dev.bus = &usb_bus_type; | 292 | dev->dev.bus = &usb_bus_type; |
293 | dev->dev.type = &usb_device_type; | 293 | dev->dev.type = &usb_device_type; |
294 | dev->dev.groups = usb_device_groups; | ||
294 | dev->dev.dma_mask = bus->controller->dma_mask; | 295 | dev->dev.dma_mask = bus->controller->dma_mask; |
295 | set_dev_node(&dev->dev, dev_to_node(bus->controller)); | 296 | set_dev_node(&dev->dev, dev_to_node(bus->controller)); |
296 | dev->state = USB_STATE_ATTACHED; | 297 | dev->state = USB_STATE_ATTACHED; |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1bf8ccb9c58d..1a8bc21c335e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -130,6 +130,10 @@ static inline int is_active(const struct usb_interface *f) | |||
130 | /* for labeling diagnostics */ | 130 | /* for labeling diagnostics */ |
131 | extern const char *usbcore_name; | 131 | extern const char *usbcore_name; |
132 | 132 | ||
133 | /* sysfs stuff */ | ||
134 | extern struct attribute_group *usb_device_groups[]; | ||
135 | extern struct attribute_group *usb_interface_groups[]; | ||
136 | |||
133 | /* usbfs stuff */ | 137 | /* usbfs stuff */ |
134 | extern struct mutex usbfs_mutex; | 138 | extern struct mutex usbfs_mutex; |
135 | extern struct usb_driver usbfs_driver; | 139 | extern struct usb_driver usbfs_driver; |