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 支持

对于 USB,只要构建内核时启用了 CONFIG_PM,就存在。。

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

什么是远程唤醒?

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

但是,某些设备具有自行恢复的能力,或者要求内核恢复它们,甚至告诉整个计算机恢复。这种能力有几个名称,例如“局域网唤醒”;我们将统称其为“远程唤醒”。当设备启用远程唤醒并被挂起时,它可能会响应某些外部事件而自行恢复(或发送请求以恢复)。例如,按下按键时挂起的键盘恢复,或者插入设备时挂起的 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 版本中,向 power/autosuspend 写入 0 会阻止设备自动挂起;此行为在 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

一些发行版在启动过程中很早的时候就通过从 initramfs 镜像运行的程序或脚本来加载 usbcore 模块。要更改参数值,您必须重建该镜像。

如果 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 方法以告知驱动程序该设备已恢复,并且也被重置。驱动程序应重新执行任何必要的设备初始化,因为设备可能已丢失其大部分或全部状态(尽管接口将与挂起之前的备用设置相同)。

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

reset_resume 方法由 USB Persist 功能使用(请参阅 系统挂起期间的 USB 设备持久性),并且在未启用 CONFIG_USB_PERSIST 的某些情况下也可以使用。当前,如果在恢复期间重置设备,并且驱动程序没有 reset_resume 方法,则驱动程序将不会收到有关恢复的任何通知。稍后的内核将调用驱动程序的 disconnect 方法;2.6.23 版本不会这样做。

USB 驱动程序绑定到接口,因此当接口挂起或恢复时,将调用它们的 suspendresume 方法。原则上,可能需要在不挂起设备的其他接口的情况下,挂起设备上的某些接口(即,强制这些接口的驱动程序停止所有活动)。USB Core 不允许这样做;当设备本身挂起时,所有接口都会挂起;当设备恢复时,所有接口都会恢复。不可能挂起或恢复设备的部分而不是全部接口。最接近的方法是取消绑定接口的驱动程序。

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

为了支持自动挂起和自动恢复,驱动程序应实现上面列出的所有三个方法。此外,驱动程序通过在其 usb_driver 结构中设置 .supports_autosuspend 标志来指示它支持自动挂起。然后,它负责在其中一个接口繁忙或空闲时通知 USB Core。驱动程序通过调用以下六个函数来完成此操作:

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” 的不平衡将在驱动程序从其接口取消绑定时仍然有效,从而阻止设备在接口再次绑定到驱动程序时进入运行时挂起状态。另一方面,允许驱动程序通过在它们的 disconnect 例程返回之后(例如,从工作队列例程中)调用 usb_autopm_* 函数来达到此平衡,前提是它们通过 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() 仅递增或递减使用计数器;它们不会尝试执行自动恢复或自动挂起。因此,可以在原子上下文中调用它们。

最简单的使用模式是,驱动程序在其打开例程中调用 usb_autopm_get_interface(),并在其关闭或释放例程中调用 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” 链接。期望所有超高速端口都具有高速对等端口。

$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

与“配套端口”或“ehci/xhci 共享切换端口”不同,对等端口只是组合到单个 USB3 连接器中的高速和超高速接口引脚。对等端口共享同一个祖先 XHCI 设备。

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

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

  2. 端口恢复是按顺序进行的,以强制超高速端口在其高速对等端口之前通电。

  3. 端口恢复始终会触发连接的子设备恢复。在电源会话丢失后,设备可能已被移除,或者需要重置。当父端口重新获得电源时恢复子设备会解决这些状态,并将最大端口电源循环频率限制在子设备可以挂起(自动挂起延迟)和恢复(重置-恢复延迟)的速率上。

与端口电源控制相关的 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:

此文件反映端口是“active”(电源打开)还是“suspended”(逻辑上关闭)。没有向用户空间指示 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 反映“suspended”状态之前不会断电。默认值由子设备驱动程序控制。

<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 端口的电源。