Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 354545
b: refs/heads/master
c: a24a607
h: refs/heads/master
i:
  354543: 4e6f6ee
v: v3
  • Loading branch information
Sarah Sharp committed Jan 3, 2013
1 parent 8262e0a commit d8c5c78
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 83 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2d4fa940f99663c82ba55b2244638833b388e4e2
refs/heads/master: a24a6078754f28528bc91e7e7b3e6ae86bd936d8
150 changes: 68 additions & 82 deletions trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -2538,73 +2538,35 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if ((portstatus & USB_PORT_STAT_RESET))
goto delay;

/*
* Some buggy devices require a warm reset to be issued even
* when the port appears not to be connected.
if (hub_port_warm_reset_required(hub, portstatus))
return -ENOTCONN;

/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return -ENOTCONN;

/* bomb out completely if the connection bounced. A USB 3.0
* connection may bounce if multiple warm resets were issued,
* but the device may have successfully re-connected. Ignore it.
*/
if (!warm) {
/*
* Some buggy devices can cause an NEC host controller
* to transition to the "Error" state after a hot port
* reset. This will show up as the port state in
* "Inactive", and the port may also report a
* disconnect. Forcing a warm port reset seems to make
* the device work.
*
* See https://bugzilla.kernel.org/show_bug.cgi?id=41752
*/
if (hub_port_warm_reset_required(hub, portstatus)) {
int ret;

if ((portchange & USB_PORT_STAT_C_CONNECTION))
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
if (portchange & USB_PORT_STAT_C_LINK_STATE)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
if (portchange & USB_PORT_STAT_C_RESET)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_RESET);
dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
port1);
ret = hub_port_reset(hub, port1,
udev, HUB_BH_RESET_TIME,
true);
if ((portchange & USB_PORT_STAT_C_CONNECTION))
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
return ret;
}
/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
return -ENOTCONN;

/* bomb out completely if the connection bounced */
if ((portchange & USB_PORT_STAT_C_CONNECTION))
return -ENOTCONN;

if ((portstatus & USB_PORT_STAT_ENABLE)) {
if (!udev)
return 0;

if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
if (!hub_is_superspeed(hub->hdev) &&
(portchange & USB_PORT_STAT_C_CONNECTION))
return -ENOTCONN;

if ((portstatus & USB_PORT_STAT_ENABLE)) {
if (!udev)
return 0;
}
} else {
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
hub_port_warm_reset_required(hub,
portstatus))
return -ENOTCONN;

if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
return 0;
}

Expand All @@ -2622,23 +2584,21 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
}

static void hub_port_finish_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, int *status, bool warm)
struct usb_device *udev, int *status)
{
switch (*status) {
case 0:
if (!warm) {
struct usb_hcd *hcd;
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
if (udev) {
update_devnum(udev, 0);
hcd = bus_to_hcd(udev->bus);
/* The xHC may think the device is already
* reset, so ignore the status.
*/
if (hcd->driver->reset_device)
hcd->driver->reset_device(hcd, udev);
}
/* TRSTRCY = 10 ms; plus some extra */
msleep(10 + 40);
if (udev) {
struct usb_hcd *hcd = bus_to_hcd(udev->bus);

update_devnum(udev, 0);
/* The xHC may think the device is already reset,
* so ignore the status.
*/
if (hcd->driver->reset_device)
hcd->driver->reset_device(hcd, udev);
}
/* FALL THROUGH */
case -ENOTCONN:
Expand All @@ -2651,8 +2611,10 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
if (!warm && udev)
if (udev)
usb_set_device_state(udev, *status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT);
Expand All @@ -2665,6 +2627,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm)
{
int i, status;
u16 portchange, portstatus;

if (!hub_is_superspeed(hub->hdev)) {
if (warm) {
Expand Down Expand Up @@ -2696,10 +2659,33 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
status);
}

/* return on disconnect or reset */
/* Check for disconnect or reset */
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
hub_port_finish_reset(hub, port1, udev, &status, warm);
goto done;
hub_port_finish_reset(hub, port1, udev, &status);

if (!hub_is_superspeed(hub->hdev))
goto done;

/*
* If a USB 3.0 device migrates from reset to an error
* state, re-issue the warm reset.
*/
if (hub_port_status(hub, port1,
&portstatus, &portchange) < 0)
goto done;

if (!hub_port_warm_reset_required(hub, portstatus))
goto done;

/*
* If the port is in SS.Inactive or Compliance Mode, the
* hot or warm reset failed. Try another warm reset.
*/
if (!warm) {
dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
port1);
warm = true;
}
}

dev_dbg (hub->intfdev,
Expand Down

0 comments on commit d8c5c78

Please sign in to comment.