Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  hub.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * USB hub driver.
 *
 * (C) Copyright 1999 Linus Torvalds
 * (C) Copyright 1999 Johannes Erdfelt
 * (C) Copyright 1999 Gregory P. Smith
 * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
 *
 * Released under the GPLv2 only.
 */


#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/kcov.h>
#include <linux/ioctl.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
#include <linux/usb/onboard_dev.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/random.h>
#include <linux/pm_qos.h>
#include <linux/kobject.h>

#include <linux/bitfield.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>

#include "hub.h"
#include "phy.h"
#include "otg_productlist.h"

#define USB_VENDOR_GENESYS_LOGIC  0x05e3
#define USB_VENDOR_SMSC    0x0424
#define USB_PRODUCT_USB5534B   0x5534
#define USB_VENDOR_CYPRESS   0x04b4
#define USB_PRODUCT_CY7C65632   0x6570
#define USB_VENDOR_TEXAS_INSTRUMENTS  0x0451
#define USB_PRODUCT_TUSB8041_USB3  0x8140
#define USB_PRODUCT_TUSB8041_USB2  0x8142
#define USB_VENDOR_MICROCHIP   0x0424
#define USB_PRODUCT_USB4913   0x4913
#define USB_PRODUCT_USB4914   0x4914
#define USB_PRODUCT_USB4915   0x4915
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND BIT(0)
#define HUB_QUIRK_DISABLE_AUTOSUSPEND  BIT(1)
#define HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL BIT(2)

#define USB_TP_TRANSMISSION_DELAY 40 /* ns */
#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */
#define USB_PING_RESPONSE_TIME  400 /* ns */
#define USB_REDUCE_FRAME_INTR_BINTERVAL 9

/*
 * The SET_ADDRESS request timeout will be 500 ms when
 * USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT quirk flag is set.
 */

#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500  /* ms */

/*
 * Give SS hubs 200ms time after wake to train downstream links before
 * assuming no port activity and allowing hub to runtime suspend back.
 */

#define USB_SS_PORT_U0_WAKE_TIME 200  /* ms */

/* Protect struct usb_device->state and ->children members
 * Note: Both are also protected by ->dev.sem, except that ->state can
 * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */

static DEFINE_SPINLOCK(device_state_lock);

/* workqueue to process hub events */
static struct workqueue_struct *hub_wq;
static void hub_event(struct work_struct *work);

/* synchronize hub-port add/remove and peering operations */
DEFINE_MUTEX(usb_port_peer_mutex);

/* cycle leds on hubs that aren't blinking for attention */
static bool blinkenlights;
module_param(blinkenlights, bool, S_IRUGO);
MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs");

/*
 * Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about
 * 10 seconds to send reply for the initial 64-byte descriptor request.
 */

/* define initial 64-byte descriptor request timeout in milliseconds */
static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT;
module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(initial_descriptor_timeout,
  "initial 64-byte descriptor request timeout in milliseconds "
  "(default 5000 - 5.0 seconds)");

/*
 * As of 2.6.10 we introduce a new USB device initialization scheme which
 * closely resembles the way Windows works.  Hopefully it will be compatible
 * with a wider range of devices than the old scheme.  However some previously
 * working devices may start giving rise to "device not accepting address"
 * errors; if that happens the user can try the old scheme by adjusting the
 * following module parameters.
 *
 * For maximum flexibility there are two boolean parameters to control the
 * hub driver's behavior.  On the first initialization attempt, if the
 * "old_scheme_first" parameter is set then the old scheme will be used,
 * otherwise the new scheme is used.  If that fails and "use_both_schemes"
 * is set, then the driver will make another attempt, using the other scheme.
 */

static bool old_scheme_first;
module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(old_scheme_first,
   "start with the old device initialization scheme");

static bool use_both_schemes = true;
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
  "try the other device initialization scheme if the "
  "first one fails");

/* Mutual exclusion for EHCI CF initialization.  This interferes with
 * port reset on some companion controllers.
 */

DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);

#define HUB_DEBOUNCE_TIMEOUT 2000
#define HUB_DEBOUNCE_STEP   25
#define HUB_DEBOUNCE_STABLE  100

static int usb_reset_and_verify_device(struct usb_device *udev);
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
  u16 portstatus);

static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
 if (hub_is_superspeedplus(hub->hdev))
  return "10.0 Gb/s";
 if (hub_is_superspeed(hub->hdev))
  return "5.0 Gb/s";
 if (portstatus & USB_PORT_STAT_HIGH_SPEED)
  return "480 Mb/s";
 else if (portstatus & USB_PORT_STAT_LOW_SPEED)
  return "1.5 Mb/s";
 else
  return "12 Mb/s";
}

/* Note that hdev or one of its children must be locked! */
struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
{
 if (!hdev || !hdev->actconfig || !hdev->maxchild)
  return NULL;
 return usb_get_intfdata(hdev->actconfig->interface[0]);
}

int usb_device_supports_lpm(struct usb_device *udev)
{
 /* Some devices have trouble with LPM */
 if (udev->quirks & USB_QUIRK_NO_LPM)
  return 0;

 /* Skip if the device BOS descriptor couldn't be read */
 if (!udev->bos)
  return 0;

 /* USB 2.1 (and greater) devices indicate LPM support through
 * their USB 2.0 Extended Capabilities BOS descriptor.
 */

 if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) {
  if (udev->bos->ext_cap &&
   (USB_LPM_SUPPORT &
    le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
   return 1;
  return 0;
 }

 /*
 * According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
 * However, there are some that don't, and they set the U1/U2 exit
 * latencies to zero.
 */

 if (!udev->bos->ss_cap) {
  dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
  return 0;
 }

 if (udev->bos->ss_cap->bU1devExitLat == 0 &&
   udev->bos->ss_cap->bU2DevExitLat == 0) {
  if (udev->parent)
   dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
  else
   dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
  return 0;
 }

 if (!udev->parent || udev->parent->lpm_capable)
  return 1;
 return 0;
}

/*
 * Set the Maximum Exit Latency (MEL) for the host to wakup up the path from
 * U1/U2, send a PING to the device and receive a PING_RESPONSE.
 * See USB 3.1 section C.1.5.2
 */

static void usb_set_lpm_mel(struct usb_device *udev,
  struct usb3_lpm_parameters *udev_lpm_params,
  unsigned int udev_exit_latency,
  struct usb_hub *hub,
  struct usb3_lpm_parameters *hub_lpm_params,
  unsigned int hub_exit_latency)
{
 unsigned int total_mel;

 /*
 * tMEL1. time to transition path from host to device into U0.
 * MEL for parent already contains the delay up to parent, so only add
 * the exit latency for the last link (pick the slower exit latency),
 * and the hub header decode latency. See USB 3.1 section C 2.2.1
 * Store MEL in nanoseconds
 */

 total_mel = hub_lpm_params->mel +
  max(udev_exit_latency, hub_exit_latency) * 1000 +
  hub->descriptor->u.ss.bHubHdrDecLat * 100;

 /*
 * tMEL2. Time to submit PING packet. Sum of tTPTransmissionDelay for
 * each link + wHubDelay for each hub. Add only for last link.
 * tMEL4, the time for PING_RESPONSE to traverse upstream is similar.
 * Multiply by 2 to include it as well.
 */

 total_mel += (__le16_to_cpu(hub->descriptor->u.ss.wHubDelay) +
        USB_TP_TRANSMISSION_DELAY) * 2;

 /*
 * tMEL3, tPingResponse. Time taken by device to generate PING_RESPONSE
 * after receiving PING. Also add 2100ns as stated in USB 3.1 C 1.5.2.4
 * to cover the delay if the PING_RESPONSE is queued behind a Max Packet
 * Size DP.
 * Note these delays should be added only once for the entire path, so
 * add them to the MEL of the device connected to the roothub.
 */

 if (!hub->hdev->parent)
  total_mel += USB_PING_RESPONSE_TIME + 2100;

 udev_lpm_params->mel = total_mel;
}

/*
 * Set the maximum Device to Host Exit Latency (PEL) for the device to initiate
 * a transition from either U1 or U2.
 */

static void usb_set_lpm_pel(struct usb_device *udev,
  struct usb3_lpm_parameters *udev_lpm_params,
  unsigned int udev_exit_latency,
  struct usb_hub *hub,
  struct usb3_lpm_parameters *hub_lpm_params,
  unsigned int hub_exit_latency,
  unsigned int port_to_port_exit_latency)
{
 unsigned int first_link_pel;
 unsigned int hub_pel;

 /*
 * First, the device sends an LFPS to transition the link between the
 * device and the parent hub into U0.  The exit latency is the bigger of
 * the device exit latency or the hub exit latency.
 */

 if (udev_exit_latency > hub_exit_latency)
  first_link_pel = udev_exit_latency * 1000;
 else
  first_link_pel = hub_exit_latency * 1000;

 /*
 * When the hub starts to receive the LFPS, there is a slight delay for
 * it to figure out that one of the ports is sending an LFPS.  Then it
 * will forward the LFPS to its upstream link.  The exit latency is the
 * delay, plus the PEL that we calculated for this hub.
 */

 hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel;

 /*
 * According to figure C-7 in the USB 3.0 spec, the PEL for this device
 * is the greater of the two exit latencies.
 */

 if (first_link_pel > hub_pel)
  udev_lpm_params->pel = first_link_pel;
 else
  udev_lpm_params->pel = hub_pel;
}

/*
 * Set the System Exit Latency (SEL) to indicate the total worst-case time from
 * when a device initiates a transition to U0, until when it will receive the
 * first packet from the host controller.
 *
 * Section C.1.5.1 describes the four components to this:
 *  - t1: device PEL
 *  - t2: time for the ERDY to make it from the device to the host.
 *  - t3: a host-specific delay to process the ERDY.
 *  - t4: time for the packet to make it from the host to the device.
 *
 * t3 is specific to both the xHCI host and the platform the host is integrated
 * into.  The Intel HW folks have said it's negligible, FIXME if a different
 * vendor says otherwise.
 */

static void usb_set_lpm_sel(struct usb_device *udev,
  struct usb3_lpm_parameters *udev_lpm_params)
{
 struct usb_device *parent;
 unsigned int num_hubs;
 unsigned int total_sel;

 /* t1 = device PEL */
 total_sel = udev_lpm_params->pel;
 /* How many external hubs are in between the device & the root port. */
 for (parent = udev->parent, num_hubs = 0; parent->parent;
   parent = parent->parent)
  num_hubs++;
 /* t2 = 2.1us + 250ns * (num_hubs - 1) */
 if (num_hubs > 0)
  total_sel += 2100 + 250 * (num_hubs - 1);

 /* t4 = 250ns * num_hubs */
 total_sel += 250 * num_hubs;

 udev_lpm_params->sel = total_sel;
}

static void usb_set_lpm_parameters(struct usb_device *udev)
{
 struct usb_hub *hub;
 unsigned int port_to_port_delay;
 unsigned int udev_u1_del;
 unsigned int udev_u2_del;
 unsigned int hub_u1_del;
 unsigned int hub_u2_del;

 if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER)
  return;

 /* Skip if the device BOS descriptor couldn't be read */
 if (!udev->bos)
  return;

 hub = usb_hub_to_struct_hub(udev->parent);
 /* It doesn't take time to transition the roothub into U0, since it
 * doesn't have an upstream link.
 */

 if (!hub)
  return;

 udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
 udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
 hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
 hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);

 usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
   hub, &udev->parent->u1_params, hub_u1_del);

 usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del,
   hub, &udev->parent->u2_params, hub_u2_del);

 /*
 * Appendix C, section C.2.2.2, says that there is a slight delay from
 * when the parent hub notices the downstream port is trying to
 * transition to U0 to when the hub initiates a U0 transition on its
 * upstream port.  The section says the delays are tPort2PortU1EL and
 * tPort2PortU2EL, but it doesn't define what they are.
 *
 * The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking
 * about the same delays.  Use the maximum delay calculations from those
 * sections.  For U1, it's tHubPort2PortExitLat, which is 1us max.  For
 * U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat.  I
 * assume the device exit latencies they are talking about are the hub
 * exit latencies.
 *
 * What do we do if the U2 exit latency is less than the U1 exit
 * latency?  It's possible, although not likely...
 */

 port_to_port_delay = 1;

 usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del,
   hub, &udev->parent->u1_params, hub_u1_del,
   port_to_port_delay);

 if (hub_u2_del > hub_u1_del)
  port_to_port_delay = 1 + hub_u2_del - hub_u1_del;
 else
  port_to_port_delay = 1 + hub_u1_del;

 usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del,
   hub, &udev->parent->u2_params, hub_u2_del,
   port_to_port_delay);

 /* Now that we've got PEL, calculate SEL. */
 usb_set_lpm_sel(udev, &udev->u1_params);
 usb_set_lpm_sel(udev, &udev->u2_params);
}

/* USB 2.0 spec Section 11.24.4.5 */
static int get_hub_descriptor(struct usb_device *hdev,
  struct usb_hub_descriptor *desc)
{
 int i, ret, size;
 unsigned dtype;

 if (hub_is_superspeed(hdev)) {
  dtype = USB_DT_SS_HUB;
  size = USB_DT_SS_HUB_SIZE;
 } else {
  dtype = USB_DT_HUB;
  size = sizeof(struct usb_hub_descriptor);
 }

 for (i = 0; i < 3; i++) {
  ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
   USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
   dtype << 8, 0, desc, size,
   USB_CTRL_GET_TIMEOUT);
  if (hub_is_superspeed(hdev)) {
   if (ret == size)
    return ret;
  } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) {
   /* Make sure we have the DeviceRemovable field. */
   size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1;
   if (ret < size)
    return -EMSGSIZE;
   return ret;
  }
 }
 return -EINVAL;
}

/*
 * USB 2.0 spec Section 11.24.2.1
 */

static int clear_hub_feature(struct usb_device *hdev, int feature)
{
 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
  USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, 1000);
}

/*
 * USB 2.0 spec Section 11.24.2.2
 */

int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
{
 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
  USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
  NULL, 0, 1000);
}

/*
 * USB 2.0 spec Section 11.24.2.13
 */

static int set_port_feature(struct usb_device *hdev, int port1, int feature)
{
 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
  USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
  NULL, 0, 1000);
}

static char *to_led_name(int selector)
{
 switch (selector) {
 case HUB_LED_AMBER:
  return "amber";
 case HUB_LED_GREEN:
  return "green";
 case HUB_LED_OFF:
  return "off";
 case HUB_LED_AUTO:
  return "auto";
 default:
  return "??";
 }
}

/*
 * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
 * for info about using port indicators
 */

static void set_port_led(struct usb_hub *hub, int port1, int selector)
{
 struct usb_port *port_dev = hub->ports[port1 - 1];
 int status;

 status = set_port_feature(hub->hdev, (selector << 8) | port1,
   USB_PORT_FEAT_INDICATOR);
 dev_dbg(&port_dev->dev, "indicator %s status %d\n",
  to_led_name(selector), status);
}

#define LED_CYCLE_PERIOD ((2*HZ)/3)

static void led_work(struct work_struct *work)
{
 struct usb_hub  *hub =
  container_of(work, struct usb_hub, leds.work);
 struct usb_device *hdev = hub->hdev;
 unsigned  i;
 unsigned  changed = 0;
 int   cursor = -1;

 if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)
  return;

 for (i = 0; i < hdev->maxchild; i++) {
  unsigned selector, mode;

  /* 30%-50% duty cycle */

  switch (hub->indicator[i]) {
  /* cycle marker */
  case INDICATOR_CYCLE:
   cursor = i;
   selector = HUB_LED_AUTO;
   mode = INDICATOR_AUTO;
   break;
  /* blinking green = sw attention */
  case INDICATOR_GREEN_BLINK:
   selector = HUB_LED_GREEN;
   mode = INDICATOR_GREEN_BLINK_OFF;
   break;
  case INDICATOR_GREEN_BLINK_OFF:
   selector = HUB_LED_OFF;
   mode = INDICATOR_GREEN_BLINK;
   break;
  /* blinking amber = hw attention */
  case INDICATOR_AMBER_BLINK:
   selector = HUB_LED_AMBER;
   mode = INDICATOR_AMBER_BLINK_OFF;
   break;
  case INDICATOR_AMBER_BLINK_OFF:
   selector = HUB_LED_OFF;
   mode = INDICATOR_AMBER_BLINK;
   break;
  /* blink green/amber = reserved */
  case INDICATOR_ALT_BLINK:
   selector = HUB_LED_GREEN;
   mode = INDICATOR_ALT_BLINK_OFF;
   break;
  case INDICATOR_ALT_BLINK_OFF:
   selector = HUB_LED_AMBER;
   mode = INDICATOR_ALT_BLINK;
   break;
  default:
   continue;
  }
  if (selector != HUB_LED_AUTO)
   changed = 1;
  set_port_led(hub, i + 1, selector);
  hub->indicator[i] = mode;
 }
 if (!changed && blinkenlights) {
  cursor++;
  cursor %= hdev->maxchild;
  set_port_led(hub, cursor + 1, HUB_LED_GREEN);
  hub->indicator[cursor] = INDICATOR_CYCLE;
  changed++;
 }
 if (changed)
  queue_delayed_work(system_power_efficient_wq,
    &hub->leds, LED_CYCLE_PERIOD);
}

