USB 电源管理

作者:

Alan Stern <stern@rowland.harvard.edu>

日期:

最后更新:2014 年 2 月

什么是电源管理?

电源管理 (PM) 是一种通过在计算机系统的某些部分未被使用时将其挂起来节省能源的做法。当一个组件被 挂起 时,它处于一个非功能性的低功耗状态;它甚至可能被完全关闭。当内核需要使用一个挂起的组件时,它可以被 恢复(返回到功能齐全的满功率状态)。(还有一些形式的 PM 将组件置于功能较弱但仍可用的状态,而不是挂起;例如降低 CPU 的时钟频率。本文档将不讨论这些其他形式。)

当被挂起的部分包括 CPU 和系统的大部分时,我们称之为“系统挂起”。当一个特定的设备在整个系统保持运行的情况下被关闭时,我们称之为“动态挂起”(也称为“运行时挂起”或“选择性挂起”)。本文档主要集中在 USB 子系统中如何实现动态 PM,尽管系统 PM 也有所涉及(有关系统 PM 的更多信息,请参阅 Documentation/power/*.rst)。

只有在构建内核时启用了 CONFIG_SUSPENDCONFIG_HIBERNATION,才存在系统 PM 支持。动态 PM 支持

只有在构建内核时启用了 CONFIG_PM,才存在 USB 的支持。

[从历史上看,只有在构建内核时启用了 CONFIG_USB_SUSPEND(依赖于 CONFIG_PM_RUNTIME),才存在 USB 的动态 PM 支持。从 3.10 内核版本开始,只要构建内核时启用了 CONFIG_PM_RUNTIME,就存在 USB 的动态 PM 支持。CONFIG_USB_SUSPEND 选项已被删除。]

什么是远程唤醒?

当一个设备被挂起时,通常要等到计算机告诉它才能恢复。同样,如果整个计算机被挂起,通常要等到用户告诉它才能恢复,例如按下电源按钮或打开盖子。

然而,有些设备有能力自己恢复,或请求内核恢复它们,甚至告诉整个计算机恢复。这种能力有几个名称,例如“Wake On LAN”;我们将它泛称为“远程唤醒”。当一个设备被启用远程唤醒并被挂起时,它可能会响应一些外部事件而自行恢复(或发送一个恢复请求)。例如,一个挂起的键盘在按下键时恢复,或一个挂起的 USB 集线器在插入设备时恢复。

何时 USB 设备处于空闲状态?

当内核认为一个设备没有忙于做任何重要的事情,因此可以被挂起时,它就处于空闲状态。确切的定义取决于设备的驱动程序;驱动程序可以声明一个设备即使在没有实际通信发生时也不处于空闲状态。(例如,除非插入集线器的所有设备都已经挂起,否则集线器不被认为是空闲的。)此外,只要一个程序保持其 usbfs 文件打开,即使没有任何 I/O 发生,一个设备也不被认为是空闲的。

如果一个 USB 设备没有驱动程序,它的 usbfs 文件没有打开,并且没有通过 sysfs 访问它,那么它肯定是空闲的。

动态 PM 的形式

当内核决定挂起一个空闲设备时,就会发生动态挂起。这简称为 自动挂起。一般来说,一个设备除非空闲了一段时间,即所谓的空闲延迟时间,否则不会被自动挂起。

当然,内核自己采取的任何措施都不应该妨碍计算机或其设备正常工作。如果一个设备被自动挂起,并且一个程序试图使用它,内核将自动恢复该设备(自动恢复)。出于同样的原因,如果设备支持远程唤醒,那么一个自动挂起的设备通常会启用远程唤醒。

值得一提的是,许多 USB 驱动程序不支持自动挂起。事实上,在撰写本文时(Linux 2.6.23),唯一支持它的驱动程序是集线器驱动程序、kaweth、asix、usblp、usblcd 和 usb-skeleton(这不算数)。如果一个不支持的驱动程序绑定到一个设备,该设备将不会被自动挂起。实际上,内核假装该设备永远不处于空闲状态。

我们可以将电源管理事件分为两大类:外部事件和内部事件。外部事件是由 USB 堆栈之外的某个代理触发的:系统挂起/恢复(由用户空间触发)、手动动态恢复(也由用户空间触发)和远程唤醒(由设备触发)。内部事件是在 USB 堆栈内触发的:自动挂起和自动恢复。请注意,所有动态挂起事件都是内部事件;不允许外部代理发出动态挂起。

动态 PM 的用户界面

控制动态 PM 的用户界面位于每个 USB 设备的 sysfs 目录的 power/ 子目录中,也就是说,位于 /sys/bus/usb/devices/.../power/ 中,其中“...”是设备的 ID。相关的属性文件是:wakeup、control 和 autosuspend_delay_ms。(也可能有一个名为 level 的文件;该文件已从 2.6.35 内核开始弃用,并被 control 文件取代。在 2.6.38 中,autosuspend 文件将被弃用,并被 autosuspend_delay_ms 文件取代。唯一的区别是,较新的文件以毫秒为单位表示延迟,而较旧的文件使用秒。令人困惑的是,这两个文件都存在于 2.6.37 中,但只有 autosuspend 起作用。)

power/wakeup

如果设备不支持远程唤醒,则此文件为空。否则,该文件包含单词 enabled 或单词 disabled,您可以将这些单词写入该文件。该设置决定了下次挂起设备时是否启用远程唤醒。(如果在设备挂起时更改了该设置,则该更改将在下一次挂起时生效。)

power/control

此文件包含两个单词之一:onauto。您可以将这些单词写入该文件以更改设备的设置。

  • on 意味着应该恢复该设备,并且不允许自动挂起。(当然,仍然允许系统挂起。)

  • auto 是正常状态,在这种状态下,内核可以自动挂起和自动恢复该设备。

(在 2.6.32 之前的内核中,您也可以指定 suspend,这意味着该设备应该保持挂起状态,并且不允许自动恢复。此设置不再受支持。)

power/autosuspend_delay_ms

此文件包含一个整数值,该值是设备在内核自动挂起它之前应该保持空闲的毫秒数(空闲延迟时间)。默认值为 2000。0 意味着一旦设备进入空闲状态就立即自动挂起,负值意味着永远不自动挂起。您可以将一个数字写入该文件以更改自动挂起空闲延迟时间。

-1 写入 power/autosuspend_delay_ms 和将 on 写入 power/control 本质上做的是同一件事 -- 它们都阻止设备被自动挂起。是的,这是 API 中的一个冗余。

(在 2.6.21 中,将 0 写入 power/autosuspend 会阻止设备被自动挂起;该行为在 2.6.22 中被更改。power/autosuspend 属性在 2.6.21 之前不存在,power/level 属性在 2.6.22 之前不存在。power/control 在 2.6.34 中添加,power/autosuspend_delay_ms 在 2.6.37 中添加,但在 2.6.38 之前没有生效。)

更改默认空闲延迟时间

默认的自动挂起空闲延迟时间(以秒为单位)由 usbcore 中的一个模块参数控制。您可以在加载 usbcore 时指定该值。例如,要将其设置为 5 秒而不是 2 秒,您可以这样做

modprobe usbcore autosuspend=5

等效地,您可以在 /etc/modprobe.d 中的一个配置文件中添加一行,内容为

options usbcore autosuspend=5

一些发行版在引导过程中非常早就加载了 usbcore 模块,通过一个程序或脚本从 initramfs 镜像运行。要更改参数值,您必须重建该镜像。

如果 usbcore 被编译到内核中,而不是作为可加载模块构建,您可以添加

usbcore.autosuspend=5

到内核的引导命令行。

最后,可以在系统运行时更改参数值。如果您这样做

echo 5 >/sys/module/usbcore/parameters/autosuspend

那么每个新的 USB 设备都会将其自动挂起空闲延迟初始化为 5。(现有设备的空闲延迟值将不会受到影响。)

将初始默认空闲延迟设置为 -1 将阻止任何 USB 设备的自动挂起。这样做的好处是允许您随后为选定的设备启用自动挂起。

警告

USB 规范指出所有 USB 设备都必须支持电源管理。然而,可悲的事实是,许多设备对它的支持并不好。您可以正确地挂起它们,但是当您尝试恢复它们时,它们会从 USB 总线断开连接,或者它们会完全停止工作。这在打印机和扫描仪中似乎特别普遍,但是许多其他类型的设备也存在同样的缺陷。

因此,默认情况下,内核会禁用自动挂起(power/control 属性初始化为 on),对于除集线器之外的所有设备。至少,集线器在这方面表现得相当好。

(在 2.6.21 和 2.6.22 中,情况并非如此。默认情况下,几乎所有 USB 设备都启用了自动挂起。结果,许多人遇到了问题。)

这意味着,除非用户或程序显式启用它,否则非集线器设备不会被自动挂起。在撰写本文时,没有任何广泛的程序会这样做;我们希望在不久的将来,像 HAL 这样的设备管理器将承担起这一额外的责任。与此同时,您始终可以手动执行必要的操作,或者将它们添加到 udev 脚本中。您还可以更改空闲延迟时间;2 秒并不是每个设备的最佳选择。

如果一个驱动程序知道它的设备具有正确的挂起/恢复支持,它可以自行启用自动挂起。例如,笔记本电脑的网络摄像头的视频驱动程序可能会这样做(在最近的内核中,它们会这样做),因为这些设备很少使用,因此通常应该自动挂起。

有时会发现,即使一个设备在自动挂起的情况下工作正常,仍然存在问题。例如,管理键盘和鼠标的 usbhid 驱动程序具有自动挂起支持。使用多个键盘的测试表明,在挂起的键盘上打字,虽然会导致键盘正确地进行远程唤醒,但仍然经常会导致按键丢失。使用鼠标的测试表明,有些鼠标会响应按钮按下而发出远程唤醒请求,但不会响应移动,有些鼠标则不会响应任何一个。

内核不会阻止您在无法处理的设备上启用自动挂起。从理论上讲,甚至有可能在错误的时间挂起设备而损坏设备。(可能性很小,但有可能。)小心。

电源管理的驱动接口

USB 驱动程序支持外部电源管理的要求非常简单;驱动程序只需要在其 usb_driver 结构中定义

.suspend
.resume
.reset_resume

方法,并且 reset_resume 方法是可选的。这些方法的工作非常简单

  • suspend 方法被调用来警告驱动程序设备即将被挂起。如果驱动程序返回一个负的错误代码,挂起将被中止。通常,驱动程序将返回 0,在这种情况下,它必须取消所有未完成的 URB(usb_kill_urb())并且不再提交任何 URB。

  • resume 方法被调用来告诉驱动程序设备已经被恢复,并且驱动程序可以恢复正常运行。URB 可以再次被提交。

  • reset_resume 方法被调用来告诉驱动程序设备已经被恢复,并且它也被重置了。驱动程序应该重新执行任何必要的设备初始化,因为该设备可能已经丢失了大部分或全部状态(尽管接口将与挂起之前的 altsetting 相同)。

如果设备在挂起时断开连接或断电,将调用 disconnect 方法,而不是 resumereset_resume 方法。当从休眠状态唤醒时,这种情况也很可能发生,因为许多系统在休眠期间不保持到 USB 主控制器的挂起电流。(可以通过使用 USB 持久化工具来解决休眠强制断开连接的问题。)

reset_resume 方法被 USB 持久化工具使用(参见 系统挂起期间的 USB 设备持久性),并且在未启用 CONFIG_USB_PERSIST 的某些情况下也可以使用。目前,如果在恢复期间重置了一个设备,并且驱动程序没有 reset_resume 方法,则驱动程序不会收到任何关于恢复的通知。以后的内核将调用驱动程序的 disconnect 方法;2.6.23 不会这样做。

USB 驱动程序绑定到接口,因此当接口被挂起或恢复时,它们的 suspendresume 方法会被调用。原则上,人们可能希望挂起一个设备上的一些接口(即,强制这些接口的驱动程序停止所有活动),而不挂起其他接口。USB 核心不允许这样做;当设备本身被挂起时,所有接口都被挂起,当设备被恢复时,所有接口都被恢复。不可能挂起或恢复一些但不是全部设备的接口。最接近的方法是解除绑定接口的驱动程序。

自动挂起和自动恢复的驱动接口

为了支持自动挂起和自动恢复,驱动程序应该实现上面列出的所有三种方法。此外,驱动程序通过在其 usb_driver 结构中设置 .supports_autosuspend 标志来表明它支持自动挂起。然后,它负责在其中一个接口变得繁忙或空闲时通知 USB 核心。驱动程序通过调用这六个函数来做到这一点

int  usb_autopm_get_interface(struct usb_interface *intf);
void usb_autopm_put_interface(struct usb_interface *intf);
int  usb_autopm_get_interface_async(struct usb_interface *intf);
void usb_autopm_put_interface_async(struct usb_interface *intf);
void usb_autopm_get_interface_no_resume(struct usb_interface *intf);
void usb_autopm_put_interface_no_suspend(struct usb_interface *intf);

这些函数通过在 usb_interface 的嵌入式设备结构中维护一个使用计数器来工作。当计数器 > 0 时,该接口被认为是繁忙的,并且内核不会自动挂起该接口的设备。当使用计数器 = 0 时,该接口被认为是空闲的,并且内核可能会自动挂起该设备。

驱动程序必须小心平衡它们对使用计数器的总体更改。当一个驱动程序从其接口解除绑定时,未平衡的“get”仍然有效,如果该接口再次绑定到一个驱动程序,则会阻止该设备进入运行时挂起。另一方面,驱动程序可以通过调用 usb_autopm_* 函数来实现这种平衡,即使在它们的 disconnect 例程返回之后 -- 例如从一个工作队列例程中 -- 前提是它们保留了对该接口的活动引用(通过 usb_get_intfusb_put_intf)。

使用异步例程的驱动程序负责它们自己的同步和互斥。

usb_autopm_get_interface() 递增使用计数器,并且如果设备被挂起,则执行自动恢复。如果自动恢复失败,计数器将递减回来。

usb_autopm_put_interface() 递减使用计数器,并且如果新值 = 0,则尝试自动挂起。

usb_autopm_get_interface_async()usb_autopm_put_interface_async() 做的事情几乎与它们的非异步对应物相同。最大的区别是它们使用一个工作队列来完成它们的工作的恢复或挂起部分。因此,它们可以在一个原子上下文中被调用,例如 URB 的完成处理程序,但是当它们返回时,设备通常不会处于所需的状态。

usb_autopm_get_interface_no_resume()usb_autopm_put_interface_no_suspend() 仅仅递增或递减使用计数器;它们不尝试执行自动恢复或自动挂起。因此,它们可以在一个原子上下文中被调用。

最简单的使用模式是,驱动程序在其 open 例程中调用 usb_autopm_get_interface(),并在其 close 或 release 例程中调用 usb_autopm_put_interface()。但是也可能有其他模式。

上面提到的自动挂起尝试通常会由于某种原因而失败。例如,power/control 属性可能被设置为 on,或者同一设备中的另一个接口可能不处于空闲状态。这是完全正常的。如果失败的原因是设备空闲的时间不够长,则会安排一个定时器在自动挂起空闲延迟到期时自动执行该操作。

自动恢复尝试也可能失败,尽管失败意味着该设备不再存在或无法正常运行。与自动挂起不同,自动恢复没有空闲延迟。

驱动接口的其他部分

驱动程序可以通过调用

usb_enable_autosuspend(struct usb_device *udev);

在其 probe() 例程中,如果它们知道该设备能够正确地挂起和恢复,来为它们的设备启用自动挂起。这与将 auto 写入设备的 power/control 属性完全等效。同样,驱动程序可以通过调用

usb_disable_autosuspend(struct usb_device *udev);

来禁用自动挂起。这与将 on 写入 power/control 属性完全相同。

有时驱动程序需要确保在自动挂起期间启用远程唤醒。例如,如果用户不能通过在键盘上打字来导致键盘执行远程唤醒,那么自动挂起键盘就没有多大意义。如果驱动程序将 intf->needs_remote_wakeup 设置为 1,那么如果远程唤醒不可用,内核将不会自动挂起该设备。(但是,如果设备已经自动挂起,则设置此标志不会导致内核自动恢复它。通常,驱动程序会在其 probe 方法中设置此标志,此时保证设备不会被自动挂起。)

如果一个驱动程序在中断上下文中异步地执行其 I/O,它应该在开始输出之前调用 usb_autopm_get_interface_async(),并在输出队列耗尽时调用 usb_autopm_put_interface_async()。当它接收到一个输入事件时,它应该在事件处理程序中调用

usb_mark_last_busy(struct usb_device *udev);

这告诉 PM 核心该设备刚刚繁忙,因此下一次自动挂起空闲延迟到期应该被推迟。许多 usb_autopm_* 例程也会进行此调用,因此驱动程序只需要在中断驱动的输入到达时担心。

异步操作总是会受到竞争的影响。例如,一个驱动程序可能会在核心刚刚决定设备空闲了足够长的时间,但还没有来得及调用驱动程序的 suspend 方法时调用 usb_autopm_get_interface_async() 例程。suspend 方法必须负责与 I/O 请求例程和 URB 完成处理程序同步;如果驱动程序需要使用该设备,它应该导致自动挂起失败并返回 -EBUSY。

不应该允许外部挂起调用以这种方式失败,只有自动挂起调用。驱动程序可以通过将 PMSG_IS_AUTO() 宏应用于 suspend 方法的消息参数来区分它们;对于内部 PM 事件(自动挂起),它将返回 True,对于外部 PM 事件,它将返回 False。

互斥

对于外部事件 -- 但不一定是对于自动挂起或自动恢复 -- 当调用 suspendresume 方法时,设备信号量 (udev->dev.sem) 将被持有。这意味着外部挂起/恢复事件与对 probedisconnectpre_resetpost_reset 的调用是互斥的;USB 核心保证这也适用于自动挂起/自动恢复事件。

如果一个驱动程序想要在一些关键部分阻止所有挂起/恢复调用,最好的方法是锁定该设备并调用 usb_autopm_get_interface()(并在关键部分的末尾执行相反的操作)。持有设备信号量将阻止所有外部 PM 调用,并且 usb_autopm_get_interface() 将阻止任何内部 PM 调用,即使它失败了。(练习:为什么?)

动态 PM 和系统 PM 之间的交互

动态电源管理和系统电源管理可以通过几种方式进行交互。

首先,当发生系统挂起时,一个设备可能已经被自动挂起。由于系统挂起应该尽可能透明,因此设备应该在系统恢复后保持挂起状态。但是这个理论可能无法很好地在实践中应用;随着时间的推移,内核在这方面的行为已经发生了变化。从 2.6.37 开始,策略是在系统恢复期间恢复所有设备,并让它们在之后处理它们自己的运行时挂起。

其次,动态电源管理事件可能会在系统挂起过程中发生。这个窗口期很短,因为系统挂起不会花费很长时间(通常是几秒钟),但这种情况确实可能发生。例如,一个已挂起的设备可能会在系统挂起时发送远程唤醒信号。远程唤醒可能成功,这会导致系统挂起中止。如果远程唤醒不成功,它可能仍然保持活动状态,从而导致系统在系统挂起完成后立即恢复。或者,远程唤醒可能失败并丢失。最终结果取决于时序以及硬件和固件设计。

USB 端口电源控制

除了挂起端点设备和启用硬件控制的链路电源管理之外,USB 子系统还能够在某些情况下禁用端口的电源。电源通过对集线器的 Set/ClearPortFeature(PORT_POWER) 请求来控制。对于根集线器或平台内部集线器,主机控制器驱动程序将 PORT_POWER 请求转换为平台固件 (ACPI) 方法调用以设置端口电源状态。有关更多背景信息,请参见 Linux Plumbers Conference 2012 幻灯片 [1] 和视频 [2]

收到 ClearPortFeature(PORT_POWER) 请求后,USB 端口在逻辑上处于关闭状态,并且可能会触发端口实际失去 VBUS [3]。如果一个集线器将多个端口连接到一个共享电源井中,导致电源保持直到所有端口都关闭,则可以维持 VBUS。VBUS 也可以由配置为充电应用的集线器端口维护。在任何情况下,一个逻辑上关闭的端口将失去与其设备的连接,不响应热插拔事件,也不响应远程唤醒事件。

警告

关闭端口可能会导致无法热添加设备。 有关详细信息,请参见“端口电源控制的用户界面”。

就对设备本身的影响而言,它类似于设备在系统挂起期间所经历的过程,即电源会话丢失。任何在系统挂起期间行为不端的 USB 设备或驱动程序都会受到端口电源周期事件的类似影响。因此,该实现与集线器的系统恢复路径共享相同的设备恢复路径(并遵守相同的怪癖)。

端口电源控制的用户界面

端口电源控制机制使用 PM 运行时系统。通过清除端口设备的 power/pm_qos_no_power_off 标志(默认为 1)来请求断电。如果端口已断开连接,它将立即收到 ClearPortFeature(PORT_POWER) 请求。否则,它将遵守 pm 运行时规则,并要求附加的子设备和所有后代设备都挂起。此机制依赖于集线器在其集线器描述符中宣告端口电源切换(wHubCharacteristics 逻辑电源切换模式字段)。

注意,某些接口设备/驱动程序不支持自动挂起。用户空间可能需要在 usb_device 挂起之前取消绑定接口驱动程序。默认情况下,未绑定的接口设备已挂起。取消绑定时,请注意取消绑定接口驱动程序,而不是父 usb 设备的驱动程序。此外,保留集线器接口驱动程序绑定。如果 usb 设备(而非接口)的驱动程序未绑定,则内核不再能够恢复该设备。如果集线器接口驱动程序未绑定,则对其子端口的控制将丢失,并且所有连接的子设备都将断开连接。一个好的经验法则是,如果设备的 'driver/module' 链接指向 /sys/module/usbcore,则取消绑定它将干扰端口电源控制。

端口电源控制的相关文件示例。注意,在此示例中,这些文件相对于 usb 集线器设备(前缀)

prefix=/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1

                 attached child device +
             hub port device +         |
hub interface device +       |         |
                     v       v         v
             $prefix/3-1:1.0/3-1-port1/device

$prefix/3-1:1.0/3-1-port1/power/pm_qos_no_power_off
$prefix/3-1:1.0/3-1-port1/device/power/control
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf0>/driver/unbind
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intf1>/driver/unbind
...
$prefix/3-1:1.0/3-1-port1/device/3-1.1:<intfN>/driver/unbind

除了这些文件之外,某些端口可能具有指向另一个集线器上的端口的“peer”链接。 预期是所有 superspeed 端口都具有 hi-speed peer

$prefix/3-1:1.0/3-1-port1/peer -> ../../../../usb2/2-1/2-1:1.0/2-1-port1
../../../../usb2/2-1/2-1:1.0/2-1-port1/peer -> ../../../../usb3/3-1/3-1:1.0/3-1-port1

与“companion ports”或“ehci/xhci 共享切换端口”不同,peer 端口只是组合到单个 usb3 连接器中的高速和超高速接口引脚。 Peer 端口共享相同的祖先 XHCI 设备。

当超高速端口断电时,设备可能会降级其连接并尝试连接到高速引脚。 该实现采取措施防止这种情况

  1. 端口挂起按顺序排列,以保证高速端口在其超高速 peer 端口允许断电之前断电。 这意味着在超高速端口上将 pm_qos_no_power_off 设置为零可能不会导致端口断电,直到其高速 peer 端口进入其运行时挂起状态。 如果用户空间想要保证超高速端口断电,则必须注意对挂起进行排序。

  2. 端口恢复按顺序排列,以强制超高速端口在其高速 peer 端口之前通电。

  3. 端口恢复始终会触发附加的子设备恢复。 在电源会话丢失后,设备可能已被移除,或者需要重置。 当父端口重新获得电源时恢复子设备可以解决这些状态,并将最大端口电源周期频率限制在子设备可以挂起 (autosuspend-delay) 和恢复 (reset-resume latency) 的速率。

与端口电源控制相关的 Sysfs 文件

<hubdev-portX>/power/pm_qos_no_power_off:

此可写标志控制空闲端口的状态。 一旦所有子设备和后代设备都已挂起,只要 pm_qos_no_power_off 为“0”,该端口就可以挂起/断电。 如果 pm_qos_no_power_off 为“1”,则无论后代的统计信息如何,该端口都将保持活动/通电状态。 默认为 1。

<hubdev-portX>/power/runtime_status:

此文件反映端口是“活动”(通电)还是“已挂起”(逻辑上关闭)。 没有向用户空间指示是否仍然提供 VBUS。

<hubdev-portX>/connect_type:

一个向用户空间指示端口的位置和连接类型的咨询只读标志。 它返回四个值之一“hotplug”、“hardwired”、“not used”和“unknown”。 除了 unknown 之外,所有值均由平台固件设置。

hotplug 指示平台上的外部可连接/可见端口。 通常,用户空间会选择保持此类端口通电以处理新的设备连接事件。

hardwired 指的是不可见但可连接的端口。 例如,可以通过外部开关断开连接的 USB 蓝牙的内部端口或具有硬连线 USB 摄像头的端口。 只要 pm_qos_no_power_off 与任何门控连接的开关协调,允许这些端口挂起应该是安全的。 用户空间必须安排在端口断电之前连接设备,或者在通过开关启用连接之前激活端口。

not used 指的是预计永远不会连接设备的内部端口。 这些可能是空的内部端口,或者是在平台上未物理暴露的端口。 认为可以随时断电。

unknown 意味着平台固件未提供此端口的信息。 最常见的是指外部集线器端口,对于策略决策,应将其视为“hotplug”。

注意

  • 由于我们依赖 BIOS 来正确获取此 ACPI 信息,因此 USB 端口描述可能缺失或错误。

  • 请注意清除 pm_qos_no_power_off。 一旦电源关闭,此端口将不会响应新的连接事件。

一旦连接了子设备,在允许端口断电之前,将应用其他约束。

<child>/power/control:

必须为 auto,并且在 <child>/power/runtime_status 反映“挂起”状态之前,端口不会断电。 默认值由子设备驱动程序控制。

<child>/power/persist:

对于大多数设备,此值默认为 1,并指示内核是否可以在电源会话丢失(挂起/端口电源事件)时保持设备的配置。 当此值为 0(有问题的设备)时,端口断电将被禁用。

<child>/driver/unbind:

能够唤醒的设备将阻止端口断电。 此时,清除接口设备的 usb 内部唤醒功能的唯一机制是取消绑定其驱动程序。

相对于端口设备,断电先决条件设置的摘要

echo 0 > power/pm_qos_no_power_off
echo 0 > peer/power/pm_qos_no_power_off # if it exists
echo auto > power/control # this is the default value
echo auto > <child>/power/control
echo 1 > <child>/power/persist # this is the default value

建议的用户空间端口电源策略

如上所述,用户空间需要小心谨慎地确定哪些端口启用断电。

默认配置是所有端口都以 power/pm_qos_no_power_off 设置为 1 开始,从而导致端口始终保持活动状态。

如果确信平台固件对端口的描述(端口的 ACPI _PLD 记录填充了“connect_type”)是正确的,则用户空间可以为所有“not used”端口清除 pm_qos_no_power_off。 只要端口的断电与任何连接开关协调,对于“hardwired”端口也可以这样做。

更积极的用户空间策略是在某些外部因素表明用户已停止与系统交互时,为所有端口启用 USB 端口断电(将 <hubdev-portX>/power/pm_qos_no_power_off 设置为 0)。 例如,发行版可能希望在屏幕变为空白时关闭所有 USB 端口的电源,并在屏幕变为活动状态时重新为其供电。 智能手机和平板电脑可能希望在用户按下电源按钮时关闭 USB 端口的电源。