📄 driver.c
字号:
/* For devices that don't have a driver, we do a generic suspend. */ if (udev->dev.driver) udriver = to_usb_device_driver(udev->dev.driver); else { udev->do_remote_wakeup = 0; udriver = &usb_generic_driver; } status = udriver->suspend(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) udev->dev.power.power_state.event = msg.event; return status;}/* Caller has locked udev's pm_mutex */static int usb_resume_device(struct usb_device *udev){ struct usb_device_driver *udriver; int status = 0; if (udev->state == USB_STATE_NOTATTACHED) goto done; if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ if (udev->dev.driver == NULL) { status = -ENOTCONN; goto done; } if (udev->quirks & USB_QUIRK_RESET_RESUME) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); status = udriver->resume(udev); done: dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) { udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; } return status;}/* Caller has locked intf's usb_device's pm mutex */static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg){ struct usb_driver *driver; int status = 0; /* with no hardware, USB interfaces only use FREEZE and ON states */ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED || !is_active(intf)) goto done; if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */ goto done; driver = to_usb_driver(intf->dev.driver); if (driver->suspend && driver->resume) { status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); else if (!interface_to_usbdev(intf)->auto_pm) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { // FIXME else if there's no suspend method, disconnect... // Not possible if auto_pm is set... dev_warn(&intf->dev, "no suspend for driver %s?\n", driver->name); mark_quiesced(intf); } done: dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); return status;}/* Caller has locked intf's usb_device's pm_mutex */static int usb_resume_interface(struct usb_interface *intf, int reset_resume){ struct usb_driver *driver; int status = 0; if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED || is_active(intf)) goto done; /* Don't let autoresume interfere with unbinding */ if (intf->condition == USB_INTERFACE_UNBINDING) goto done; /* Can't resume it if it doesn't have a driver. */ if (intf->condition == USB_INTERFACE_UNBOUND) { status = -ENOTCONN; goto done; } driver = to_usb_driver(intf->dev.driver); if (reset_resume) { if (driver->reset_resume) { status = driver->reset_resume(intf); if (status) dev_err(&intf->dev, "%s error %d\n", "reset_resume", status); } else { // status = -EOPNOTSUPP; dev_warn(&intf->dev, "no %s for driver %s?\n", "reset_resume", driver->name); } } else { if (driver->resume) { status = driver->resume(intf); if (status) dev_err(&intf->dev, "%s error %d\n", "resume", status); } else { // status = -EOPNOTSUPP; dev_warn(&intf->dev, "no %s for driver %s?\n", "resume", driver->name); } }done: dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) mark_active(intf); /* FIXME: Unbind the driver and reprobe if the resume failed * (not possible if auto_pm is set) */ return status;}#ifdef CONFIG_USB_SUSPEND/* Internal routine to check whether we may autosuspend a device. */static int autosuspend_check(struct usb_device *udev, int reschedule){ int i; struct usb_interface *intf; unsigned long suspend_time, j; /* For autosuspend, fail fast if anything is in use or autosuspend * is disabled. Also fail if any interfaces require remote wakeup * but it isn't available. */ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) return -EPERM; suspend_time = udev->last_busy + udev->autosuspend_delay; if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; if (!is_active(intf)) continue; if (intf->pm_usage_cnt > 0) return -EBUSY; if (intf->needs_remote_wakeup && !udev->do_remote_wakeup) { dev_dbg(&udev->dev, "remote wakeup needed " "for autosuspend\n"); return -EOPNOTSUPP; } /* Don't allow autosuspend if the device will need * a reset-resume and any of its interface drivers * doesn't include support. */ if (udev->quirks & USB_QUIRK_RESET_RESUME) { struct usb_driver *driver; driver = to_usb_driver(intf->dev.driver); if (!driver->reset_resume) return -EOPNOTSUPP; } } } /* If everything is okay but the device hasn't been idle for long * enough, queue a delayed autosuspend request. If the device * _has_ been idle for long enough and the reschedule flag is set, * likewise queue a delayed (1 second) autosuspend request. */ j = jiffies; if (time_before(j, suspend_time)) reschedule = 1; else suspend_time = j + HZ; if (reschedule) { if (!timer_pending(&udev->autosuspend.timer)) { queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, round_jiffies_relative(suspend_time - j)); } return -EAGAIN; } return 0;}#elsestatic inline int autosuspend_check(struct usb_device *udev, int reschedule){ return 0;}#endif /* CONFIG_USB_SUSPEND *//** * usb_suspend_both - suspend a USB device and its interfaces * @udev: the usb_device to suspend * @msg: Power Management message describing this state transition * * This is the central routine for suspending USB devices. It calls the * suspend methods for all the interface drivers in @udev and then calls * the suspend method for @udev itself. If an error occurs at any stage, * all the interfaces which were suspended are resumed so that they remain * in the same state as the device. * * If an autosuspend is in progress (@udev->auto_pm is set), the routine * checks first to make sure that neither the device itself or any of its * active interfaces is in use (pm_usage_cnt is greater than 0). If they * are, the autosuspend fails. * * If the suspend succeeds, the routine recursively queues an autosuspend * request for @udev's parent device, thereby propagating the change up * the device tree. If all of the parent's children are now suspended, * the parent will autosuspend in turn. * * The suspend method calls are subject to mutual exclusion under control * of @udev's pm_mutex. Many of these calls are also under the protection * of @udev's device lock (including all requests originating outside the * USB subsystem), but autosuspend requests generated by a child device or * interface driver may not be. Usbcore will insure that the method calls * do not arrive during bind, unbind, or reset operations. However, drivers * must be prepared to handle suspend calls arriving at unpredictable times. * The only way to block such calls is to do an autoresume (preventing * autosuspends) while holding @udev's device lock (preventing outside * suspends). * * The caller must hold @udev->pm_mutex. * * This routine can run only in process context. */static int usb_suspend_both(struct usb_device *udev, pm_message_t msg){ int status = 0; int i = 0; struct usb_interface *intf; struct usb_device *parent = udev->parent; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) goto done; udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->auto_pm) { status = autosuspend_check(udev, 0); if (status < 0) goto done; } /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { for (; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; status = usb_suspend_interface(intf, msg); if (status != 0) break; } } if (status == 0) status = usb_suspend_device(udev, msg); /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { while (--i >= 0) { intf = udev->actconfig->interface[i]; usb_resume_interface(intf, 0); } /* Try another autosuspend when the interfaces aren't busy */ if (udev->auto_pm) autosuspend_check(udev, status == -EBUSY); /* If the suspend succeeded then prevent any more URB submissions, * flush any outstanding URBs, and propagate the suspend up the tree. */ } else { cancel_delayed_work(&udev->autosuspend); udev->can_submit = 0; for (i = 0; i < 16; ++i) { usb_hcd_flush_endpoint(udev, udev->ep_out[i]); usb_hcd_flush_endpoint(udev, udev->ep_in[i]); } /* If this is just a FREEZE or a PRETHAW, udev might * not really be suspended. Only true suspends get * propagated up the device tree. */ if (parent && udev->state == USB_STATE_SUSPENDED) usb_autosuspend_device(parent); } done: dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status;}/** * usb_resume_both - resume a USB device and its interfaces * @udev: the usb_device to resume * * This is the central routine for resuming USB devices. It calls the * the resume method for @udev and then calls the resume methods for all * the interface drivers in @udev. * * Before starting the resume, the routine calls itself recursively for * the parent device of @udev, thereby propagating the change up the device * tree and assuring that @udev will be able to resume. If the parent is * unable to resume successfully, the routine fails. * * The resume method calls are subject to mutual exclusion under control * of @udev's pm_mutex. Many of these calls are also under the protection * of @udev's device lock (including all requests originating outside the * USB subsystem), but autoresume requests generated by a child device or * interface driver may not be. Usbcore will insure that the method calls * do not arrive during bind, unbind, or reset operations. However, drivers * must be prepared to handle resume calls arriving at unpredictable times. * The only way to block such calls is to do an autoresume (preventing * other autoresumes) while holding @udev's device lock (preventing outside * resumes). * * The caller must hold @udev->pm_mutex. * * This routine can run only in process context. */static int usb_resume_both(struct usb_device *udev){ int status = 0; int i; struct usb_interface *intf; struct usb_device *parent = udev->parent; cancel_delayed_work(&udev->autosuspend); if (udev->state == USB_STATE_NOTATTACHED) { status = -ENODEV; goto done; } udev->can_submit = 1; /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { if (udev->auto_pm && udev->autoresume_disabled) { status = -EPERM; goto done; } if (parent) { status = usb_autoresume_device(parent); if (status == 0) { status = usb_resume_device(udev); if (status || udev->state == USB_STATE_NOTATTACHED) { usb_autosuspend_device(parent); /* It's possible usb_resume_device() * failed after the port was * unsuspended, causing udev to be * logically disconnected. We don't * want usb_disconnect() to autosuspend * the parent again, so tell it that * udev disconnected while still * suspended. */ if (udev->state == USB_STATE_NOTATTACHED) udev->discon_suspended = 1; } } } else { /* We can't progagate beyond the USB subsystem, * so if a root hub's controller is suspended * then we're stuck. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -