📄 hub.c
字号:
* USB resume explicitly guarantees that the power session between * the host and the device is the same as it was when the device * suspended. * * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this * routine won't check that the port is still enabled. Furthermore, * if @udev->reset_resume is set then finish_port_resume() above will * reset @udev. The end result is that a broken power session can be * recovered and @udev will appear to persist across a loss of VBUS power. * * For example, if a host controller doesn't maintain VBUS suspend current * during a system sleep or is reset when the system wakes up, all the USB * power sessions below it will be broken. This is especially troublesome * for mass-storage devices containing mounted filesystems, since the * device will appear to have disconnected and all the memory mappings * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * * This facility is inherently dangerous. Although usb_reset_device() * makes every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee. Furthermore it's * quite possible for a device to remain unaltered but its media to be * changed. If the user replaces a flash memory card while the system is * asleep, he will have only himself to blame when the filesystem on the * new card is corrupted and the system crashes. * * Returns 0 on success, else negative errno. */int usb_port_resume(struct usb_device *udev){ struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; int status; u16 portchange, portstatus; unsigned mask_flags, want_flags; /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) goto SuspendCleared; // dev_dbg(hub->intfdev, "resume port %d\n", port1); set_bit(port1, hub->busy_bits); /* see 7.1.7.7; affects power usage, but not budgeting */ status = clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", port1, status); } else { /* drive resume for at least 20 msec */ dev_dbg(&udev->dev, "usb %sresume\n", udev->auto_pm ? "auto-" : ""); msleep(25); /* Virtual root hubs can trigger on GET_PORT_STATUS to * stop resume signaling. Then finish the resume * sequence. */ status = hub_port_status(hub, port1, &portstatus, &portchange); SuspendCleared: if (USB_PERSIST && udev->reset_resume) want_flags = USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION; else want_flags = USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE; mask_flags = want_flags | USB_PORT_STAT_SUSPEND; if (status < 0 || (portstatus & mask_flags) != want_flags) { dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, portstatus, status); if (status >= 0) status = -ENODEV; } else { if (portchange & USB_PORT_STAT_C_SUSPEND) clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_SUSPEND); /* TRSMRCY = 10 msec */ msleep(10); } } clear_bit(port1, hub->busy_bits); if (!hub->hdev->parent && !hub->busy_bits[0]) usb_enable_root_hub_irq(hub->hdev->bus); if (status == 0) status = finish_port_resume(udev); if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); } return status;}static int remote_wakeup(struct usb_device *udev){ int status = 0; usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); usb_mark_last_busy(udev); status = usb_external_resume_device(udev); } usb_unlock_device(udev); return status;}#else /* CONFIG_USB_SUSPEND *//* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */int usb_port_suspend(struct usb_device *udev){ return 0;}int usb_port_resume(struct usb_device *udev){ int status = 0; /* However we may need to do a reset-resume */ if (udev->reset_resume) { dev_dbg(&udev->dev, "reset-resume\n"); status = usb_reset_device(udev); } return status;}static inline int remote_wakeup(struct usb_device *udev){ return 0;}#endifstatic int hub_suspend(struct usb_interface *intf, pm_message_t msg){ struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; udev = hdev->children [port1-1]; if (udev && udev->can_submit) { if (!hdev->auto_pm) dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); return -EBUSY; } } dev_dbg(&intf->dev, "%s\n", __FUNCTION__); /* stop khubd and related activity */ hub_quiesce(hub); return 0;}static int hub_resume(struct usb_interface *intf){ struct usb_hub *hub = usb_get_intfdata (intf); dev_dbg(&intf->dev, "%s\n", __FUNCTION__); /* tell khubd to look for changes on this hub */ hub_activate(hub); return 0;}static int hub_reset_resume(struct usb_interface *intf){ struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; int port1; hub_power_on(hub); for (port1 = 1; port1 <= hdev->maxchild; ++port1) { struct usb_device *child = hdev->children[port1-1]; if (child) { /* For "USB_PERSIST"-enabled children we must * mark the child device for reset-resume and * turn off the connect-change status to prevent * khubd from disconnecting it later. */ if (USB_PERSIST && child->persist_enabled) { child->reset_resume = 1; clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); /* Otherwise we must disconnect the child, * but as we may not lock the child device here * we have to do a "logical" disconnect. */ } else { hub_port_logical_disconnect(hub, port1); } } } hub_activate(hub); return 0;}/** * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power * @rhdev: struct usb_device for the root hub * * The USB host controller driver calls this function when its root hub * is resumed and Vbus power has been interrupted or the controller * has been reset. The routine marks @rhdev as having lost power. When * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST * is enabled then it will carry out power-session recovery, otherwise * it will disconnect all the child devices. */void usb_root_hub_lost_power(struct usb_device *rhdev){ dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); rhdev->reset_resume = 1;}EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);#else /* CONFIG_PM */static inline int remote_wakeup(struct usb_device *udev){ return 0;}#define hub_suspend NULL#define hub_resume NULL#define hub_reset_resume NULL#endif/* USB 2.0 spec, 7.1.7.3 / fig 7-29: * * Between connect detection and reset signaling there must be a delay * of 100ms at least for debounce and power-settling. The corresponding * timer shall restart whenever the downstream port detects a disconnect. * * Apparently there are some bluetooth and irda-dongles and a number of * low-speed devices for which this debounce period may last over a second. * Not covered by the spec - but easy to deal with. * * This implementation uses a 1500ms total debounce timeout; if the * connection isn't stable by then it returns -ETIMEDOUT. It checks * every 25ms for transient disconnects. When the port status has been * unchanged for 100ms it returns the port status. */#define HUB_DEBOUNCE_TIMEOUT 1500#define HUB_DEBOUNCE_STEP 25#define HUB_DEBOUNCE_STABLE 100static int hub_port_debounce(struct usb_hub *hub, int port1){ int ret; int total_time, stable_time = 0; u16 portchange, portstatus; unsigned connection = 0xffff; for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { ret = hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) return ret; if (!(portchange & USB_PORT_STAT_C_CONNECTION) && (portstatus & USB_PORT_STAT_CONNECTION) == connection) { stable_time += HUB_DEBOUNCE_STEP; if (stable_time >= HUB_DEBOUNCE_STABLE) break; } else { stable_time = 0; connection = portstatus & USB_PORT_STAT_CONNECTION; } if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_CONNECTION); } if (total_time >= HUB_DEBOUNCE_TIMEOUT) break; msleep(HUB_DEBOUNCE_STEP); } dev_dbg (hub->intfdev, "debounce: port %d: total %dms stable %dms status 0x%x\n", port1, total_time, stable_time, portstatus); if (stable_time < HUB_DEBOUNCE_STABLE) return -ETIMEDOUT; return portstatus;}static void ep0_reinit(struct usb_device *udev){ usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); usb_enable_endpoint(udev, &udev->ep0);}#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN)static int hub_set_address(struct usb_device *udev, int devnum){ int retval; if (devnum <= 1) return -EINVAL; if (udev->state == USB_STATE_ADDRESS) return 0; if (udev->state != USB_STATE_DEFAULT) return -EINVAL; retval = usb_control_msg(udev, usb_sndaddr0pipe(), USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval == 0) { udev->devnum = devnum; /* Device now using proper address */ usb_set_device_state(udev, USB_STATE_ADDRESS); ep0_reinit(udev); } return retval;}/* Reset device, (re)assign address, get device descriptor. * Device connection must be stable, no more debouncing needed. * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of * usb_reset_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */static inthub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter){ static DEFINE_MUTEX(usb_address0_mutex); struct usb_device *hdev = hub->hdev; int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; char *speed, *type; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period * (from USB 2.0 spec, section 7.1.7.5) */ if (!hdev->parent) { delay = HUB_ROOT_RESET_TIME; if (port1 == hdev->bus->otg_port) hdev->bus->b_hnp_enable = 0; } /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (oldspeed == USB_SPEED_LOW) delay = HUB_LONG_RESET_TIME; mutex_lock(&usb_address0_mutex); /* Reset the device; full speed may morph to high speed */ retval = hub_port_reset(hub, port1, udev, delay); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { dev_dbg(&udev->dev, "device reset changed speed!\n"); goto fail; } oldspeed = udev->speed; /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. * For Wireless USB devices, ep0 max packet is always 512 (tho * reported as 0xff in the device descriptor). WUSB1.0[4.8.1]. */ switch (udev->speed) { case USB_SPEED_VARIABLE: /* fixed at 512 */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512); break; case USB_SPEED_HIGH: /* fixed at 64 */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); break; case USB_SPEED_FULL: /* 8, 16, 32, or 64 */ /* to determine the ep0 maxpacket size, try to read * the device descriptor to get bMaxPacketSize0 and * then correct our initial guess. */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64); break; case USB_SPEED_LOW: /* fixed at 8 */ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(8); break; default: goto fail; } type = ""; switch (udev->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; case USB_SPEED_VARIABLE: speed = "variable"; type = "Wireless "; break; default: speed = "?"; break; } dev_info (&udev->dev, "%s %s speed %sUSB device using %s and address %d\n", (udev->config) ? "reset" : "new", speed, type, udev->bus->controller->driver->name, devnum); /* Set up TT records, if needed */ if (hdev->tt) { udev->tt = hdev->tt; udev->ttport = hdev->ttport; } else if (udev->speed != USB_SPEED_HIGH && hdev->speed == USB_SPEED_HIGH) { udev->tt = &hub->tt; udev->ttport = port1; } /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way? * Because device hardware and firmware is sometimes buggy in * this area, and this is how Linux has done it for ages. * Change it cautiously. * * NOTE: If USE_NEW_SCHEME() is true we will start by issuing * a 64-byte GET_DESCRIPTOR request. This is what Windows does, * so it may help with some non-standards-compliant devices. * Otherwise we start with SET_ADDRESS and then try to read the * first 8 bytes of the device descript
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -