diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2006-06-01 13:33:42 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-21 18:04:15 -0400 |
| commit | 79efa097e75018a2918155f343f0e08e61ee8a8c (patch) | |
| tree | 336237ca96191aeb9cac6ed8706bc479545f3108 | |
| parent | efcaa20525fde82bbb4fb8cd9e9016f6fabc6509 (diff) | |
[PATCH] usbcore: port reset for composite devices
This patch (as699) adds usb_reset_composite_device(), a routine for
sending a USB port reset to a device with multiple interfaces owned by
different drivers. Drivers are notified about impending and completed
resets through two new methods in the usb_driver structure.
The patch modifieds the usbfs ioctl code to make it use the new routine
instead of usb_reset_device(). Follow-up patches will modify the hub,
usb-storage, and usbhid drivers so they can utilize this new API.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/usb/core/devio.c | 3 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 84 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
| -rw-r--r-- | include/linux/usb.h | 9 |
4 files changed, 92 insertions, 5 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b04ede772f2c..df3fb57d71e6 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
| @@ -823,8 +823,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) | |||
| 823 | 823 | ||
| 824 | static int proc_resetdevice(struct dev_state *ps) | 824 | static int proc_resetdevice(struct dev_state *ps) |
| 825 | { | 825 | { |
| 826 | return usb_reset_device(ps->dev); | 826 | return usb_reset_composite_device(ps->dev, NULL); |
| 827 | |||
| 828 | } | 827 | } |
| 829 | 828 | ||
| 830 | static int proc_setintf(struct dev_state *ps, void __user *arg) | 829 | static int proc_setintf(struct dev_state *ps, void __user *arg) |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f41c08946a52..37c67d7e8b84 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
| @@ -3007,9 +3007,9 @@ static int config_descriptors_changed(struct usb_device *udev) | |||
| 3007 | * usb_reset_device - perform a USB port reset to reinitialize a device | 3007 | * usb_reset_device - perform a USB port reset to reinitialize a device |
| 3008 | * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) | 3008 | * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) |
| 3009 | * | 3009 | * |
| 3010 | * WARNING - don't reset any device unless drivers for all of its | 3010 | * WARNING - don't use this routine to reset a composite device |
| 3011 | * interfaces are expecting that reset! Maybe some driver->reset() | 3011 | * (one with multiple interfaces owned by separate drivers)! |
| 3012 | * method should eventually help ensure sufficient cooperation. | 3012 | * Use usb_reset_composite_device() instead. |
| 3013 | * | 3013 | * |
| 3014 | * Do a port reset, reassign the device's address, and establish its | 3014 | * Do a port reset, reassign the device's address, and establish its |
| 3015 | * former operating configuration. If the reset fails, or the device's | 3015 | * former operating configuration. If the reset fails, or the device's |
| @@ -3125,3 +3125,81 @@ re_enumerate: | |||
| 3125 | hub_port_logical_disconnect(parent_hub, port1); | 3125 | hub_port_logical_disconnect(parent_hub, port1); |
| 3126 | return -ENODEV; | 3126 | return -ENODEV; |
| 3127 | } | 3127 | } |
| 3128 | |||
| 3129 | /** | ||
| 3130 | * usb_reset_composite_device - warn interface drivers and perform a USB port reset | ||
| 3131 | * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) | ||
| 3132 | * @iface: interface bound to the driver making the request (optional) | ||
| 3133 | * | ||
| 3134 | * Warns all drivers bound to registered interfaces (using their pre_reset | ||
| 3135 | * method), performs the port reset, and then lets the drivers know that | ||
| 3136 | * the reset is over (using their post_reset method). | ||
| 3137 | * | ||
| 3138 | * Return value is the same as for usb_reset_device(). | ||
| 3139 | * | ||
| 3140 | * The caller must own the device lock. For example, it's safe to use | ||
| 3141 | * this from a driver probe() routine after downloading new firmware. | ||
| 3142 | * For calls that might not occur during probe(), drivers should lock | ||
| 3143 | * the device using usb_lock_device_for_reset(). | ||
| 3144 | * | ||
| 3145 | * The interface locks are acquired during the pre_reset stage and released | ||
| 3146 | * during the post_reset stage. However if iface is not NULL and is | ||
| 3147 | * currently being probed, we assume that the caller already owns its | ||
| 3148 | * lock. | ||
| 3149 | */ | ||
| 3150 | int usb_reset_composite_device(struct usb_device *udev, | ||
| 3151 | struct usb_interface *iface) | ||
| 3152 | { | ||
| 3153 | int ret; | ||
| 3154 | struct usb_host_config *config = udev->actconfig; | ||
| 3155 | |||
| 3156 | if (udev->state == USB_STATE_NOTATTACHED || | ||
| 3157 | udev->state == USB_STATE_SUSPENDED) { | ||
| 3158 | dev_dbg(&udev->dev, "device reset not allowed in state %d\n", | ||
| 3159 | udev->state); | ||
| 3160 | return -EINVAL; | ||
| 3161 | } | ||
| 3162 | |||
| 3163 | if (iface && iface->condition != USB_INTERFACE_BINDING) | ||
| 3164 | iface = NULL; | ||
| 3165 | |||
| 3166 | if (config) { | ||
| 3167 | int i; | ||
| 3168 | struct usb_interface *cintf; | ||
| 3169 | struct usb_driver *drv; | ||
| 3170 | |||
| 3171 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { | ||
| 3172 | cintf = config->interface[i]; | ||
| 3173 | if (cintf != iface) | ||
| 3174 | down(&cintf->dev.sem); | ||
| 3175 | if (device_is_registered(&cintf->dev) && | ||
| 3176 | cintf->dev.driver) { | ||
| 3177 | drv = to_usb_driver(cintf->dev.driver); | ||
| 3178 | if (drv->pre_reset) | ||
| 3179 | (drv->pre_reset)(cintf); | ||
| 3180 | } | ||
| 3181 | } | ||
| 3182 | } | ||
| 3183 | |||
| 3184 | ret = usb_reset_device(udev); | ||
| 3185 | |||
| 3186 | if (config) { | ||
| 3187 | int i; | ||
| 3188 | struct usb_interface *cintf; | ||
| 3189 | struct usb_driver *drv; | ||
| 3190 | |||
| 3191 | for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { | ||
| 3192 | cintf = config->interface[i]; | ||
| 3193 | if (device_is_registered(&cintf->dev) && | ||
| 3194 | cintf->dev.driver) { | ||
| 3195 | drv = to_usb_driver(cintf->dev.driver); | ||
| 3196 | if (drv->post_reset) | ||
| 3197 | (drv->post_reset)(cintf); | ||
| 3198 | } | ||
| 3199 | if (cintf != iface) | ||
| 3200 | up(&cintf->dev.sem); | ||
| 3201 | } | ||
| 3202 | } | ||
| 3203 | |||
| 3204 | return ret; | ||
| 3205 | } | ||
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b7fdc1cd134a..515310751303 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
| @@ -1207,6 +1207,7 @@ EXPORT_SYMBOL(usb_ifnum_to_if); | |||
| 1207 | EXPORT_SYMBOL(usb_altnum_to_altsetting); | 1207 | EXPORT_SYMBOL(usb_altnum_to_altsetting); |
| 1208 | 1208 | ||
| 1209 | EXPORT_SYMBOL(usb_reset_device); | 1209 | EXPORT_SYMBOL(usb_reset_device); |
| 1210 | EXPORT_SYMBOL(usb_reset_composite_device); | ||
| 1210 | 1211 | ||
| 1211 | EXPORT_SYMBOL(__usb_get_extra_descriptor); | 1212 | EXPORT_SYMBOL(__usb_get_extra_descriptor); |
| 1212 | 1213 | ||
diff --git a/include/linux/usb.h b/include/linux/usb.h index 317ec9f28bce..5ad30cefe7b2 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
| @@ -386,6 +386,8 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, | |||
| 386 | 386 | ||
| 387 | /* USB port reset for device reinitialization */ | 387 | /* USB port reset for device reinitialization */ |
| 388 | extern int usb_reset_device(struct usb_device *dev); | 388 | extern int usb_reset_device(struct usb_device *dev); |
| 389 | extern int usb_reset_composite_device(struct usb_device *dev, | ||
| 390 | struct usb_interface *iface); | ||
| 389 | 391 | ||
| 390 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); | 392 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); |
| 391 | 393 | ||
| @@ -554,6 +556,10 @@ struct usb_dynids { | |||
| 554 | * do (or don't) show up otherwise in the filesystem. | 556 | * do (or don't) show up otherwise in the filesystem. |
| 555 | * @suspend: Called when the device is going to be suspended by the system. | 557 | * @suspend: Called when the device is going to be suspended by the system. |
| 556 | * @resume: Called when the device is being resumed by the system. | 558 | * @resume: Called when the device is being resumed by the system. |
| 559 | * @pre_reset: Called by usb_reset_composite_device() when the device | ||
| 560 | * is about to be reset. | ||
| 561 | * @post_reset: Called by usb_reset_composite_device() after the device | ||
| 562 | * has been reset. | ||
| 557 | * @id_table: USB drivers use ID table to support hotplugging. | 563 | * @id_table: USB drivers use ID table to support hotplugging. |
| 558 | * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set | 564 | * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set |
| 559 | * or your driver's probe function will never get called. | 565 | * or your driver's probe function will never get called. |
| @@ -592,6 +598,9 @@ struct usb_driver { | |||
| 592 | int (*suspend) (struct usb_interface *intf, pm_message_t message); | 598 | int (*suspend) (struct usb_interface *intf, pm_message_t message); |
| 593 | int (*resume) (struct usb_interface *intf); | 599 | int (*resume) (struct usb_interface *intf); |
| 594 | 600 | ||
| 601 | void (*pre_reset) (struct usb_interface *intf); | ||
| 602 | void (*post_reset) (struct usb_interface *intf); | ||
| 603 | |||
| 595 | const struct usb_device_id *id_table; | 604 | const struct usb_device_id *id_table; |
| 596 | 605 | ||
| 597 | struct usb_dynids dynids; | 606 | struct usb_dynids dynids; |
