aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c383
1 files changed, 246 insertions, 137 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a815fd2cc5e7..b2d007565180 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -836,7 +836,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
unsigned delay;
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
/* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
* USB 2.0 hubs. Some hubs do not implement port-power switching
@@ -877,6 +876,60 @@ static int hub_hub_status(struct usb_hub *hub,
return ret;
}
+static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+ unsigned int link_status)
+{
+ return set_port_feature(hub->hdev,
+ port1 | (link_status << 3),
+ USB_PORT_FEAT_LINK_STATE);
+}
+
+/*
+ * If USB 3.0 ports are placed into the Disabled state, they will no longer
+ * detect any device connects or disconnects. This is generally not what the
+ * USB core wants, since it expects a disabled port to produce a port status
+ * change event when a new device connects.
+ *
+ * Instead, set the link state to Disabled, wait for the link to settle into
+ * that state, clear any change bits, and then put the port into the RxDetect
+ * state.
+ */
+static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
+{
+ int ret;
+ int total_time;
+ u16 portchange, portstatus;
+
+ if (!hub_is_superspeed(hub->hdev))
+ return -EINVAL;
+
+ ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+ if (ret) {
+ dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+ port1, ret);
+ return ret;
+ }
+
+ /* Wait for the link to enter the disabled state. */
+ for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+ ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (ret < 0)
+ return ret;
+
+ if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_SS_DISABLED)
+ break;
+ if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+ break;
+ msleep(HUB_DEBOUNCE_STEP);
+ }
+ if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+ dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n",
+ port1, total_time);
+
+ return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+}
+
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
struct usb_device *hdev = hub->hdev;
@@ -885,8 +938,13 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
if (hub->ports[port1 - 1]->child && set_state)
usb_set_device_state(hub->ports[port1 - 1]->child,
USB_STATE_NOTATTACHED);
- if (!hub->error && !hub_is_superspeed(hub->hdev))
- ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev))
+ ret = hub_usb3_port_disable(hub, port1);
+ else
+ ret = clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
if (ret)
dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
port1, ret);
@@ -960,7 +1018,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
int status;
bool need_debounce_delay = false;
unsigned delay;
-
/* Continue a partial initialization */
if (type == HUB_INIT2)
goto init2;
@@ -1041,7 +1098,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hub->ports[port1 - 1]->child;
u16 portstatus, portchange;
-
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
@@ -1327,7 +1383,7 @@ static int hub_configure(struct usb_hub *hub,
}
hdev->maxchild = hub->descriptor->bNbrPorts;
- dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
+ dev_dbg (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
@@ -1338,7 +1394,6 @@ static int hub_configure(struct usb_hub *hub,
}
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
/* FIXME for USB 3.0, skip for now */
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
@@ -1351,9 +1406,9 @@ static int hub_configure(struct usb_hub *hub,
? 'F' : 'R';
portstr[hdev->maxchild] = 0;
dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
- } else
+ } else {
dev_dbg(hub_dev, "standalone hub\n");
-
+ }
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
case HUB_CHAR_COMMON_LPSM:
dev_dbg(hub_dev, "ganged power switching\n");
@@ -1684,7 +1739,7 @@ descriptor_error:
goto descriptor_error;
/* We found a hub */
- dev_info (&intf->dev, "USB hub found\n");
+ dev_dbg (&intf->dev, "USB hub found\n");
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
@@ -1711,7 +1766,6 @@ descriptor_error:
if (hub_configure(hub, endpoint) >= 0)
return 0;
-
hub_disconnect (intf);
return -ENODEV;
}
@@ -2003,7 +2057,7 @@ void usb_disconnect(struct usb_device **pdev)
* this quiesces everything except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
- dev_info(&udev->dev, "USB disconnect, device number %d\n",
+ dev_dbg(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
usb_lock_device(udev);
@@ -2056,10 +2110,10 @@ static void show_string(struct usb_device *udev, char *id, char *string)
static void announce_device(struct usb_device *udev)
{
- dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
+ dev_dbg(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
@@ -2105,7 +2159,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port1 = udev->portnum;
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
? "" : "non-");
@@ -2124,7 +2178,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
/* OTG MESSAGE: report errors here,
* customize to match your product.
*/
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"can't set HNP mode: %d\n",
err);
bus->b_hnp_enable = 0;
@@ -2407,7 +2461,7 @@ int usb_authorize_device(struct usb_device *usb_dev)
* set other configurations. */
}
}
- dev_info(&usb_dev->dev, "authorized to connect\n");
+ dev_dbg(&usb_dev->dev, "authorized to connect\n");
error_enumerate:
error_device_descriptor:
@@ -2440,7 +2494,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define HUB_SHORT_RESET_TIME 10
#define HUB_BH_RESET_TIME 50
#define HUB_LONG_RESET_TIME 200
-#define HUB_RESET_TIMEOUT 500
+#define HUB_RESET_TIMEOUT 800
static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm);
@@ -2475,73 +2529,43 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /*
- * Some buggy devices require a warm reset to be issued even
- * when the port appears not to be connected.
- */
- 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;
+ /* The port state is unknown until the reset completes. */
+ if ((portstatus & USB_PORT_STAT_RESET))
+ goto delay;
- /* bomb out completely if the connection bounced */
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- return -ENOTCONN;
+ if (hub_port_warm_reset_required(hub, portstatus))
+ return -ENOTCONN;
- /* if we`ve finished resetting, then break out of
- * the loop
- */
- if (!(portstatus & USB_PORT_STAT_RESET) &&
- (portstatus & USB_PORT_STAT_ENABLE)) {
- 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;
- }
- } else {
- if (portchange & USB_PORT_STAT_C_BH_RESET)
+ /* 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 (!hub_is_superspeed(hub->hdev) &&
+ (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;
+ return 0;
}
+delay:
/* switch to the long delay after two short delay failures */
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
@@ -2555,41 +2579,39 @@ 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);
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+ if (udev) {
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
update_devnum(udev, 0);
- hcd = bus_to_hcd(udev->bus);
- if (hcd->driver->reset_device) {
- *status = hcd->driver->reset_device(hcd, udev);
- if (*status < 0) {
- dev_err(&udev->dev, "Cannot reset "
- "HCD device state\n");
- break;
- }
- }
+ /* 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:
case -ENODEV:
clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_C_RESET);
- /* FIXME need disconnect() for NOTATTACHED device */
- if (warm) {
+ if (hub_is_superspeed(hub->hdev)) {
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
- } else {
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ }
+ if (udev)
usb_set_device_state(udev, *status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT);
- }
break;
}
}
@@ -2599,18 +2621,30 @@ 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 (!warm) {
- /* Block EHCI CF initialization during the port reset.
- * Some companion controllers don't like it when they mix.
- */
- down_read(&ehci_cf_port_reset_rwsem);
- } else {
- if (!hub_is_superspeed(hub->hdev)) {
+ if (!hub_is_superspeed(hub->hdev)) {
+ if (warm) {
dev_err(hub->intfdev, "only USB3 hub support "
"warm reset\n");
return -EINVAL;
}
+ /* Block EHCI CF initialization during the port reset.
+ * Some companion controllers don't like it when they mix.
+ */
+ down_read(&ehci_cf_port_reset_rwsem);
+ } else if (!warm) {
+ /*
+ * If the caller hasn't explicitly requested a warm reset,
+ * double check and see if one is needed.
+ */
+ status = hub_port_status(hub, port1,
+ &portstatus, &portchange);
+ if (status < 0)
+ goto done;
+
+ if (hub_port_warm_reset_required(hub, portstatus))
+ warm = true;
}
/* Reset the port */
@@ -2631,10 +2665,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,
@@ -2648,7 +2705,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
port1);
done:
- if (!warm)
+ if (!hub_is_superspeed(hub->hdev))
up_read(&ehci_cf_port_reset_rwsem);
return status;
@@ -2777,6 +2834,23 @@ void usb_enable_ltm(struct usb_device *udev)
EXPORT_SYMBOL_GPL(usb_enable_ltm);
#ifdef CONFIG_USB_SUSPEND
+/*
+ * usb_disable_function_remotewakeup - disable usb3.0
+ * device's function remote wakeup
+ * @udev: target device
+ *
+ * Assume there's only one function on the USB 3.0
+ * device and disable remote wake for the first
+ * interface. FIXME if the interface association
+ * descriptor shows there's more than one function.
+ */
+static int usb_disable_function_remotewakeup(struct usb_device *udev)
+{
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
+ USB_INTRF_FUNC_SUSPEND, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+}
/*
* usb_port_suspend - suspend a usb device's upstream port
@@ -2894,12 +2968,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
/* paranoia: "should not happen" */
- if (udev->do_remote_wakeup)
- (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ if (udev->do_remote_wakeup) {
+ if (!hub_is_superspeed(hub->hdev)) {
+ (void) usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP, 0,
+ NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ } else
+ (void) usb_disable_function_remotewakeup(udev);
+
+ }
/* Try to enable USB2 hardware LPM again */
if (udev->usb2_hw_lpm_capable == 1)
@@ -2939,7 +3020,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
static int finish_port_resume(struct usb_device *udev)
{
int status = 0;
- u16 devstatus;
+ u16 devstatus = 0;
/* caller owns the udev device lock */
dev_dbg(&udev->dev, "%s\n",
@@ -2984,21 +3065,37 @@ static int finish_port_resume(struct usb_device *udev)
if (status) {
dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
status);
- } else if (udev->actconfig) {
- le16_to_cpus(&devstatus);
- if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
- status = usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE,
+ /*
+ * There are a few quirky devices which violate the standard
+ * by claiming to have remote wakeup enabled after a reset,
+ * which crash if the feature is cleared, hence check for
+ * udev->reset_resume
+ */
+ } else if (udev->actconfig && !udev->reset_resume) {
+ if (!hub_is_superspeed(udev->parent)) {
+ le16_to_cpus(&devstatus);
+ if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
+ status = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE,
USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
- if (status)
- dev_dbg(&udev->dev,
- "disable remote wakeup, status %d\n",
- status);
+ USB_DEVICE_REMOTE_WAKEUP, 0,
+ NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ } else {
+ status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
+ &devstatus);
+ le16_to_cpus(&devstatus);
+ if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
+ | USB_INTRF_STAT_FUNC_RW))
+ status =
+ usb_disable_function_remotewakeup(udev);
}
+
+ if (status)
+ dev_dbg(&udev->dev,
+ "disable remote wakeup, status %d\n",
+ status);
status = 0;
}
return status;
@@ -3894,7 +3991,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
speed = usb_speed_string(udev->speed);
if (udev->speed != USB_SPEED_SUPER)
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"%s %s USB device number %d using %s\n",
(udev->config) ? "reset" : "new", speed,
devnum, udev->bus->controller->driver->name);
@@ -4008,7 +4105,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (udev->speed == USB_SPEED_SUPER) {
devnum = udev->devnum;
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"%s SuperSpeed USB device number %d using %s\n",
(udev->config) ? "reset" : "new",
devnum, udev->bus->controller->driver->name);
@@ -4120,7 +4217,7 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,
qual, sizeof *qual);
if (status == sizeof *qual) {
- dev_info(&udev->dev, "not running at top speed; "
+ dev_dbg(&udev->dev, "not running at top speed; "
"connect to a high speed hub\n");
/* hub LEDs are probably harder to miss than syslog */
if (hub->has_indicators) {
@@ -4189,7 +4286,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev;
int status, i;
-
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed(hub, portstatus));
@@ -4538,7 +4634,6 @@ static void hub_events(void)
hub->nerrors = 0;
hub->error = 0;
}
-
/* deal with port status changes */
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
if (test_bit(i, hub->busy_bits))
@@ -4638,9 +4733,23 @@ static void hub_events(void)
* SS.Inactive state.
*/
if (hub_port_warm_reset_required(hub, portstatus)) {
+ int status;
+ struct usb_device *udev =
+ hub->ports[i - 1]->child;
+
dev_dbg(hub_dev, "warm reset port %d\n", i);
- hub_port_reset(hub, i, NULL,
- HUB_BH_RESET_TIME, true);
+ if (!udev) {
+ status = hub_port_reset(hub, i,
+ NULL, HUB_BH_RESET_TIME,
+ true);
+ if (status < 0)
+ hub_port_disable(hub, i, 1);
+ } else {
+ usb_lock_device(udev);
+ status = usb_reset_device(udev);
+ usb_unlock_device(udev);
+ }
+ connect_change = 0;
}
if (connect_change)
@@ -4936,7 +5045,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
- dev_info(&udev->dev, "device firmware changed\n");
+ dev_dbg(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
}