/* use a short timeout for hub/port status fetches */
#define USB_STS_TIMEOUT  1000
#define USB_STS_RETRIES  5

/*
 * USB 2.0 spec Section 11.24.2.6
 */

static int get_hub_status(struct usb_device *hdev,
  struct usb_hub_status *data)
{
 int i, status = -ETIMEDOUT;

 for (i = 0; i < USB_STS_RETRIES &&
   (status == -ETIMEDOUT || status == -EPIPE); i++) {
  status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
   USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
   data, sizeof(*data), USB_STS_TIMEOUT);
 }
 return status;
}

/*
 * USB 2.0 spec Section 11.24.2.7
 * USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6
 */

static int get_port_status(struct usb_device *hdev, int port1,
      void *data, u16 value, u16 length)
{
 int i, status = -ETIMEDOUT;

 for (i = 0; i < USB_STS_RETRIES &&
   (status == -ETIMEDOUT || status == -EPIPE); i++) {
  status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
   USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,
   port1, data, length, USB_STS_TIMEOUT);
 }
 return status;
}

static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
          u16 *status, u16 *change, u32 *ext_status)
{
 int ret;
 int len = 4;

 if (type != HUB_PORT_STATUS)
  len = 8;

 mutex_lock(&hub->status_mutex);
 ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);
 if (ret < len) {
  if (ret != -ENODEV)
   dev_err(hub->intfdev,
    "%s failed (err = %d)\n", __func__, ret);
  if (ret >= 0)
   ret = -EIO;
 } else {
  *status = le16_to_cpu(hub->status->port.wPortStatus);
  *change = le16_to_cpu(hub->status->port.wPortChange);
  if (type != HUB_PORT_STATUS && ext_status)
   *ext_status = le32_to_cpu(
    hub->status->port.dwExtPortStatus);
  ret = 0;
 }
 mutex_unlock(&hub->status_mutex);

 /*
 * There is no need to lock status_mutex here, because status_mutex
 * protects hub->status, and the phy driver only checks the port
 * status without changing the status.
 */

 if (!ret) {
  struct usb_device *hdev = hub->hdev;

  /*
 * Only roothub will be notified of connection changes,
 * since the USB PHY only cares about changes at the next
 * level.
 */

  if (is_root_hub(hdev)) {
   struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
   bool connect;
   bool connect_change;

   connect_change = *change & USB_PORT_STAT_C_CONNECTION;
   connect = *status & USB_PORT_STAT_CONNECTION;
   if (connect_change && connect)
    usb_phy_roothub_notify_connect(hcd->phy_roothub, port1 - 1);
   else if (connect_change)
    usb_phy_roothub_notify_disconnect(hcd->phy_roothub, port1 - 1);
  }
 }

 return ret;
}

int usb_hub_port_status(struct usb_hub *hub, int port1,
  u16 *status, u16 *change)
{
 return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
       status, change, NULL);
}

static void hub_resubmit_irq_urb(struct usb_hub *hub)
{
 unsigned long flags;
 int status;

 spin_lock_irqsave(&hub->irq_urb_lock, flags);

 if (hub->quiescing) {
  spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
  return;
 }

 status = usb_submit_urb(hub->urb, GFP_ATOMIC);
 if (status && status != -ENODEV && status != -EPERM &&
     status != -ESHUTDOWN) {
  dev_err(hub->intfdev, "resubmit --> %d\n", status);
  mod_timer(&hub->irq_urb_retry, jiffies + HZ);
 }

 spin_unlock_irqrestore(&hub->irq_urb_lock, flags);
}

static void hub_retry_irq_urb(struct timer_list *t)
{
 struct usb_hub *hub = timer_container_of(hub, t, irq_urb_retry);

 hub_resubmit_irq_urb(hub);
}


static void kick_hub_wq(struct usb_hub *hub)
{
 struct usb_interface *intf;

 if (hub->disconnected || work_pending(&hub->events))
  return;

 /*
 * Suppress autosuspend until the event is proceed.
 *
 * Be careful and make sure that the symmetric operation is
 * always called. We are here only when there is no pending
 * work for this hub. Therefore put the interface either when
 * the new work is called or when it is canceled.
 */

 intf = to_usb_interface(hub->intfdev);
 usb_autopm_get_interface_no_resume(intf);
 hub_get(hub);

 if (queue_work(hub_wq, &hub->events))
  return;

 /* the work has already been scheduled */
 usb_autopm_put_interface_async(intf);
 hub_put(hub);
}

void usb_kick_hub_wq(struct usb_device *hdev)
{
 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);

 if (hub)
  kick_hub_wq(hub);
}

/*
 * Let the USB core know that a USB 3.0 device has sent a Function Wake Device
 * Notification, which indicates it had initiated remote wakeup.
 *
 * USB 3.0 hubs do not report the port link state change from U3 to U0 when the
 * device initiates resume, so the USB core will not receive notice of the
 * resume through the normal hub interrupt URB.
 */

void usb_wakeup_notification(struct usb_device *hdev,
  unsigned int portnum)
{
 struct usb_hub *hub;
 struct usb_port *port_dev;

 if (!hdev)
  return;

 hub = usb_hub_to_struct_hub(hdev);
 if (hub) {
  port_dev = hub->ports[portnum - 1];
  if (port_dev && port_dev->child)
   pm_wakeup_event(&port_dev->child->dev, 0);

  set_bit(portnum, hub->wakeup_bits);
  kick_hub_wq(hub);
 }
}
EXPORT_SYMBOL_GPL(usb_wakeup_notification);

/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
{
 struct usb_hub *hub = urb->context;
 int status = urb->status;
 unsigned i;
 unsigned long bits;

 switch (status) {
 case -ENOENT:  /* synchronous unlink */
 case -ECONNRESET: /* async unlink */
 case -ESHUTDOWN: /* hardware going away */
  return;

 default:  /* presumably an error */
  /* Cause a hub reset after 10 consecutive errors */
  dev_dbg(hub->intfdev, "transfer --> %d\n", status);
  if ((++hub->nerrors < 10) || hub->error)
   goto resubmit;
  hub->error = status;
  fallthrough;

 /* let hub_wq handle things */
 case 0:   /* we got data:  port status changed */
  bits = 0;
  for (i = 0; i < urb->actual_length; ++i)
   bits |= ((unsigned long) ((*hub->buffer)[i]))
     << (i*8);
  hub->event_bits[0] = bits;
  break;
 }

 hub->nerrors = 0;

 /* Something happened, let hub_wq figure it out */
 kick_hub_wq(hub);

resubmit:
 hub_resubmit_irq_urb(hub);
}

/* USB 2.0 spec Section 11.24.2.3 */
static inline int
hub_clear_tt_buffer(struct usb_device *hdev, u16 devinfo, u16 tt)
{
 /* Need to clear both directions for control ep */
 if (((devinfo >> 11) & USB_ENDPOINT_XFERTYPE_MASK) ==
   USB_ENDPOINT_XFER_CONTROL) {
  int status = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
    HUB_CLEAR_TT_BUFFER, USB_RT_PORT,
    devinfo ^ 0x8000, tt, NULL, 0, 1000);
  if (status)
   return status;
 }
 return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
          HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
          tt, NULL, 0, 1000);
}

/*
 * enumeration blocks hub_wq for a long time. we use keventd instead, since
 * long blocking there is the exception, not the rule.  accordingly, HCDs
 * talking to TTs must queue control transfers (not just bulk and iso), so
 * both can talk to the same hub concurrently.
 */

static void hub_tt_work(struct work_struct *work)
{
 struct usb_hub  *hub =
  container_of(work, struct usb_hub, tt.clear_work);
 unsigned long  flags;

 spin_lock_irqsave(&hub->tt.lock, flags);
 while (!list_empty(&hub->tt.clear_list)) {
  struct list_head *next;
  struct usb_tt_clear *clear;
  struct usb_device *hdev = hub->hdev;
  const struct hc_driver *drv;
  int   status;

  next = hub->tt.clear_list.next;
  clear = list_entry(next, struct usb_tt_clear, clear_list);
  list_del(&clear->clear_list);

  /* drop lock so HCD can concurrently report other TT errors */
  spin_unlock_irqrestore(&hub->tt.lock, flags);
  status = hub_clear_tt_buffer(hdev, clear->devinfo, clear->tt);
  if (status && status != -ENODEV)
   dev_err(&hdev->dev,
    "clear tt %d (%04x) error %d\n",
    clear->tt, clear->devinfo, status);

  /* Tell the HCD, even if the operation failed */
  drv = clear->hcd->driver;
  if (drv->clear_tt_buffer_complete)
   (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep);

  kfree(clear);
  spin_lock_irqsave(&hub->tt.lock, flags);
 }
 spin_unlock_irqrestore(&hub->tt.lock, flags);
}

/**
 * usb_hub_set_port_power - control hub port's power state
 * @hdev: USB device belonging to the usb hub
 * @hub: target hub
 * @port1: port index
 * @set: expected status
 *
 * call this function to control port's power via setting or
 * clearing the port's PORT_POWER feature.
 *
 * Return: 0 if successful. A negative error code otherwise.
 */

int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
      int port1, bool set)
{
 int ret;

 if (set)
  ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 else
  ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);

 if (ret)
  return ret;

 if (set)
  set_bit(port1, hub->power_bits);
 else
  clear_bit(port1, hub->power_bits);
 return 0;
}

/**
 * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
 * @urb: an URB associated with the failed or incomplete split transaction
 *
 * High speed HCDs use this to tell the hub driver that some split control or
 * bulk transaction failed in a way that requires clearing internal state of
 * a transaction translator.  This is normally detected (and reported) from
 * interrupt context.
 *
 * It may not be possible for that hub to handle additional full (or low)
 * speed transactions until that state is fully cleared out.
 *
 * Return: 0 if successful. A negative error code otherwise.
 */

int usb_hub_clear_tt_buffer(struct urb *urb)
{
 struct usb_device *udev = urb->dev;
 int   pipe = urb->pipe;
 struct usb_tt  *tt = udev->tt;
 unsigned long  flags;
 struct usb_tt_clear *clear;

 /* we've got to cope with an arbitrary number of pending TT clears,
 * since each TT has "at least two" buffers that can need it (and
 * there can be many TTs per hub).  even if they're uncommon.
 */

 clear = kmalloc(sizeof *clear, GFP_ATOMIC);
 if (clear == NULL) {
  dev_err(&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
  /* FIXME recover somehow ... RESET_TT? */
  return -ENOMEM;
 }

 /* info that CLEAR_TT_BUFFER needs */
 clear->tt = tt->multi ? udev->ttport : 1;
 clear->devinfo = usb_pipeendpoint (pipe);
 clear->devinfo |= ((u16)udev->devaddr) << 4;
 clear->devinfo |= usb_pipecontrol(pipe)
   ? (USB_ENDPOINT_XFER_CONTROL << 11)
   : (USB_ENDPOINT_XFER_BULK << 11);
 if (usb_pipein(pipe))
  clear->devinfo |= 1 << 15;

 /* info for completion callback */
 clear->hcd = bus_to_hcd(udev->bus);
 clear->ep = urb->ep;

 /* tell keventd to clear state for this TT */
 spin_lock_irqsave(&tt->lock, flags);
 list_add_tail(&clear->clear_list, &tt->clear_list);
 schedule_work(&tt->clear_work);
 spin_unlock_irqrestore(&tt->lock, flags);
 return 0;
}
EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);

static void hub_power_on(struct usb_hub *hub, bool do_delay)
{
 int port1;

 /* 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
 * but only emulate it.  In all cases, the ports won't work
 * unless we send these messages to the hub.
 */

 if (hub_is_port_power_switchable(hub))
  dev_dbg(hub->intfdev, "enabling power on all ports\n");
 else
  dev_dbg(hub->intfdev, "trying to enable port power on "
    "non-switchable hub\n");
 for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
  if (test_bit(port1, hub->power_bits))
   set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
  else
   usb_clear_port_feature(hub->hdev, port1,
      USB_PORT_FEAT_POWER);
 if (do_delay)
  msleep(hub_power_on_good_delay(hub));
}

static int hub_hub_status(struct usb_hub *hub,
  u16 *status, u16 *change)
{
 int ret;

 mutex_lock(&hub->status_mutex);
 ret = get_hub_status(hub->hdev, &hub->status->hub);
 if (ret < 0) {
  if (ret != -ENODEV)
   dev_err(hub->intfdev,
    "%s failed (err = %d)\n", __func__, ret);
 } else {
  *status = le16_to_cpu(hub->status->hub.wHubStatus);
  *change = le16_to_cpu(hub->status->hub.wHubChange);
  ret = 0;
 }
 mutex_unlock(&hub->status_mutex);
 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);
}

/*
 * Disable a port and mark a logical connect-change event, so that some
 * time later hub_wq will disconnect() any existing usb_device on the port
 * and will re-enumerate if there actually is a device attached.
 */

static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
{
 dev_dbg(&hub->ports[port1 - 1]->dev, "logical disconnect\n");
 hub_port_disable(hub, port1, 1);

 /* FIXME let caller ask to power down the port:
 *  - some devices won't enumerate without a VBUS power cycle
 *  - SRP saves power that way
 *  - ... new call, TBD ...
 * That's easy if this hub can switch power per-port, and
 * hub_wq reactivates the port later (timer, SRP, etc).
 * Powerdown must be optional, because of reset/DFU.
 */


 set_bit(port1, hub->change_bits);
 kick_hub_wq(hub);
}

/**
 * usb_remove_device - disable a device's port on its parent hub
 * @udev: device to be disabled and removed
 * Context: @udev locked, must be able to sleep.
 *
 * After @udev's port has been disabled, hub_wq is notified and it will
 * see that the device has been disconnected.  When the device is
 * physically unplugged and something is plugged in, the events will
 * be received and processed normally.
 *
 * Return: 0 if successful. A negative error code otherwise.
 */

int usb_remove_device(struct usb_device *udev)
{
 struct usb_hub *hub;
 struct usb_interface *intf;
 int ret;

 if (!udev->parent) /* Can't remove a root hub */
  return -EINVAL;
 hub = usb_hub_to_struct_hub(udev->parent);
 intf = to_usb_interface(hub->intfdev);

 ret = usb_autopm_get_interface(intf);
 if (ret < 0)
  return ret;

 set_bit(udev->portnum, hub->removed_bits);
 hub_port_logical_disconnect(hub, udev->portnum);
 usb_autopm_put_interface(intf);
 return 0;
}

enum hub_activation_type {
 HUB_INIT, HUB_INIT2, HUB_INIT3,  /* INITs must come first */
 HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};

static void hub_init_func2(struct work_struct *ws);
static void hub_init_func3(struct work_struct *ws);

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
 struct usb_device *hdev = hub->hdev;
 struct usb_hcd *hcd;
 int ret;
 int port1;
 int status;
 bool need_debounce_delay = false;
 unsigned delay;

 /* Continue a partial initialization */
 if (type == HUB_INIT2 || type == HUB_INIT3) {
  device_lock(&hdev->dev);

  /* Was the hub disconnected while we were waiting? */
  if (hub->disconnected)
   goto disconnected;
  if (type == HUB_INIT2)
   goto init2;
  goto init3;
 }

 hub_get(hub);

 /* The superspeed hub except for root hub has to use Hub Depth
 * value as an offset into the route string to locate the bits
 * it uses to determine the downstream port number. So hub driver
 * should send a set hub depth request to superspeed hub after
 * the superspeed hub is set configuration in initialization or
 * reset procedure.
 *
 * After a resume, port power should still be on.
 * For any other type of activation, turn it on.
 */

 if (type != HUB_RESUME) {
  if (hdev->parent && hub_is_superspeed(hdev)) {
   ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
     HUB_SET_DEPTH, USB_RT_HUB,
     hdev->level - 1, 0, NULL, 0,
     USB_CTRL_SET_TIMEOUT);
   if (ret < 0)
    dev_err(hub->intfdev,
      "set hub depth failed\n");
  }

  /* Speed up system boot by using a delayed_work for the
 * hub's initial power-up delays.  This is pretty awkward
 * and the implementation looks like a home-brewed sort of
 * setjmp/longjmp, but it saves at least 100 ms for each
 * root hub (assuming usbcore is compiled into the kernel
 * rather than as a module).  It adds up.
 *
 * This can't be done for HUB_RESUME or HUB_RESET_RESUME
 * because for those activation types the ports have to be
 * operational when we return.  In theory this could be done
 * for HUB_POST_RESET, but it's easier not to.
 */

  if (type == HUB_INIT) {
   delay = hub_power_on_good_delay(hub);

   hub_power_on(hub, false);
   INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
   queue_delayed_work(system_power_efficient_wq,
     &hub->init_work,
     msecs_to_jiffies(delay));

   /* Suppress autosuspend until init is done */
   usb_autopm_get_interface_no_resume(
     to_usb_interface(hub->intfdev));
   return;  /* Continues at init2: below */
  } else if (type == HUB_RESET_RESUME) {
   /* The internal host controller state for the hub device
 * may be gone after a host power loss on system resume.
 * Update the device's info so the HW knows it's a hub.
 */

   hcd = bus_to_hcd(hdev->bus);
   if (hcd->driver->update_hub_device) {
    ret = hcd->driver->update_hub_device(hcd, hdev,
      &hub->tt, GFP_NOIO);
    if (ret < 0) {
     dev_err(hub->intfdev,
      "Host not accepting hub info update\n");
     dev_err(hub->intfdev,
      "LS/FS devices and hubs may not work under this hub\n");
    }
   }
   hub_power_on(hub, true);
  } else {
   hub_power_on(hub, true);
  }
 /* Give some time on remote wakeup to let links to transit to U0 */
 } else if (hub_is_superspeed(hub->hdev))
  msleep(20);

 init2:

 /*
 * Check each port and set hub->change_bits to let hub_wq know
 * which ports need attention.
 */

 for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
  struct usb_port *port_dev = hub->ports[port1 - 1];
  struct usb_device *udev = port_dev->child;
  u16 portstatus, portchange;

  portstatus = portchange = 0;
  status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
  if (status)
   goto abort;

  if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
   dev_dbg(&port_dev->dev, "status %04x change %04x\n",
     portstatus, portchange);

  /*
 * After anything other than HUB_RESUME (i.e., initialization
 * or any sort of reset), every port should be disabled.
 * Unconnected ports should likewise be disabled (paranoia),
 * and so should ports for which we have no usb_device.
 */

  if ((portstatus & USB_PORT_STAT_ENABLE) && (
    type != HUB_RESUME ||
    !(portstatus & USB_PORT_STAT_CONNECTION) ||
    !udev ||
    udev->state == USB_STATE_NOTATTACHED)) {
   /*
 * USB3 protocol ports will automatically transition
 * to Enabled state when detect an USB3.0 device attach.
 * Do not disable USB3 protocol ports, just pretend
 * power was lost
 */

   portstatus &= ~USB_PORT_STAT_ENABLE;
   if (!hub_is_superspeed(hdev))
    usb_clear_port_feature(hdev, port1,
         USB_PORT_FEAT_ENABLE);
  }

  /* Make sure a warm-reset request is handled by port_event */
  if (type == HUB_RESUME &&
      hub_port_warm_reset_required(hub, port1, portstatus))
   set_bit(port1, hub->event_bits);

  /*
 * Add debounce if USB3 link is in polling/link training state.
 * Link will automatically transition to Enabled state after
 * link training completes.
 */

  if (hub_is_superspeed(hdev) &&
      ((portstatus & USB_PORT_STAT_LINK_STATE) ==
      USB_SS_PORT_LS_POLLING))
   need_debounce_delay = true;

  /* Clear status-change flags; we'll debounce later */
  if (portchange & USB_PORT_STAT_C_CONNECTION) {
   need_debounce_delay = true;
   usb_clear_port_feature(hub->hdev, port1,
     USB_PORT_FEAT_C_CONNECTION);
  }
  if (portchange & USB_PORT_STAT_C_ENABLE) {
   need_debounce_delay = true;
   usb_clear_port_feature(hub->hdev, port1,
     USB_PORT_FEAT_C_ENABLE);
  }
  if (portchange & USB_PORT_STAT_C_RESET) {
   need_debounce_delay = true;
   usb_clear_port_feature(hub->hdev, port1,
     USB_PORT_FEAT_C_RESET);
  }
  if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
    hub_is_superspeed(hub->hdev)) {
   need_debounce_delay = true;
   usb_clear_port_feature(hub->hdev, port1,
     USB_PORT_FEAT_C_BH_PORT_RESET);
  }
  /* We can forget about a "removed" device when there's a
 * physical disconnect or the connect status changes.
 */

  if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
    (portchange & USB_PORT_STAT_C_CONNECTION))
   clear_bit(port1, hub->removed_bits);

  if (!udev || udev->state == USB_STATE_NOTATTACHED) {
   /* Tell hub_wq to disconnect the device or
 * check for a new connection or over current condition.
 * Based on USB2.0 Spec Section 11.12.5,
 * C_PORT_OVER_CURRENT could be set while
 * PORT_OVER_CURRENT is not. So check for any of them.
 */

   if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
       (portchange & USB_PORT_STAT_C_CONNECTION) ||
       (portstatus & USB_PORT_STAT_OVERCURRENT) ||
       (portchange & USB_PORT_STAT_C_OVERCURRENT))
    set_bit(port1, hub->change_bits);

  } else if (portstatus & USB_PORT_STAT_ENABLE) {
   bool port_resumed = (portstatus &
     USB_PORT_STAT_LINK_STATE) ==
    USB_SS_PORT_LS_U0;
   /* The power session apparently survived the resume.
 * If there was an overcurrent or suspend change
 * (i.e., remote wakeup request), have hub_wq
 * take care of it.  Look at the port link state
 * for USB 3.0 hubs, since they don't have a suspend
 * change bit, and they don't set the port link change
 * bit on device-initiated resume.
 */

   if (portchange || (hub_is_superspeed(hub->hdev) &&
      port_resumed))
    set_bit(port1, hub->event_bits);

  } else if (udev->persist_enabled) {
#ifdef CONFIG_PM
   udev->reset_resume = 1;
#endif
   /* Don't set the change_bits when the device
 * was powered off.
 */

   if (test_bit(port1, hub->power_bits))
    set_bit(port1, hub->change_bits);

  } else {
   /* The power session is gone; tell hub_wq */
   usb_set_device_state(udev, USB_STATE_NOTATTACHED);
   set_bit(port1, hub->change_bits);
  }
 }

 /* If no port-status-change flags were set, we don't need any
 * debouncing.  If flags were set we can try to debounce the
 * ports all at once right now, instead of letting hub_wq do them
 * one at a time later on.
 *
 * If any port-status changes do occur during this delay, hub_wq
 * will see them later and handle them normally.
 */

 if (need_debounce_delay) {
  delay = HUB_DEBOUNCE_STABLE;

  /* Don't do a long sleep inside a workqueue routine */
  if (type == HUB_INIT2) {
   INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
   queue_delayed_work(system_power_efficient_wq,
     &hub->init_work,
     msecs_to_jiffies(delay));
   device_unlock(&hdev->dev);
   return;  /* Continues at init3: below */
  } else {
   msleep(delay);
  }
 }
 init3:
 hub->quiescing = 0;

 status = usb_submit_urb(hub->urb, GFP_NOIO);
 if (status < 0)
  dev_err(hub->intfdev, "activate --> %d\n", status);
 if (hub->has_indicators && blinkenlights)
  queue_delayed_work(system_power_efficient_wq,
    &hub->leds, LED_CYCLE_PERIOD);

 /* Scan all ports that need attention */
 kick_hub_wq(hub);
 abort:
 if (type == HUB_INIT2 || type == HUB_INIT3) {
  /* Allow autosuspend if it was suppressed */
 disconnected:
  usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
  device_unlock(&hdev->dev);
 }

 if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) {
  /* give usb3 downstream links training time after hub resume */
  usb_autopm_get_interface_no_resume(
   to_usb_interface(hub->intfdev));

  queue_delayed_work(system_power_efficient_wq,
       &hub->post_resume_work,
       msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
  return;
 }

 hub_put(hub);
}

/* Implement the continuations for the delays above */
static void hub_init_func2(struct work_struct *ws)
{
 struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);

 hub_activate(hub, HUB_INIT2);
}

static void hub_init_func3(struct work_struct *ws)
{
 struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);

 hub_activate(hub, HUB_INIT3);
}

static void hub_post_resume(struct work_struct *ws)
{
 struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work);

 usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
 hub_put(hub);
}

enum hub_quiescing_type {
 HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};

static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
{
 struct usb_device *hdev = hub->hdev;
 unsigned long flags;
 int i;

 /* hub_wq and related activity won't re-trigger */
 spin_lock_irqsave(&hub->irq_urb_lock, flags);
 hub->quiescing = 1;
 spin_unlock_irqrestore(&hub->irq_urb_lock, flags);

 if (type != HUB_SUSPEND) {
  /* Disconnect all the children */
  for (i = 0; i < hdev->maxchild; ++i) {
   if (hub->ports[i]->child)
    usb_disconnect(&hub->ports[i]->child);
  }
 }

 /* Stop hub_wq and related activity */
 timer_delete_sync(&hub->irq_urb_retry);
 flush_delayed_work(&hub->post_resume_work);
 usb_kill_urb(hub->urb);
 if (hub->has_indicators)
  cancel_delayed_work_sync(&hub->leds);
 if (hub->tt.hub)
  flush_work(&hub->tt.clear_work);
}

static void hub_pm_barrier_for_all_ports(struct usb_hub *hub)
{
 int i;

 for (i = 0; i < hub->hdev->maxchild; ++i)
  pm_runtime_barrier(&hub->ports[i]->dev);
}

/* caller has locked the hub device */
static int hub_pre_reset(struct usb_interface *intf)
{
 struct usb_hub *hub = usb_get_intfdata(intf);

 hub_quiesce(hub, HUB_PRE_RESET);
 hub->in_reset = 1;
 hub_pm_barrier_for_all_ports(hub);
 return 0;
}

/* caller has locked the hub device */
static int hub_post_reset(struct usb_interface *intf)
{
 struct usb_hub *hub = usb_get_intfdata(intf);

 hub->in_reset = 0;
 hub_pm_barrier_for_all_ports(hub);
 hub_activate(hub, HUB_POST_RESET);
 return 0;
}

static int hub_configure(struct usb_hub *hub,
 struct usb_endpoint_descriptor *endpoint)
{
 struct usb_hcd *hcd;
 struct usb_device *hdev = hub->hdev;
 struct device *hub_dev = hub->intfdev;
 u16 hubstatus, hubchange;
 u16 wHubCharacteristics;
 unsigned int pipe;
 int maxp, ret, i;
 char *message = "out of memory";
 unsigned unit_load;
 unsigned full_load;
 unsigned maxchild;

 hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
 if (!hub->buffer) {
  ret = -ENOMEM;
  goto fail;
 }

 hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
 if (!hub->status) {
  ret = -ENOMEM;
  goto fail;
 }
 mutex_init(&hub->status_mutex);

 hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
 if (!hub->descriptor) {
  ret = -ENOMEM;
  goto fail;
 }

 /* Request the entire hub descriptor.
 * hub->descriptor can handle USB_MAXCHILDREN ports,
 * but a (non-SS) hub can/will return fewer bytes here.
 */

 ret = get_hub_descriptor(hdev, hub->descriptor);
 if (ret < 0) {
  message = "can't read hub descriptor";
  goto fail;
 }

 maxchild = USB_MAXCHILDREN;
 if (hub_is_superspeed(hdev))
  maxchild = min_t(unsigned, maxchild, USB_SS_MAXPORTS);

 if (hub->descriptor->bNbrPorts > maxchild) {
  message = "hub has too many ports!";
  ret = -ENODEV;
  goto fail;
 } else if (hub->descriptor->bNbrPorts == 0) {
  message = "hub doesn't have any ports!";
  ret = -ENODEV;
  goto fail;
 }

 /*
 * Accumulate wHubDelay + 40ns for every hub in the tree of devices.
 * The resulting value will be used for SetIsochDelay() request.
 */

 if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) {
  u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay);

  if (hdev->parent)
   delay += hdev->parent->hub_delay;

  delay += USB_TP_TRANSMISSION_DELAY;
  hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX);
 }

 maxchild = hub->descriptor->bNbrPorts;
 dev_info(hub_dev, "%d port%s detected\n", maxchild,
   str_plural(maxchild));

 hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
 if (!hub->ports) {
  ret = -ENOMEM;
  goto fail;
 }

 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 if (hub_is_superspeed(hdev)) {
  unit_load = 150;
  full_load = 900;
 } else {
  unit_load = 100;
  full_load = 500;
 }

 /* FIXME for USB 3.0, skip for now */
 if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
   !(hub_is_superspeed(hdev))) {
  char portstr[USB_MAXCHILDREN + 1];

  for (i = 0; i < maxchild; i++)
   portstr[i] = hub->descriptor->u.hs.DeviceRemovable
        [((i + 1) / 8)] & (1 << ((i + 1) % 8))
    ? 'F' : 'R';
  portstr[maxchild] = 0;
  dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
 } 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");
  break;
 case HUB_CHAR_INDV_PORT_LPSM:
  dev_dbg(hub_dev, "individual port power switching\n");
  break;
 case HUB_CHAR_NO_LPSM:
 case HUB_CHAR_LPSM:
  dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
  break;
 }

 switch (wHubCharacteristics & HUB_CHAR_OCPM) {
 case HUB_CHAR_COMMON_OCPM:
  dev_dbg(hub_dev, "global over-current protection\n");
  break;
 case HUB_CHAR_INDV_PORT_OCPM:
  dev_dbg(hub_dev, "individual port over-current protection\n");
  break;
 case HUB_CHAR_NO_OCPM:
 case HUB_CHAR_OCPM:
  dev_dbg(hub_dev, "no over-current protection\n");
  break;
 }

 spin_lock_init(&hub->tt.lock);
 INIT_LIST_HEAD(&hub->tt.clear_list);
 INIT_WORK(&hub->tt.clear_work, hub_tt_work);
 switch (hdev->descriptor.bDeviceProtocol) {
 case USB_HUB_PR_FS:
  break;
 case USB_HUB_PR_HS_SINGLE_TT:
  dev_dbg(hub_dev, "Single TT\n");
  hub->tt.hub = hdev;
  break;
 case USB_HUB_PR_HS_MULTI_TT:
  ret = usb_set_interface(hdev, 0, 1);
  if (ret == 0) {
   dev_dbg(hub_dev, "TT per port\n");
   hub->tt.multi = 1;
  } else
   dev_err(hub_dev, "Using single TT (err %d)\n",
    ret);
  hub->tt.hub = hdev;
  break;
 case USB_HUB_PR_SS:
  /* USB 3.0 hubs don't have a TT */
  break;
 default:
  dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
   hdev->descriptor.bDeviceProtocol);
  break;
 }

 /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
 switch (wHubCharacteristics & HUB_CHAR_TTTT) {
 case HUB_TTTT_8_BITS:
  if (hdev->descriptor.bDeviceProtocol != 0) {
   hub->tt.think_time = 666;
   dev_dbg(hub_dev, "TT requires at most %d "
     "FS bit times (%d ns)\n",
    8, hub->tt.think_time);
  }
  break;
 case HUB_TTTT_16_BITS:
  hub->tt.think_time = 666 * 2;
  dev_dbg(hub_dev, "TT requires at most %d "
    "FS bit times (%d ns)\n",
   16, hub->tt.think_time);
  break;
 case HUB_TTTT_24_BITS:
  hub->tt.think_time = 666 * 3;
  dev_dbg(hub_dev, "TT requires at most %d "
    "FS bit times (%d ns)\n",
   24, hub->tt.think_time);
  break;
 case HUB_TTTT_32_BITS:
  hub->tt.think_time = 666 * 4;
  dev_dbg(hub_dev, "TT requires at most %d "
    "FS bit times (%d ns)\n",
   32, hub->tt.think_time);
  break;
 }

 /* probe() zeroes hub->indicator[] */
 if (wHubCharacteristics & HUB_CHAR_PORTIND) {
  hub->has_indicators = 1;
  dev_dbg(hub_dev, "Port indicators are supported\n");
 }

 dev_dbg(hub_dev, "power on to power good time: %dms\n",
  hub->descriptor->bPwrOn2PwrGood * 2);

 /* power budgeting mostly matters with bus-powered hubs,
 * and battery-powered root hubs (may provide just 8 mA).
 */

 ret = usb_get_std_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
 if (ret) {
  message = "can't get hub status";
  goto fail;
 }
 hcd = bus_to_hcd(hdev->bus);
 if (hdev == hdev->bus->root_hub) {
  if (hcd->power_budget > 0)
   hdev->bus_mA = hcd->power_budget;
  else
   hdev->bus_mA = full_load * maxchild;
  if (hdev->bus_mA >= full_load)
   hub->mA_per_port = full_load;
  else {
   hub->mA_per_port = hdev->bus_mA;
   hub->limited_power = 1;
  }
 } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
  int remaining = hdev->bus_mA -
   hub->descriptor->bHubContrCurrent;

  dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
   hub->descriptor->bHubContrCurrent);
  hub->limited_power = 1;

  if (remaining < maxchild * unit_load)
   dev_warn(hub_dev,
     "insufficient power available "
     "to use all downstream ports\n");
  hub->mA_per_port = unit_load; /* 7.2.1 */

 } else { /* Self-powered external hub */
  /* FIXME: What about battery-powered external hubs that
 * provide less current per port? */

  hub->mA_per_port = full_load;
 }
 if (hub->mA_per_port < full_load)
  dev_dbg(hub_dev, "%umA bus power budget for each child\n",
    hub->mA_per_port);

 ret = hub_hub_status(hub, &hubstatus, &hubchange);
 if (ret < 0) {
  message = "can't get hub status";
  goto fail;
 }

 /* local power status reports aren't always correct */
 if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)
  dev_dbg(hub_dev, "local power source is %s\n",
   (hubstatus & HUB_STATUS_LOCAL_POWER)
   ? "lost (inactive)" : "good");

 if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
  dev_dbg(hub_dev, "%sover-current condition exists\n",
   (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");

 /* set up the interrupt endpoint
 * We use the EP's maxpacket size instead of (PORTS+1+7)/8
 * bytes as USB2.0[11.12.3] says because some hubs are known
 * to send more data (and thus cause overflow). For root hubs,
 * maxpktsize is defined in hcd.c's fake endpoint descriptors
 * to be big enough for at least USB_MAXCHILDREN ports. */

 pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
 maxp = usb_maxpacket(hdev, pipe);

 if (maxp > sizeof(*hub->buffer))
  maxp = sizeof(*hub->buffer);

 hub->urb = usb_alloc_urb(0, GFP_KERNEL);
 if (!hub->urb) {
  ret = -ENOMEM;
  goto fail;
 }

 usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
  hub, endpoint->bInterval);

 /* maybe cycle the hub leds */
 if (hub->has_indicators && blinkenlights)
  hub->indicator[0] = INDICATOR_CYCLE;

 mutex_lock(&usb_port_peer_mutex);
 for (i = 0; i < maxchild; i++) {
  ret = usb_hub_create_port_device(hub, i + 1);
  if (ret < 0) {
   dev_err(hub->intfdev,
    "couldn't create port%d device.\n", i + 1);
   break;
  }
 }
 hdev->maxchild = i;
 for (i = 0; i < hdev->maxchild; i++) {
  struct usb_port *port_dev = hub->ports[i];

  pm_runtime_put(&port_dev->dev);
 }

 mutex_unlock(&usb_port_peer_mutex);
 if (ret < 0)
  goto fail;

 /* Update the HCD's internal representation of this hub before hub_wq
 * starts getting port status changes for devices under the hub.
 */

 if (hcd->driver->update_hub_device) {
  ret = hcd->driver->update_hub_device(hcd, hdev,
    &hub->tt, GFP_KERNEL);
  if (ret < 0) {
   message = "can't update HCD hub info";
   goto fail;
  }
 }

 usb_hub_adjust_deviceremovable(hdev, hub->descriptor);

 hub_activate(hub, HUB_INIT);
 return 0;

fail:
 dev_err(hub_dev, "config failed, %s (err %d)\n",
   message, ret);
 /* hub_disconnect() frees urb and descriptor */
 return ret;
}

static void hub_release(struct kref *kref)
{
 struct usb_hub *hub = container_of(kref, struct usb_hub, kref);

 usb_put_dev(hub->hdev);
 usb_put_intf(to_usb_interface(hub->intfdev));
 kfree(hub);
}

void hub_get(struct usb_hub *hub)
{
 kref_get(&hub->kref);
}

void hub_put(struct usb_hub *hub)
{
 kref_put(&hub->kref, hub_release);
}

static unsigned highspeed_hubs;

static void hub_disconnect(struct usb_interface *intf)
{
 struct usb_hub *hub = usb_get_intfdata(intf);
 struct usb_device *hdev = interface_to_usbdev(intf);
 int port1;

 /*
 * Stop adding new hub events. We do not want to block here and thus
 * will not try to remove any pending work item.
 */

 hub->disconnected = 1;

 /* Disconnect all children and quiesce the hub */
 hub->error = 0;
 hub_quiesce(hub, HUB_DISCONNECT);

 mutex_lock(&usb_port_peer_mutex);

 /* Avoid races with recursively_mark_NOTATTACHED() */
 spin_lock_irq(&device_state_lock);
 port1 = hdev->maxchild;
 hdev->maxchild = 0;
 usb_set_intfdata(intf, NULL);
 spin_unlock_irq(&device_state_lock);

 for (; port1 > 0; --port1)
  usb_hub_remove_port_device(hub, port1);

 mutex_unlock(&usb_port_peer_mutex);

 if (hub->hdev->speed == USB_SPEED_HIGH)
  highspeed_hubs--;

 usb_free_urb(hub->urb);
 kfree(hub->ports);
 kfree(hub->descriptor);
 kfree(hub->status);
 kfree(hub->buffer);

 pm_suspend_ignore_children(&intf->dev, false);

 if (hub->quirk_disable_autosuspend)
  usb_autopm_put_interface(intf);

 onboard_dev_destroy_pdevs(&hub->onboard_devs);

 hub_put(hub);
}

static bool hub_descriptor_is_sane(struct usb_host_interface *desc)
{
 /* Some hubs have a subclass of 1, which AFAICT according to the */
 /*  specs is not defined, but it works */
 if (desc->desc.bInterfaceSubClass != 0 &&
     desc->desc.bInterfaceSubClass != 1)
  return false;

 /* Multiple endpoints? What kind of mutant ninja-hub is this? */
 if (desc->desc.bNumEndpoints != 1)
  return false;

 /* If the first endpoint is not interrupt IN, we'd better punt! */
 if (!usb_endpoint_is_int_in(&desc->endpoint[0].desc))
  return false;

        return true;
}

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
 struct usb_host_interface *desc;
 struct usb_device *hdev;
 struct usb_hub *hub;

 desc = intf->cur_altsetting;
 hdev = interface_to_usbdev(intf);

 /*
 * The USB 2.0 spec prohibits hubs from having more than one
 * configuration or interface, and we rely on this prohibition.
 * Refuse to accept a device that violates it.
 */

 if (hdev->descriptor.bNumConfigurations > 1 ||
   hdev->actconfig->desc.bNumInterfaces > 1) {
  dev_err(&intf->dev, "Invalid hub with more than one config or interface\n");
  return -EINVAL;
 }

 /*
 * Set default autosuspend delay as 0 to speedup bus suspend,
 * based on the below considerations:
 *
 * - Unlike other drivers, the hub driver does not rely on the
 *   autosuspend delay to provide enough time to handle a wakeup
 *   event, and the submitted status URB is just to check future
 *   change on hub downstream ports, so it is safe to do it.
 *
 * - The patch might cause one or more auto supend/resume for
 *   below very rare devices when they are plugged into hub
 *   first time:
 *
 *    devices having trouble initializing, and disconnect
 *    themselves from the bus and then reconnect a second
 *    or so later
 *
 *    devices just for downloading firmware, and disconnects
 *    themselves after completing it
 *
 *   For these quite rare devices, their drivers may change the
 *   autosuspend delay of their parent hub in the probe() to one
 *   appropriate value to avoid the subtle problem if someone
 *   does care it.
 *
 * - The patch may cause one or more auto suspend/resume on
 *   hub during running 'lsusb', but it is probably too
 *   infrequent to worry about.
 *
 * - Change autosuspend delay of hub can avoid unnecessary auto
 *   suspend timer for hub, also may decrease power consumption
 *   of USB bus.
 *
 * - If user has indicated to prevent autosuspend by passing
 *   usbcore.autosuspend = -1 then keep autosuspend disabled.
 */

#ifdef CONFIG_PM
 if (hdev->dev.power.autosuspend_delay >= 0)
  pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
#endif

 /*
 * Hubs have proper suspend/resume support, except for root hubs
 * where the controller driver doesn't have bus_suspend and
 * bus_resume methods.
 */

 if (hdev->parent) {  /* normal device */
  usb_enable_autosuspend(hdev);
 } else {   /* root hub */
  const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;

  if (drv->bus_suspend && drv->bus_resume)
   usb_enable_autosuspend(hdev);
 }

 if (hdev->level == MAX_TOPO_LEVEL) {
  dev_err(&intf->dev,
   "Unsupported bus topology: hub nested too deep\n");
  return -E2BIG;
 }

#ifdef CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB
 if (hdev->parent) {
  dev_warn(&intf->dev, "ignoring external hub\n");
  return -ENODEV;
 }
