📄 hub.c
字号:
} return -EBUSY;}static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay){ int i, status; /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_RESET); if (status) dev_err(hub->intfdev, "cannot reset port %d (err = %d)\n", port1, status); else { status = hub_port_wait_reset(hub, port1, udev, delay); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", status); } /* return on disconnect or reset */ switch (status) { case 0: /* TRSTRCY = 10 ms; plus some extra */ msleep(10 + 40); /* FALL THROUGH */ case -ENOTCONN: case -ENODEV: clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_RESET); /* FIXME need disconnect() for NOTATTACHED device */ usb_set_device_state(udev, status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); return status; } dev_dbg (hub->intfdev, "port %d not enabled, trying reset again...\n", port1); delay = HUB_LONG_RESET_TIME; } dev_err (hub->intfdev, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); return status;}/* * Disable a port and mark a logical connnect-change event, so that some * time later khubd 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->intfdev, "logical disconnect on port %d\n", port1); 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 * - usb_suspend_device(dev, PMSG_SUSPEND) * That's easy if this hub can switch power per-port, and * khubd reactivates the port later (timer, SRP, etc). * Powerdown must be optional, because of reset/DFU. */ set_bit(port1, hub->change_bits); kick_khubd(hub);}#ifdef CONFIG_USB_SUSPEND/* * Selective port suspend reduces power; most suspended devices draw * less than 500 uA. It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. * * Devices leave suspend state when the host wakes them up. Some devices * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet. In * some cases, this wakes the USB host. */static int hub_port_suspend(struct usb_hub *hub, int port1, struct usb_device *udev){ int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); /* enable remote wakeup when appropriate; this lets the device * wake up the upstream hub (including maybe the root hub). * * NOTE: OTG devices may issue remote wakeup (or SRP) even when * we don't explicitly enable it here. */ if (udev->actconfig // && FIXME (remote wakeup enabled on this bus) // ... currently assuming it's always appropriate && (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) != 0) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); } /* see 7.1.7.6 */ status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); } else { /* device has up to 10 msec to fully suspend */ dev_dbg(&udev->dev, "usb suspend\n"); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } return status;}/* * Devices on USB hub ports have only one "suspend" state, corresponding * to ACPI D2, "may cause the device to lose some context". * State transitions include: * * - suspend, resume ... when the VBUS power link stays live * - suspend, disconnect ... VBUS lost * * Once VBUS drop breaks the circuit, the port it's using has to go through * normal re-enumeration procedures, starting with enabling VBUS power. * Other than re-initializing the hub (plug/unplug, except for root hubs), * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. */static int __usb_suspend_device (struct usb_device *udev, int port1, pm_message_t state){ int status; /* caller owns the udev device lock */ if (port1 < 0) return port1; if (udev->state == USB_STATE_SUSPENDED || udev->state == USB_STATE_NOTATTACHED) { return 0; } /* suspend interface drivers; if this is a hub, it * suspends the child devices */ if (udev->actconfig) { int i; for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *intf; struct usb_driver *driver; intf = udev->actconfig->interface[i]; if (state.event <= intf->dev.power.power_state.event) continue; if (!intf->dev.driver) continue; driver = to_usb_driver(intf->dev.driver); if (driver->suspend) { status = driver->suspend(intf, state); if (intf->dev.power.power_state.event != state.event || status) dev_err(&intf->dev, "suspend %d fail, code %d\n", state.event, status); } /* only drivers with suspend() can ever resume(); * and after power loss, even they won't. * bus_rescan_devices() can rebind drivers later. * * FIXME the PM core self-deadlocks when unbinding * drivers during suspend/resume ... everything grabs * dpm_sem (not a spinlock, ugh). we want to unbind, * since we know every driver's probe/disconnect works * even for drivers that can't suspend. */ if (!driver->suspend || state.event > PM_EVENT_FREEZE) {#if 1 dev_warn(&intf->dev, "resume is unsafe!\n");#else down_write(&usb_bus_type.rwsem); device_release_driver(&intf->dev); up_write(&usb_bus_type.rwsem);#endif } } } /* * FIXME this needs port power off call paths too, to help force * USB into the "generic" PM model. At least for devices on * ports that aren't using ganged switching (usually root hubs). * * NOTE: SRP-capable links should adopt more aggressive poweroff * policies (when HNP doesn't apply) once we have mechanisms to * turn power back on! (Likely not before 2.7...) */ if (state.event > PM_EVENT_FREEZE) { dev_warn(&udev->dev, "no poweroff yet, suspending instead\n"); } /* "global suspend" of the HC-to-USB interface (root hub), or * "selective suspend" of just one hub-device link. */ if (!udev->parent) { struct usb_bus *bus = udev->bus; if (bus && bus->op->hub_suspend) { status = bus->op->hub_suspend (bus); if (status == 0) { dev_dbg(&udev->dev, "usb suspend\n"); usb_set_device_state(udev, USB_STATE_SUSPENDED); } } else status = -EOPNOTSUPP; } else status = hub_port_suspend(hdev_to_hub(udev->parent), port1, udev); if (status == 0) udev->dev.power.power_state = state; return status;}/** * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use * @state: PMSG_SUSPEND to suspend * Context: must be able to sleep; device not locked * * Suspends a USB device that isn't in active use, conserving power. * Devices may wake out of a suspend, if anything important happens, * using the remote wakeup mechanism. They may also be taken out of * suspend by the host, using usb_resume_device(). It's also routine * to disconnect devices while they are suspended. * * Suspending OTG devices may trigger HNP, if that's been enabled * between a pair of dual-role devices. That will change roles, such * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. * * Returns 0 on success, else negative errno. */int usb_suspend_device(struct usb_device *udev, pm_message_t state){ int port1, status; port1 = locktree(udev); if (port1 < 0) return port1; status = __usb_suspend_device(udev, port1, state); usb_unlock_device(udev); return status;}/* * hardware resume signaling is finished, either because of selective * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. */static int finish_port_resume(struct usb_device *udev){ int status; u16 devstatus; /* caller owns the udev device lock */ dev_dbg(&udev->dev, "usb resume\n"); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the * first two on the host side; they'd be inside hub_port_init() * during many timeouts, but khubd can't suspend until later. */ usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); udev->dev.power.power_state = PMSG_ON; /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, * and device drivers will know about any resume quirks. */ status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); if (status < 0) dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); else if (udev->actconfig) { unsigned i; le16_to_cpus(&devstatus); if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) { dev_dbg(&udev->dev, "disable remote " "wakeup, status %d\n", status); status = 0; } } /* resume interface drivers; if this is a hub, it * resumes the child devices */ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *intf; struct usb_driver *driver; intf = udev->actconfig->interface[i]; if (intf->dev.power.power_state.event == PM_EVENT_ON) continue; if (!intf->dev.driver) { /* FIXME maybe force to alt 0 */ continue; } driver = to_usb_driver(intf->dev.driver); /* bus_rescan_devices() may rebind drivers */ if (!driver->resume) continue; /* can we do better than just logging errors? */ status = driver->resume(intf); if (intf->dev.power.power_state.event != PM_EVENT_ON || status) dev_dbg(&intf->dev, "resume fail, state %d code %d\n", intf->dev.power.power_state.event, status); } status = 0; } else if (udev->devnum <= 0) { dev_dbg(&udev->dev, "bogus resume!\n"); status = -EINVAL; } return status;}static inthub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev){ int status; // dev_dbg(hub->intfdev, "resume port %d\n", port1); /* 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 { u16 devstatus; u16 portchange; /* drive resume for at least 20 msec */ if (udev) dev_dbg(&udev->dev, "RESUME\n"); msleep(25);#define LIVE_FLAGS ( USB_PORT_STAT_POWER \ | USB_PORT_STAT_ENABLE \ | USB_PORT_STAT_CONNECTION) /* Virtual root hubs can trigger on GET_PORT_STATUS to * stop resume signaling. Then finish the resume * sequence. */ devstatus = portchange = 0; status = hub_port_status(hub, port1, &devstatus, &portchange); if (status < 0 || (devstatus & LIVE_FLAGS) != LIVE_FLAGS || (devstatus & USB_PORT_STAT_SUSPEND) != 0 ) { dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, devstatus, status); } else { /* TRSMRCY = 10 msec */ msleep(10); if (udev) status = finish_port_resume(udev); } } if (status < 0) hub_port_logical_disconnect(hub, port1); return status;}static int hub_resume (struct usb_interface *intf);/** * usb_resume_device - re-activate a suspended usb device * @udev: device to re-activate * Context: must be able to sleep; device not locked * * This will re-activate the suspended device, increasing power usage * while letting drivers communicate again with its endpoints. * USB resume explicitly guarantees that the power session between * the host and the device is the same as it was when the device * suspended. * * Returns 0 on success, else negative errno. */int usb_resume_device(struct usb_device *udev){ int port1, status; port1 = locktree(udev); if (port1 < 0) return port1; /* "global resume" of the HC-to-USB interface (root hub), or * selective resume of one hub-to-device port */ if (!udev->parent) { struct usb_bus *bus = udev->bus; if (bus && bus->op->hub_resume) { status = bus->op->hub_resume (bus); } else status = -EOPNOTSUPP; if (status == 0) { dev_dbg(&udev->dev, "usb resume\n"); /* TRSMRCY = 10 msec */ msleep(10); usb_set_device_state (udev, USB_STATE_CONFIGURED); udev->dev.power.power_state = PMSG_ON; status = hub_resume (udev ->actconfig->interface[0]); } } else if (udev->state == USB_STATE_SUSPENDED) { // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), port1, udev); } else { status = 0; } if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); } usb_unlock_device(udev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -