/* * 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 */ staticstruct workqueue_struct *hub_wq; staticvoid 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 */ staticbool 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 */ staticint 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.
*/ staticbool 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");
staticbool 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);
staticint usb_reset_and_verify_device(struct usb_device *udev); staticint hub_port_disable(struct usb_hub *hub, int port1, int set_state); staticbool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
u16 portstatus);
staticinlinechar *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"; elseif (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
*/ staticvoid usb_set_lpm_mel(struct usb_device *udev, struct usb3_lpm_parameters *udev_lpm_params, unsignedint udev_exit_latency, struct usb_hub *hub, struct usb3_lpm_parameters *hub_lpm_params, unsignedint hub_exit_latency)
{ unsignedint 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.
*/ staticvoid usb_set_lpm_pel(struct usb_device *udev, struct usb3_lpm_parameters *udev_lpm_params, unsignedint udev_exit_latency, struct usb_hub *hub, struct usb3_lpm_parameters *hub_lpm_params, unsignedint hub_exit_latency, unsignedint port_to_port_exit_latency)
{ unsignedint first_link_pel; unsignedint 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.
*/ staticvoid usb_set_lpm_sel(struct usb_device *udev, struct usb3_lpm_parameters *udev_lpm_params)
{ struct usb_device *parent; unsignedint num_hubs; unsignedint 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);
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;
/* * 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 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
*/ staticint 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);
}
staticchar *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
*/ staticvoid 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);
}
/* 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
*/ staticint 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
*/ staticint 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;
}
staticint 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;
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);
}
/* * 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, unsignedint 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);
/* completion function, fires on port status changes and various faults */ staticvoid hub_irq(struct urb *urb)
{ struct usb_hub *hub = urb->context; int status = urb->status; unsigned i; unsignedlong 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 |= ((unsignedlong) ((*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 */ staticinlineint
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.
*/ staticvoid hub_tt_work(struct work_struct *work)
{ struct usb_hub *hub =
container_of(work, struct usb_hub, tt.clear_work); unsignedlong flags;
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);
/** * 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; unsignedlong 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 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);
staticvoid 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));
}
/* * 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.
*/ staticvoid 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.
*/
/** * 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;
staticvoid 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);
/* Suppress autosuspend until init is done */
usb_autopm_get_interface_no_resume(
to_usb_interface(hub->intfdev)); return; /* Continues at init2: below */
} elseif (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 */
} elseif (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;
/* * 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);
} elseif (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);
} elseif (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));
/* 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);
}
staticvoid 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 */ staticint hub_pre_reset(struct usb_interface *intf)
{ struct usb_hub *hub = usb_get_intfdata(intf);
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;
} elseif (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;
/* FIXME for USB 3.0, skip for now */ if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) { char portstr[USB_MAXCHILDREN + 1];
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;
}
} elseif ((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;
}
/* 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;
}
}
if (hub->quirk_disable_autosuspend)
usb_autopm_put_interface(intf);
onboard_dev_destroy_pdevs(&hub->onboard_devs);
hub_put(hub);
}
staticbool 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) returnfalse;
/* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (desc->desc.bNumEndpoints != 1) returnfalse;
/* If the first endpoint is not interrupt IN, we'd better punt! */ if (!usb_endpoint_is_int_in(&desc->endpoint[0].desc)) returnfalse;
/* * 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 */ conststruct 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;
}
/* 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.
*/ staticint 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;
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);
}
}
}
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)
{ unsignedlong flags; int wakeup = -1;
spin_lock_irqsave(&device_state_lock, flags); if (udev->state == USB_STATE_NOTATTACHED)
; /* do nothing */ elseif (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 */ elseif (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; elseif (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.
*/ staticvoid 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
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
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.