#endif

 if (!hub_descriptor_is_sane(desc)) {
  dev_err(&intf->dev, "bad descriptor, ignoring hub\n");
  return -EIO;
 }

 /* We found a hub */
 dev_info(&intf->dev, "USB hub found\n");

 hub = kzalloc(sizeof(*hub), GFP_KERNEL);
 if (!hub)
  return -ENOMEM;

 kref_init(&hub->kref);
 hub->intfdev = &intf->dev;
 hub->hdev = hdev;
 INIT_DELAYED_WORK(&hub->leds, led_work);
 INIT_DELAYED_WORK(&hub->init_work, NULL);
 INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
 INIT_WORK(&hub->events, hub_event);
 INIT_LIST_HEAD(&hub->onboard_devs);
 spin_lock_init(&hub->irq_urb_lock);
 timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
 usb_get_intf(intf);
 usb_get_dev(hdev);

 usb_set_intfdata(intf, hub);
 intf->needs_remote_wakeup = 1;
 pm_suspend_ignore_children(&intf->dev, true);

 if (hdev->speed == USB_SPEED_HIGH)
  highspeed_hubs++;

 if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
  hub->quirk_check_port_auto_suspend = 1;

 if (id->driver_info & HUB_QUIRK_DISABLE_AUTOSUSPEND) {
  hub->quirk_disable_autosuspend = 1;
  usb_autopm_get_interface_no_resume(intf);
 }

 if ((id->driver_info & HUB_QUIRK_REDUCE_FRAME_INTR_BINTERVAL) &&
     desc->endpoint[0].desc.bInterval > USB_REDUCE_FRAME_INTR_BINTERVAL) {
  desc->endpoint[0].desc.bInterval =
   USB_REDUCE_FRAME_INTR_BINTERVAL;
  /* Tell the HCD about the interrupt ep's new bInterval */
  usb_set_interface(hdev, 0, 0);
 }

 if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
  onboard_dev_create_pdevs(hdev, &hub->onboard_devs);

  return 0;
 }

 hub_disconnect(intf);
 return -ENODEV;
}

static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
 struct usb_device *hdev = interface_to_usbdev(intf);
 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);

 /* assert ifno == 0 (part of hub spec) */
 switch (code) {
 case USBDEVFS_HUB_PORTINFO: {
  struct usbdevfs_hub_portinfo *info = user_data;
  int i;

  spin_lock_irq(&device_state_lock);
  if (hdev->devnum <= 0)
   info->nports = 0;
  else {
   info->nports = hdev->maxchild;
   for (i = 0; i < info->nports; i++) {
    if (hub->ports[i]->child == NULL)
     info->port[i] = 0;
    else
     info->port[i] =
      hub->ports[i]->child->devnum;
   }
  }
  spin_unlock_irq(&device_state_lock);

  return info->nports + 1;
  }

 default:
  return -ENOSYS;
 }
}

/*
 * Allow user programs to claim ports on a hub.  When a device is attached
 * to one of these "claimed" ports, the program will "own" the device.
 */

static int find_port_owner(struct usb_device *hdev, unsigned port1,
  struct usb_dev_state ***ppowner)
{
 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);

 if (hdev->state == USB_STATE_NOTATTACHED)
  return -ENODEV;
 if (port1 == 0 || port1 > hdev->maxchild)
  return -EINVAL;

 /* Devices not managed by the hub driver
 * will always have maxchild equal to 0.
 */

 *ppowner = &(hub->ports[port1 - 1]->port_owner);
 return 0;
}

/* In the following three functions, the caller must hold hdev's lock */
int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
         struct usb_dev_state *owner)
{
 int rc;
 struct usb_dev_state **powner;

 rc = find_port_owner(hdev, port1, &powner);
 if (rc)
  return rc;
 if (*powner)
  return -EBUSY;
 *powner = owner;
 return rc;
}
EXPORT_SYMBOL_GPL(usb_hub_claim_port);

int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
    struct usb_dev_state *owner)
{
 int rc;
 struct usb_dev_state **powner;

 rc = find_port_owner(hdev, port1, &powner);
 if (rc)
  return rc;
 if (*powner != owner)
  return -ENOENT;
 *powner = NULL;
 return rc;
}
EXPORT_SYMBOL_GPL(usb_hub_release_port);

void usb_hub_release_all_ports(struct usb_device *hdev, struct usb_dev_state *owner)
{
 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
 int n;

 for (n = 0; n < hdev->maxchild; n++) {
  if (hub->ports[n]->port_owner == owner)
   hub->ports[n]->port_owner = NULL;
 }

}

/* The caller must hold udev's lock */
bool usb_device_is_owned(struct usb_device *udev)
{
 struct usb_hub *hub;

 if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
  return false;
 hub = usb_hub_to_struct_hub(udev->parent);
 return !!hub->ports[udev->portnum - 1]->port_owner;
}

static void update_port_device_state(struct usb_device *udev)
{
 struct usb_hub *hub;
 struct usb_port *port_dev;

 if (udev->parent) {
  hub = usb_hub_to_struct_hub(udev->parent);

  /*
 * The Link Layer Validation System Driver (lvstest)
 * has a test step to unbind the hub before running the
 * rest of the procedure. This triggers hub_disconnect
 * which will set the hub's maxchild to 0, further
 * resulting in usb_hub_to_struct_hub returning NULL.
 */

  if (hub) {
   port_dev = hub->ports[udev->portnum - 1];
   WRITE_ONCE(port_dev->state, udev->state);
   sysfs_notify_dirent(port_dev->state_kn);
  }
 }
}

static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
 struct usb_hub *hub = usb_hub_to_struct_hub(udev);
 int i;

 for (i = 0; i < udev->maxchild; ++i) {
  if (hub->ports[i]->child)
   recursively_mark_NOTATTACHED(hub->ports[i]->child);
 }
 if (udev->state == USB_STATE_SUSPENDED)
  udev->active_duration -= jiffies;
 udev->state = USB_STATE_NOTATTACHED;
 update_port_device_state(udev);
}

/**
 * usb_set_device_state - change a device's current state (usbcore, hcds)
 * @udev: pointer to device whose state should be changed
 * @new_state: new state value to be stored
 *
 * udev->state is _not_ fully protected by the device lock.  Although
 * most transitions are made only while holding the lock, the state can
 * can change to USB_STATE_NOTATTACHED at almost any time.  This
 * is so that devices can be marked as disconnected as soon as possible,
 * without having to wait for any semaphores to be released.  As a result,
 * all changes to any device's state must be protected by the
 * device_state_lock spinlock.
 *
 * Once a device has been added to the device tree, all changes to its state
 * should be made using this routine.  The state should _not_ be set directly.
 *
 * If udev->state is already USB_STATE_NOTATTACHED then no change is made.
 * Otherwise udev->state is set to new_state, and if new_state is
 * USB_STATE_NOTATTACHED then all of udev's descendants' states are also set
 * to USB_STATE_NOTATTACHED.
 */

void usb_set_device_state(struct usb_device *udev,
  enum usb_device_state new_state)
{
 unsigned long flags;
 int wakeup = -1;

 spin_lock_irqsave(&device_state_lock, flags);
 if (udev->state == USB_STATE_NOTATTACHED)
  ; /* do nothing */
 else if (new_state != USB_STATE_NOTATTACHED) {

  /* root hub wakeup capabilities are managed out-of-band
 * and may involve silicon errata ... ignore them here.
 */

  if (udev->parent) {
   if (udev->state == USB_STATE_SUSPENDED
     || new_state == USB_STATE_SUSPENDED)
    ; /* No change to wakeup settings */
   else if (new_state == USB_STATE_CONFIGURED)
    wakeup = (udev->quirks &
     USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 :
     udev->actconfig->desc.bmAttributes &
     USB_CONFIG_ATT_WAKEUP;
   else
    wakeup = 0;
  }
  if (udev->state == USB_STATE_SUSPENDED &&
   new_state != USB_STATE_SUSPENDED)
   udev->active_duration -= jiffies;
  else if (new_state == USB_STATE_SUSPENDED &&
    udev->state != USB_STATE_SUSPENDED)
   udev->active_duration += jiffies;
  udev->state = new_state;
  update_port_device_state(udev);
 } else
  recursively_mark_NOTATTACHED(udev);
 spin_unlock_irqrestore(&device_state_lock, flags);
 if (wakeup >= 0)
  device_set_wakeup_capable(&udev->dev, wakeup);
}
EXPORT_SYMBOL_GPL(usb_set_device_state);

/*
 * Choose a device number.
 *
 * Device numbers are used as filenames in usbfs.  On USB-1.1 and
 * USB-2.0 buses they are also used as device addresses, however on
 * USB-3.0 buses the address is assigned by the controller hardware
 * and it usually is not the same as the device number.
 *
 * Devices connected under xHCI are not as simple.  The host controller
 * supports virtualization, so the hardware assigns device addresses and
 * the HCD must setup data structures before issuing a set address
 * command to the hardware.
 */

static void choose_devnum(struct usb_device *udev)
{
 int  devnum;
 struct usb_bus *bus = udev->bus;

 /* be safe when more hub events are proceed in parallel */
 mutex_lock(&bus->devnum_next_mutex);

 /* Try to allocate the next devnum beginning at bus->devnum_next. */
 devnum = find_next_zero_bit(bus->devmap, 128, bus->devnum_next);
 if (devnum >= 128)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=92 G=92

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge