📄 driver.c
字号:
* 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); 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); } /* Try another autosuspend when the interfaces aren't busy */ if (udev->auto_pm) autosuspend_check(udev); /* If the suspend succeeded, propagate it up the tree */ } else { cancel_delayed_work(&udev->autosuspend); if (parent) usb_autosuspend_device(parent); } done: // dev_dbg(&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; } /* 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) { 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. */ if (udev->dev.parent->power.power_state.event != PM_EVENT_ON) status = -EHOSTUNREACH; else status = usb_resume_device(udev); } } else { /* Needed only for setting udev->dev.power.power_state.event * and for possible debugging message. */ status = usb_resume_device(udev); } if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; usb_resume_interface(intf); } } done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status;}/* Internal routine to adjust a device's usage counter and change * its autosuspend state. */static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt){ int status = 0; usb_pm_lock(udev); udev->auto_pm = 1; udev->pm_usage_cnt += inc_usage_cnt; WARN_ON(udev->pm_usage_cnt < 0); if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) status = usb_resume_both(udev); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; else if (inc_usage_cnt) udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { if (inc_usage_cnt) udev->last_busy = jiffies; status = usb_suspend_both(udev, PMSG_SUSPEND); } usb_pm_unlock(udev); return status;}/* usb_autosuspend_work - callback routine to autosuspend a USB device */void usb_autosuspend_work(struct work_struct *work){ struct usb_device *udev = container_of(work, struct usb_device, autosuspend.work); usb_autopm_do_device(udev, 0);}/** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend * * This routine should be called when a core subsystem is finished using * @udev and wants to allow it to autosuspend. Examples would be when * @udev's device file in usbfs is closed or after a configuration change. * * @udev's usage counter is decremented. If it or any of the usage counters * for an active interface is greater than 0, no autosuspend request will be * queued. (If an interface driver does not support autosuspend then its * usage counter is permanently positive.) Furthermore, if an interface * driver requires remote-wakeup capability during autosuspend but remote * wakeup is disabled, the autosuspend will fail. * * Often the caller will hold @udev's device lock, but this is not * necessary. * * This routine can run only in process context. */void usb_autosuspend_device(struct usb_device *udev){ int status; status = usb_autopm_do_device(udev, -1); // dev_dbg(&udev->dev, "%s: cnt %d\n", // __FUNCTION__, udev->pm_usage_cnt);}/** * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend * * This routine should be called when a core subsystem thinks @udev may * be ready to autosuspend. * * @udev's usage counter left unchanged. If it or any of the usage counters * for an active interface is greater than 0, or autosuspend is not allowed * for any other reason, no autosuspend request will be queued. * * This routine can run only in process context. */void usb_try_autosuspend_device(struct usb_device *udev){ usb_autopm_do_device(udev, 0); // dev_dbg(&udev->dev, "%s: cnt %d\n", // __FUNCTION__, udev->pm_usage_cnt);}/** * usb_autoresume_device - immediately autoresume a USB device and its interfaces * @udev: the usb_device to autoresume * * This routine should be called when a core subsystem wants to use @udev * and needs to guarantee that it is not suspended. No autosuspend will * occur until usb_autosuspend_device is called. (Note that this will not * prevent suspend events originating in the PM core.) Examples would be * when @udev's device file in usbfs is opened or when a remote-wakeup * request is received. * * @udev's usage counter is incremented to prevent subsequent autosuspends. * However if the autoresume fails then the usage counter is re-decremented. * * Often the caller will hold @udev's device lock, but this is not * necessary (and attempting it might cause deadlock). * * This routine can run only in process context. */int usb_autoresume_device(struct usb_device *udev){ int status; status = usb_autopm_do_device(udev, 1); // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, udev->pm_usage_cnt); return status;}/* Internal routine to adjust an interface's usage counter and change * its device's autosuspend state. */static int usb_autopm_do_interface(struct usb_interface *intf, int inc_usage_cnt){ struct usb_device *udev = interface_to_usbdev(intf); int status = 0; usb_pm_lock(udev); if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { udev->auto_pm = 1; intf->pm_usage_cnt += inc_usage_cnt; if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) status = usb_resume_both(udev); if (status != 0) intf->pm_usage_cnt -= inc_usage_cnt; else if (inc_usage_cnt) udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { if (inc_usage_cnt) udev->last_busy = jiffies; status = usb_suspend_both(udev, PMSG_SUSPEND); } } usb_pm_unlock(udev); return status;}/** * usb_autopm_put_interface - decrement a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be decremented * * This routine should be called by an interface driver when it is * finished using @intf and wants to allow it to autosuspend. A typical * example would be a character-device driver when its device file is * closed. * * The routine decrements @intf's usage counter. When the counter reaches * 0, a delayed autosuspend request for @intf's device is queued. When * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all * the other usage counters for the sibling interfaces and @intf's * usb_device, the device and all its interfaces will be autosuspended. * * Note that @intf->pm_usage_cnt is owned by the interface driver. The * core will not change its value other than the increment and decrement * in usb_autopm_get_interface and usb_autopm_put_interface. The driver * may use this simple counter-oriented discipline or may set the value * any way it likes. * * If the driver has set @intf->needs_remote_wakeup then autosuspend will * take place only if the device's remote-wakeup facility is enabled. * * Suspend method calls queued by this routine can arrive at any time * while @intf is resumed and its usage counter is equal to 0. They are * not protected by the usb_device's lock but only by its pm_mutex. * Drivers must provide their own synchronization. * * This routine can run only in process context. */void usb_autopm_put_interface(struct usb_interface *intf){ int status; status = usb_autopm_do_interface(intf, -1); // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, intf->pm_usage_cnt);}EXPORT_SYMBOL_GPL(usb_autopm_put_interface);/** * usb_autopm_get_interface - increment a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be incremented * * This routine should be called by an interface driver when it wants to * use @intf and needs to guarantee that it is not suspended. In addition, * the routine prevents @intf from being autosuspended subsequently. (Note * that this will not prevent suspend events originating in the PM core.) * This prevention will persist until usb_autopm_put_interface() is called * or @intf is unbound. A typical example would be a character-device * driver when its device file is opened. * * * The routine increments @intf's usage counter. (However if the * autoresume fails then the counter is re-decremented.) So long as the * counter is greater than 0, autosuspend will not be allowed for @intf * or its usb_device. When the driver is finished using @intf it should * call usb_autopm_put_interface() to decrement the usage counter and * queue a delayed autosuspend request (if the counter is <= 0). * * * Note that @intf->pm_usage_cnt is owned by the interface driver. The * core will not change its value other than the increment and decrement * in usb_autopm_get_interface and usb_autopm_put_interface. The driver * may use this simple counter-oriented discipline or may set the value * any way it likes. * * Resume method calls generated by this routine can arrive at any time * while @intf is suspended. They are not protected by the usb_device's * lock but only by its pm_mutex. Drivers must provide their own * synchronization. * * This routine can run only in process context. */int usb_autopm_get_interface(struct usb_interface *intf){ int status; status = usb_autopm_do_interface(intf, 1); // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, intf->pm_usage_cnt); return status;}EXPORT_SYMBOL_GPL(usb_autopm_get_interface);/** * usb_autopm_set_interface - set a USB interface's autosuspend state * @intf: the usb_interface whose state should be set * * This routine sets the autosuspend state of @intf's device according * to @intf's usage counter, which the caller must have set previously. * If the counter is <= 0, the device is autosuspended (if it isn't * already suspended and if nothing else prevents the autosuspend). If * the counter is > 0, the device is autoresumed (if it isn't already * awake). */int usb_autopm_set_interface(struct usb_interface *intf){ int status; status = usb_autopm_do_interface(intf, 0); // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", // __FUNCTION__, status, intf->pm_usage_cnt); return status;}EXPORT_SYMBOL_GPL(usb_autopm_set_interface);/** * usb_external_suspend_device - external suspend of a USB device and its interfaces * @udev: the usb_device to suspend * @msg: Power Management message describing this state transition * * This routine handles external suspend requests: ones not generated * internally by a USB driver (autosuspend) but rather coming from the user * (via sysfs) or the PM core (system sleep). The suspend will be carried * out regardless of @udev's usage counter or those of its interfaces, * and regardless of whether or not remote wakeup is enabled. Of course, * interface drivers still have the option of failing the suspend (if * there are unsuspended children, for example). * * The caller must hold @udev's device lock. */int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg){ int status; usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, msg); usb_pm_unlock(udev); return status;}/** * usb_external_resume_device - external resume of a USB device and its interfaces * @udev: the usb_device to resume * * This routine handles external resume requests: ones not generated * internally by a USB driver (autoresume) but rather coming from the user * (via sysfs), the PM core (system resume), or the device itself (remote * wakeup). @udev's usage counter is unaffected. * * The caller must hold @udev's device lock. */int usb_external_resume_device(struct usb_device *udev){ int status; usb_pm_lock(udev); udev->auto_pm = 0; status = usb_resume_both(udev); udev->last_busy = jiffies; usb_pm_unlock(udev); /* Now that the device is awake, we can start trying to autosuspend * it again. */ if (status == 0) usb_try_autosuspend_device(udev); return status;}static int usb_suspend(struct device *dev, pm_message_t message){ if (!is_usb_device(dev)) /* Ignore PM for interfaces */ return 0; return usb_external_suspend_device(to_usb_device(dev), message);}static int usb_resume(struct device *dev){ struct usb_device *udev; if (!is_usb_device(dev)) /* Ignore PM for interfaces */ return 0; udev = to_usb_device(dev); if (udev->autoresume_disabled) return -EPERM; return usb_external_resume_device(udev);}struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, .suspend = usb_suspend, .resume = usb_resume,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -