Commit 2e5f10e4f0a9649186d8a8c793822b2e0dae8373
Committed by
Greg Kroah-Hartman
1 parent
6986a978ee
Exists in
master
and in
7 other branches
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>
Showing 5 changed files with 96 additions and 58 deletions Side-by-side Diff
drivers/usb/core/endpoint.c
... | ... | @@ -156,6 +156,10 @@ |
156 | 156 | static struct attribute_group ep_dev_attr_grp = { |
157 | 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 | 164 | static int usb_endpoint_major_init(void) |
161 | 165 | { |
... | ... | @@ -298,6 +302,7 @@ |
298 | 302 | |
299 | 303 | ep_dev->desc = &endpoint->desc; |
300 | 304 | ep_dev->udev = udev; |
305 | + ep_dev->dev.groups = ep_dev_groups; | |
301 | 306 | ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor); |
302 | 307 | ep_dev->dev.class = ep_class->class; |
303 | 308 | ep_dev->dev.parent = parent; |
... | ... | @@ -309,9 +314,6 @@ |
309 | 314 | retval = device_register(&ep_dev->dev); |
310 | 315 | if (retval) |
311 | 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 | 318 | /* create the symlink to the old-style "ep_XX" directory */ |
317 | 319 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); |
... | ... | @@ -322,8 +324,6 @@ |
322 | 324 | return retval; |
323 | 325 | |
324 | 326 | error_link: |
325 | - sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | |
326 | -error_group: | |
327 | 327 | device_unregister(&ep_dev->dev); |
328 | 328 | destroy_endpoint_class(); |
329 | 329 | return retval; |
... | ... | @@ -348,7 +348,6 @@ |
348 | 348 | |
349 | 349 | sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); |
350 | 350 | sysfs_remove_link(&ep_dev->dev.parent->kobj, name); |
351 | - sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); | |
352 | 351 | device_unregister(&ep_dev->dev); |
353 | 352 | endpoint->ep_dev = NULL; |
354 | 353 | destroy_endpoint_class(); |
drivers/usb/core/message.c
... | ... | @@ -1607,6 +1607,7 @@ |
1607 | 1607 | intf->dev.driver = NULL; |
1608 | 1608 | intf->dev.bus = &usb_bus_type; |
1609 | 1609 | intf->dev.type = &usb_if_device_type; |
1610 | + intf->dev.groups = usb_interface_groups; | |
1610 | 1611 | intf->dev.dma_mask = dev->dev.dma_mask; |
1611 | 1612 | device_initialize(&intf->dev); |
1612 | 1613 | mark_quiesced(intf); |
drivers/usb/core/sysfs.c
... | ... | @@ -538,6 +538,46 @@ |
538 | 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 | 581 | /* Binary descriptors */ |
542 | 582 | |
543 | 583 | static ssize_t |
... | ... | @@ -591,10 +631,9 @@ |
591 | 631 | struct device *dev = &udev->dev; |
592 | 632 | int retval; |
593 | 633 | |
594 | - retval = sysfs_create_group(&dev->kobj, &dev_attr_grp); | |
595 | - if (retval) | |
596 | - return retval; | |
597 | - | |
634 | + /* Unforunately these attributes cannot be created before | |
635 | + * the uevent is broadcast. | |
636 | + */ | |
598 | 637 | retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); |
599 | 638 | if (retval) |
600 | 639 | goto error; |
... | ... | @@ -607,21 +646,6 @@ |
607 | 646 | if (retval) |
608 | 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 | 649 | retval = usb_create_ep_files(dev, &udev->ep0, udev); |
626 | 650 | if (retval) |
627 | 651 | goto error; |
628 | 652 | |
... | ... | @@ -636,13 +660,9 @@ |
636 | 660 | struct device *dev = &udev->dev; |
637 | 661 | |
638 | 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 | 663 | remove_power_attributes(dev); |
643 | 664 | remove_persist_attributes(dev); |
644 | 665 | device_remove_bin_file(dev, &dev_bin_attr_descriptors); |
645 | - sysfs_remove_group(&dev->kobj, &dev_attr_grp); | |
646 | 666 | } |
647 | 667 | |
648 | 668 | /* Interface Accociation Descriptor fields */ |
649 | 669 | |
650 | 670 | |
... | ... | @@ -688,17 +708,15 @@ |
688 | 708 | struct device_attribute *attr, char *buf) |
689 | 709 | { |
690 | 710 | struct usb_interface *intf; |
691 | - struct usb_device *udev; | |
692 | - int len; | |
711 | + char *string; | |
693 | 712 | |
694 | 713 | intf = to_usb_interface(dev); |
695 | - udev = interface_to_usbdev(intf); | |
696 | - len = snprintf(buf, 256, "%s", intf->cur_altsetting->string); | |
697 | - if (len < 0) | |
714 | + string = intf->cur_altsetting->string; | |
715 | + barrier(); /* The altsetting might change! */ | |
716 | + | |
717 | + if (!string) | |
698 | 718 | return 0; |
699 | - buf[len] = '\n'; | |
700 | - buf[len+1] = 0; | |
701 | - return len+1; | |
719 | + return sprintf(buf, "%s\n", string); | |
702 | 720 | } |
703 | 721 | static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); |
704 | 722 | |
... | ... | @@ -727,18 +745,6 @@ |
727 | 745 | } |
728 | 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 | 748 | static struct attribute *intf_attrs[] = { |
743 | 749 | &dev_attr_bInterfaceNumber.attr, |
744 | 750 | &dev_attr_bAlternateSetting.attr, |
... | ... | @@ -753,6 +759,37 @@ |
753 | 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 | 793 | static inline void usb_create_intf_ep_files(struct usb_interface *intf, |
757 | 794 | struct usb_device *udev) |
758 | 795 | { |
759 | 796 | |
760 | 797 | |
761 | 798 | |
... | ... | @@ -777,23 +814,21 @@ |
777 | 814 | |
778 | 815 | int usb_create_sysfs_intf_files(struct usb_interface *intf) |
779 | 816 | { |
780 | - struct device *dev = &intf->dev; | |
781 | 817 | struct usb_device *udev = interface_to_usbdev(intf); |
782 | 818 | struct usb_host_interface *alt = intf->cur_altsetting; |
783 | 819 | int retval; |
784 | 820 | |
785 | 821 | if (intf->sysfs_files_created) |
786 | 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 | 828 | if (alt->string == NULL) |
792 | 829 | alt->string = usb_cache_string(udev, alt->desc.iInterface); |
793 | 830 | if (alt->string) |
794 | - retval = device_create_file(dev, &dev_attr_interface); | |
795 | - if (intf->intf_assoc) | |
796 | - retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp); | |
831 | + retval = device_create_file(&intf->dev, &dev_attr_interface); | |
797 | 832 | usb_create_intf_ep_files(intf, udev); |
798 | 833 | intf->sysfs_files_created = 1; |
799 | 834 | return 0; |
... | ... | @@ -807,8 +842,6 @@ |
807 | 842 | return; |
808 | 843 | usb_remove_intf_ep_files(intf); |
809 | 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 | 845 | intf->sysfs_files_created = 0; |
813 | 846 | } |
drivers/usb/core/usb.c
... | ... | @@ -291,6 +291,7 @@ |
291 | 291 | device_initialize(&dev->dev); |
292 | 292 | dev->dev.bus = &usb_bus_type; |
293 | 293 | dev->dev.type = &usb_device_type; |
294 | + dev->dev.groups = usb_device_groups; | |
294 | 295 | dev->dev.dma_mask = bus->controller->dma_mask; |
295 | 296 | set_dev_node(&dev->dev, dev_to_node(bus->controller)); |
296 | 297 | dev->state = USB_STATE_ATTACHED; |
drivers/usb/core/usb.h
... | ... | @@ -130,6 +130,10 @@ |
130 | 130 | /* for labeling diagnostics */ |
131 | 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 | 137 | /* usbfs stuff */ |
134 | 138 | extern struct mutex usbfs_mutex; |
135 | 139 | extern struct usb_driver usbfs_driver; |