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; |