diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-12-31 11:31:33 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-07 13:00:13 -0500 |
commit | 2caf7fcdb8532045680f06b67b9e63f0c9613aaa (patch) | |
tree | d9faee328532a3badd73d20e175259e8bdea271a /drivers/usb/core | |
parent | df718962bf91c7bd345060aadaa24b03f6140b07 (diff) |
USB: re-enable interface after driver unbinds
This patch (as1197) fixes an error introduced recently. Since a
significant number of devices can't handle Set-Interface requests, we
no longer call usb_set_interface() when a driver unbinds from an
interface, provided the interface is already in altsetting 0. However
the interface still does get disabled, and the call to
usb_set_interface() was the only thing re-enabling it. Since the
interface doesn't get re-enabled, further attempts to use it fail.
So the patch adds a call to usb_enable_interface() when a driver
unbinds and the interface is in altsetting 0. For this to work
right, the interface's endpoints have to be re-enabled but their
toggles have to be left alone. Therefore an additional argument is
added to usb_enable_endpoint() and usb_enable_interface(), a flag
indicating whether or not the endpoint toggles should be reset.
This is a forward-ported version of a patch which fixes Bugzilla
#12301.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: David Roka <roka@dawid.hu>
Reported-by: Erik Ekman <erik@kryo.se>
Tested-by: Erik Ekman <erik@kryo.se>
Tested-by: Alon Bar-Lev <alon.barlev@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/driver.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 25 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 4 |
5 files changed, 26 insertions, 16 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 41c06025506e..98760553bc95 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -295,9 +295,12 @@ static int usb_unbind_interface(struct device *dev) | |||
295 | * altsetting means creating new endpoint device entries). | 295 | * altsetting means creating new endpoint device entries). |
296 | * When either of these happens, defer the Set-Interface. | 296 | * When either of these happens, defer the Set-Interface. |
297 | */ | 297 | */ |
298 | if (intf->cur_altsetting->desc.bAlternateSetting == 0) | 298 | if (intf->cur_altsetting->desc.bAlternateSetting == 0) { |
299 | ; /* Already in altsetting 0 so skip Set-Interface */ | 299 | /* Already in altsetting 0 so skip Set-Interface. |
300 | else if (!error && intf->dev.power.status == DPM_ON) | 300 | * Just re-enable it without affecting the endpoint toggles. |
301 | */ | ||
302 | usb_enable_interface(udev, intf, false); | ||
303 | } else if (!error && intf->dev.power.status == DPM_ON) | ||
301 | usb_set_interface(udev, intf->altsetting[0]. | 304 | usb_set_interface(udev, intf->altsetting[0]. |
302 | desc.bInterfaceNumber, 0); | 305 | desc.bInterfaceNumber, 0); |
303 | else | 306 | else |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 756b8d9993fc..d5d0e40b1e2d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -2384,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev) | |||
2384 | { | 2384 | { |
2385 | usb_disable_endpoint(udev, 0 + USB_DIR_IN); | 2385 | usb_disable_endpoint(udev, 0 + USB_DIR_IN); |
2386 | usb_disable_endpoint(udev, 0 + USB_DIR_OUT); | 2386 | usb_disable_endpoint(udev, 0 + USB_DIR_OUT); |
2387 | usb_enable_endpoint(udev, &udev->ep0); | 2387 | usb_enable_endpoint(udev, &udev->ep0, true); |
2388 | } | 2388 | } |
2389 | EXPORT_SYMBOL_GPL(usb_ep0_reinit); | 2389 | EXPORT_SYMBOL_GPL(usb_ep0_reinit); |
2390 | 2390 | ||
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5589686981f1..de51667dd64d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c | |||
@@ -1143,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) | |||
1143 | * usb_enable_endpoint - Enable an endpoint for USB communications | 1143 | * usb_enable_endpoint - Enable an endpoint for USB communications |
1144 | * @dev: the device whose interface is being enabled | 1144 | * @dev: the device whose interface is being enabled |
1145 | * @ep: the endpoint | 1145 | * @ep: the endpoint |
1146 | * @reset_toggle: flag to set the endpoint's toggle back to 0 | ||
1146 | * | 1147 | * |
1147 | * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. | 1148 | * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers. |
1148 | * For control endpoints, both the input and output sides are handled. | 1149 | * For control endpoints, both the input and output sides are handled. |
1149 | */ | 1150 | */ |
1150 | void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) | 1151 | void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, |
1152 | bool reset_toggle) | ||
1151 | { | 1153 | { |
1152 | int epnum = usb_endpoint_num(&ep->desc); | 1154 | int epnum = usb_endpoint_num(&ep->desc); |
1153 | int is_out = usb_endpoint_dir_out(&ep->desc); | 1155 | int is_out = usb_endpoint_dir_out(&ep->desc); |
1154 | int is_control = usb_endpoint_xfer_control(&ep->desc); | 1156 | int is_control = usb_endpoint_xfer_control(&ep->desc); |
1155 | 1157 | ||
1156 | if (is_out || is_control) { | 1158 | if (is_out || is_control) { |
1157 | usb_settoggle(dev, epnum, 1, 0); | 1159 | if (reset_toggle) |
1160 | usb_settoggle(dev, epnum, 1, 0); | ||
1158 | dev->ep_out[epnum] = ep; | 1161 | dev->ep_out[epnum] = ep; |
1159 | } | 1162 | } |
1160 | if (!is_out || is_control) { | 1163 | if (!is_out || is_control) { |
1161 | usb_settoggle(dev, epnum, 0, 0); | 1164 | if (reset_toggle) |
1165 | usb_settoggle(dev, epnum, 0, 0); | ||
1162 | dev->ep_in[epnum] = ep; | 1166 | dev->ep_in[epnum] = ep; |
1163 | } | 1167 | } |
1164 | ep->enabled = 1; | 1168 | ep->enabled = 1; |
@@ -1168,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) | |||
1168 | * usb_enable_interface - Enable all the endpoints for an interface | 1172 | * usb_enable_interface - Enable all the endpoints for an interface |
1169 | * @dev: the device whose interface is being enabled | 1173 | * @dev: the device whose interface is being enabled |
1170 | * @intf: pointer to the interface descriptor | 1174 | * @intf: pointer to the interface descriptor |
1175 | * @reset_toggles: flag to set the endpoints' toggles back to 0 | ||
1171 | * | 1176 | * |
1172 | * Enables all the endpoints for the interface's current altsetting. | 1177 | * Enables all the endpoints for the interface's current altsetting. |
1173 | */ | 1178 | */ |
1174 | static void usb_enable_interface(struct usb_device *dev, | 1179 | void usb_enable_interface(struct usb_device *dev, |
1175 | struct usb_interface *intf) | 1180 | struct usb_interface *intf, bool reset_toggles) |
1176 | { | 1181 | { |
1177 | struct usb_host_interface *alt = intf->cur_altsetting; | 1182 | struct usb_host_interface *alt = intf->cur_altsetting; |
1178 | int i; | 1183 | int i; |
1179 | 1184 | ||
1180 | for (i = 0; i < alt->desc.bNumEndpoints; ++i) | 1185 | for (i = 0; i < alt->desc.bNumEndpoints; ++i) |
1181 | usb_enable_endpoint(dev, &alt->endpoint[i]); | 1186 | usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles); |
1182 | } | 1187 | } |
1183 | 1188 | ||
1184 | /** | 1189 | /** |
@@ -1303,7 +1308,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) | |||
1303 | * during the SETUP stage - hence EP0 toggles are "don't care" here. | 1308 | * during the SETUP stage - hence EP0 toggles are "don't care" here. |
1304 | * (Likewise, EP0 never "halts" on well designed devices.) | 1309 | * (Likewise, EP0 never "halts" on well designed devices.) |
1305 | */ | 1310 | */ |
1306 | usb_enable_interface(dev, iface); | 1311 | usb_enable_interface(dev, iface, true); |
1307 | if (device_is_registered(&iface->dev)) { | 1312 | if (device_is_registered(&iface->dev)) { |
1308 | usb_create_sysfs_intf_files(iface); | 1313 | usb_create_sysfs_intf_files(iface); |
1309 | create_intf_ep_devs(iface); | 1314 | create_intf_ep_devs(iface); |
@@ -1382,7 +1387,7 @@ int usb_reset_configuration(struct usb_device *dev) | |||
1382 | usb_remove_sysfs_intf_files(intf); | 1387 | usb_remove_sysfs_intf_files(intf); |
1383 | } | 1388 | } |
1384 | intf->cur_altsetting = alt; | 1389 | intf->cur_altsetting = alt; |
1385 | usb_enable_interface(dev, intf); | 1390 | usb_enable_interface(dev, intf, true); |
1386 | if (device_is_registered(&intf->dev)) { | 1391 | if (device_is_registered(&intf->dev)) { |
1387 | usb_create_sysfs_intf_files(intf); | 1392 | usb_create_sysfs_intf_files(intf); |
1388 | create_intf_ep_devs(intf); | 1393 | create_intf_ep_devs(intf); |
@@ -1685,7 +1690,7 @@ free_interfaces: | |||
1685 | alt = &intf->altsetting[0]; | 1690 | alt = &intf->altsetting[0]; |
1686 | 1691 | ||
1687 | intf->cur_altsetting = alt; | 1692 | intf->cur_altsetting = alt; |
1688 | usb_enable_interface(dev, intf); | 1693 | usb_enable_interface(dev, intf, true); |
1689 | intf->dev.parent = &dev->dev; | 1694 | intf->dev.parent = &dev->dev; |
1690 | intf->dev.driver = NULL; | 1695 | intf->dev.driver = NULL; |
1691 | intf->dev.bus = &usb_bus_type; | 1696 | intf->dev.bus = &usb_bus_type; |
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index c0821564a3fe..dcfc072630c1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
@@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, | |||
362 | dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; | 362 | dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; |
363 | dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; | 363 | dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; |
364 | /* ep0 maxpacket comes later, from device descriptor */ | 364 | /* ep0 maxpacket comes later, from device descriptor */ |
365 | usb_enable_endpoint(dev, &dev->ep0); | 365 | usb_enable_endpoint(dev, &dev->ep0, true); |
366 | dev->can_submit = 1; | 366 | dev->can_submit = 1; |
367 | 367 | ||
368 | /* Save readable and stable topology id, distinguishing devices | 368 | /* Save readable and stable topology id, distinguishing devices |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 381eae90c3b7..386177867a8a 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -12,7 +12,9 @@ extern int usb_create_ep_devs(struct device *parent, | |||
12 | extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint); | 12 | extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint); |
13 | 13 | ||
14 | extern void usb_enable_endpoint(struct usb_device *dev, | 14 | extern void usb_enable_endpoint(struct usb_device *dev, |
15 | struct usb_host_endpoint *ep); | 15 | struct usb_host_endpoint *ep, bool reset_toggle); |
16 | extern void usb_enable_interface(struct usb_device *dev, | ||
17 | struct usb_interface *intf, bool reset_toggles); | ||
16 | extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); | 18 | extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); |
17 | extern void usb_disable_interface(struct usb_device *dev, | 19 | extern void usb_disable_interface(struct usb_device *dev, |
18 | struct usb_interface *intf); | 20 | struct usb_interface *intf); |