uhubctl
uhubctl 是一个用于在智能 USB 集线器上逐端口控制 USB 电源的实用工具。
智能集线器定义为实现逐端口电源切换的集线器。
此代码的原始构想受 Niibe Yutaka 的 hub-ctrl.c 启发:
https://www.gniibe.org/development/ac-power-control-by-USB-hub
兼容的 USB 集线器
请注意,没有多少 USB 集线器能够正确支持逐端口电源切换。
其中一些已不再生产,可能很难找到。
以下是已知兼容 USB 集线器的列表:
| 制造商 | 产品 | 端口 | USB | VID:PID | 发布年份 | 停产 |
|---|
| Acer | BE270U 显示器 (见) | 4 | 3.0 | 2109:2811 | 2016 | |
| AmazonBasics | HU3641V1 (RPi 问题) | 4 | 3.0 | 2109:2811 | 2013 | |
| AmazonBasics | HU3770V1 (RPi 问题) | 7 | 3.0 | 2109:2811 | 2013 | |
| AmazonBasics | HU9003V1EBL, HUC9003V1EBL | 7 | 3.1 | 2109:2817 | 2018 | |
| AmazonBasics | HU9002V1SBL, HU9002V1EBL, HU9002V1ESL, HHU9002V1EZPR01 (注) | 10 | 3.1 | 2109:2817 | 2018 | |
| AmazonBasics | HUC9002V1SBL, HUC9002V1EBL, HUC9002V1ESL | 10 | 3.1 | 2109:2817 | 2018 | |
| AmazonBasics | U3-7HUB (仅适用于 1 个充电端口) | 7 | 3.0 | 2109:2813 | 2020 | |
| Anker | AK-68ANHUB-BV7A-0004 (注) | 7 | 3.0 | 2109:0812 |
此表绝非完整列表。
如果你的集线器可以与 uhubctl 配合使用,但未在上面列出,请通过
在 https://github.com/mvp/uhubctl/issues 打开新 issue 提交报告,
以便我们将其添加到支持表中。报告中请提供确切的产品型号,
并附上 uhubctl 的输出,同时请按下文 FAQ 中的描述测试 VBUS 关闭支持。
请注意,不少现代主板内置的根集线器就支持此功能,
你可能根本不需要购买任何外部集线器。
USB 3.0 双接口说明
如果你将 USB 3.0 集线器连接到 USB3 上游端口,它会被检测为
两个独立的虚拟集线器:USB2 和 USB3,你的 USB 设备将根据其能力和连接速度
连接到 USB2 或 USB3 虚拟集线器。
要控制此类集线器的电源,必须同时关闭/开启 USB2 和 USB3
两个虚拟集线器的电源,才能让电源关闭/开启的变更生效。uhubctl 将尝试自动执行此操作
(除非你使用选项 -e 禁用此行为)。
不幸的是,虽然大多数集线器会切断 USB 数据连接,但有些可能仍不会切断端口的 VBUS,
这意味着已连接的手机可能仍会继续从被 uhubctl 关闭电源的端口充电。
安装
对于 Linux 和 MacOS,uhubctl 可通过标准包管理器获得,
并且可以通过以下命令安装:
- MacOS:
brew install uhubctl 或 sudo port install uhubctl
- ⚠️ MacOS 26+ 需要 libusb 1.0.30 或更高版本!
- Ubuntu/Debian/Raspbian:
sudo apt install uhubctl
- Redhat/EPEL/Fedora/CentOS:
sudo yum install uhubctl
- OpenSUSE:
sudo zypper install uhubctl
- FreeBSD:
cd /usr/ports/sysutils/uhubctl && sudo make install
不过,通过标准包管理器安装的 uhubctl 不一定是最新版本,甚至可能严重滞后于当前版本。
如果最新发布的 uhubctl 版本比你包管理器提供的版本更新,
你可能需要按照下文所述从源码编译并安装。
编译
该工具已测试可在 Linux(Ubuntu/Debian/Raspbian、
Redhat/EPEL/Fedora/CentOS、Arch Linux、Gentoo、openSUSE、Buildroot)、
FreeBSD、NetBSD、SunOS 和 MacOS 上编译和运行。
虽然 uhubctl 可以在 Windows 上编译,但 USB 电源切换在 Windows 上无法工作,因为 libusb
使用的是 winusb.sys 驱动程序,而据 Microsoft 称该驱动程序不支持
必要的 USB 控制请求。
如果 libusb 开始支持 Windows 上的其他驱动程序,此问题可能会得到修复。
请注意,强烈建议安装 pkgconf(或 pkg-config)工具
(通常默认已安装)。
首先,你需要安装 libusb-1.0 库(需要 1.0.13 或更高版本,
建议使用 1.0.23 或更高版本):
- Ubuntu:
sudo apt-get install libusb-1.0-0-dev pkgconf
- Redhat:
sudo yum install libusb1-devel pkgconf
- OpenSUSE:
sudo zypper install libusb-1_0-devel pkgconf
- MacOS:
brew install libusb pkgconf,或 sudo port install libusb-devel pkgconf
- FreeBSD:
pkg install gmake pkgconf(libusb 默认已包含)
- NetBSD:
sudo pkgin install libusb1 gmake pkgconf
- Windows:待定?
要获取 uhubctl 源码并编译:
git clone https://github.com/mvp/uhubctl
cd uhubctl
make
这应该会生成 uhubctl 可执行文件。
你可以使用以下命令将其安装到系统中的 /usr/sbin/uhubctl:
请注意,在某些操作系统(例如 FreeBSD/NetBSD)上,你需要使用 gmake 来构建。
用法
⚠️ 在 Linux 上,请使用 sudo 或按下文所述配置 USB 权限!
要列出所有受支持的集线器:
你可以这样控制一个或多个 USB 端口的电源:
这意味着在默认智能集线器上操作,并关闭端口 2(-p 2)的电源(-a off,或 -a 0)。
支持的操作有 off/on/cycle/toggle(或 0/1/2/3)。
cycle 表示关闭电源,等待一段延迟(可通过 -d 配置),然后重新打开。
端口可以是逗号分隔的列表,也可以使用 - 表示范围,例如 2、2,4、 或 。> ⚠️ 关闭内置 USB 端口可能会切断键盘或鼠标的连接,
因此请小心选择要关闭的端口!
如果您连接了多个智能 USB 集线器,应使用 -l(位置)参数选择要控制的特定集线器。
要查找集线器位置,只需不带任何参数直接运行 uhubctl 即可。
集线器位置形如 b-x.y.z,其中 b 是 USB 总线编号,x、y、z...
是链中所有集线器的端口号,从给定 USB 总线的根集线器开始。
该地址是半稳定的——即使您将 USB 设备拔下/重新插入(或关闭/打开电源)到同一物理 USB 端口,
它也不会改变(Linux 内核也使用此方法)。
Linux USB 权限
在 Linux 上,您应配置 udev USB 权限(否则将不得不使用 sudo uhubctl 以 root 身份运行)。
从 Linux 内核 6.0 开始,有了用于打开或关闭 USB 集线器端口的标准接口,
uhubctl 将尝试使用它(而不是 libusb)来设置端口状态。
这就是为什么针对 6.0+ 内核还有额外规则的原因。
在运行较旧内核版本的系统上保留这些规则也没有坏处。
要修复 USB 权限,首先运行 sudo uhubctl 并记下需要控制的集线器的所有 vid:pid。
然后,将如下 udev 规则添加到文件 /etc/udev/rules.d/52-usb.rules 中
(将 2001 替换为您的集线器供应商 ID,或完全移除 ATTR{idVendor} 过滤器以允许访问任何 USB 集线器):
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="2001"
# Linux 6.0 or later (its ok to have this block present for older Linux kernels):
SUBSYSTEM=="usb", DRIVER=="hub|usb", \
RUN="/bin/sh -c \"chmod -f 666 $sys$devpath/*port*/disable || true\""
请注意,对于 USB3 集线器,某些集线器在同一芯片的 USB2 与 USB3 组件上使用不同的供应商 ID,
要使 uhubctl 正常工作,两者都需要权限。
例如,对于 Raspberry Pi 4B,您需要添加这两行(或移除 idVendor 过滤器):
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="2109"
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0666", ATTR{idVendor}=="1d6b"
如果您不喜欢完全开放的 0666 模式,可以按如下方式通过用户组限制访问:
SUBSYSTEM=="usb", DRIVER=="hub|usb", MODE="0664", GROUP="dialout"
# Linux 6.0 or later (its ok to have this block present for older Linux kernels):
SUBSYSTEM=="usb", DRIVER=="hub|usb", \
RUN+="/bin/sh -c \"chown -f root:dialout $sys$devpath/*port*/disable || true\"" \
RUN+="/bin/sh -c \"chmod -f 660 $sys$devpath/*port*/disable || true\""
然后将允许的用户添加到 dialout 用户组:
sudo usermod -a -G dialout $USER
要使 udev 规则更改生效,请重启或运行:
sudo udevadm trigger --attr-match=subsystem=usb
为方便起见,这里 提供了可直接使用的 udev 规则。
常见问题
什么是 USB 每端口电源切换?
根据 USB 2.0 规范,USB 集线器可以声明为无电源切换、
组控(所有端口同时)电源切换或每端口(单独)电源切换。
请注意,默认情况下 uhubctl 只会检测支持每端口电源切换的 USB 集线器
(但您可以使用 -f 选项强制它尝试操作不支持的集线器)。
您可以通过 sudo lsusb -v 查看您的硬件支持哪种电源切换:
无电源切换:
wHubCharacteristic 0x000a
No power switching (usb 1.0)
Per-port overcurrent protection
组控电源切换:
wHubCharacteristic 0x0008
Ganged power switching
Per-port overcurrent protection
每端口电源切换:
wHubCharacteristic 0x0009
Per-port power switching
Per-port overcurrent protection
如何检查我的 USB 集线器是否受 uhubctl 支持?
- 运行
sudo uhubctl。如果您的集线器未被列出,则不受支持。
或者,您可以运行 sudo lsusb -v 并检查是否有
Per-port power switching——如果在 lsusb 输出中看不到此行,
则说明集线器不受支持。
- 检查 VBUS(电压)关闭支持:将手机、USB 灯
或 USB 风扇插入集线器的 USB 端口。
尝试使用
uhubctl 关闭该端口的电源,并检查
手机是否停止充电、USB 灯是否停止发光或 USB 风扇是否停止转动。
如果 VBUS 没有关闭,说明您的集线器制造商没有包含
真正切断电源的电路。这样的集线器仍然可以
切断 USB 数据连接,但无法关闭电源,
我们不认为这是受支持的设备。
- 如果以上测试成功,请报告您的集线器——
在 https://github.com/mvp/uhubctl/issues 打开新 issue,
以便我们将其添加到受支持设备列表中。
请不要报告不受支持的集线器,除非它是
某个已列出的受支持型号的不同硬件修订版。
在 Linux 上关闭端口电源后,USB 设备不会被移除
关闭 USB 端口电源后,udev 不会收到任何事件,因此它会保留设备文件。
但是,尝试访问这些设备文件将导致 IO 错误。
这是 Linux 内核问题,并且已在 Linux 内核 6.0 或更高版本的
系统中自 uhubctl 2.5.0 起修复。
如果您仍在使用 Linux 5.x 或更早版本,则可以使用以下变通方法解决此问题:
sudo uhubctl -a off -l ${location} -p ${port}
sudo udevadm trigger --action=remove /sys/bus/usb/devices/${location}.${port}/
设备文件将被 udev 移除,但 USB 设备在 lsusb 中仍然可见。
请注意,路径 /sys/bus/usb/devices/${location}.${port} 仅当该端口上检测到设备时才存在。
当您重新打开电源时,设备应能正确重新枚举(无需再次调用 udevadm)。
在 Linux 上电源在几秒钟后又恢复
内核中的某些设备驱动程序会对 USB 设备被关闭感到意外,并自动尝试重新为其供电。
这是 Linux 内核问题,并且已在 Linux 内核 6.0 或更高版本的
系统中自 uhubctl 2.5.0 起修复。
如果您仍在使用 Linux 5.x 或更早版本:
您可以使用 -r N 选项(N 为 10 到 1000 之间的某个数字)来解决此问题——
uhubctl 将尝试快速连续多次关闭电源,这应该可以抑制该行为。
在使用 uhubctl 关闭电源之前,先禁用相关设备的 USB 授权应该会有帮助:
echo 0 > sudo tee /sys/bus/usb/devices/${location}.${port}/authorized
如果您的设备是 USB 大容量存储设备,在调用 uhubctl 之前先调用 udisksctl 也会有帮助:
sudo udisksctl power-off --block-device /dev/disk/...`
sudo uhubctl -a off ...
检测到多个 4 端口集线器,但我只连接了一个 7 端口集线器
许多集线器制造商使用基本的 4 端口 USB 芯片构建其 USB 集线器。
例如,要制作 7 端口集线器,他们将两个 4 端口集线器菊花链连接——1 个端口用于菊花链连接,
因此形成 4+4-1=7 端口集线器。类似地,10 端口集线器可以由 3 个 4 端口集线器
菊花链连接而成,从而得到 4+4+4-2=10 个可用端口。
请注意,切勿尝试更改用于将内部集线器菊花链连接在一起的端口的电源状态。
这样做会扰乱内部集线器电路,并导致不可预测的行为。
Raspberry Pi 关闭所有端口的电源,而不仅仅是我指定的那一个
这是 Raspberry Pi 硬件设计的限制。
作为变通方法,您可以从上面的受支持列表购买任意外部 USB 集线器,
将其连接到 Raspberry Pi 的任意 USB 端口,并独立控制其端口的电源。
此外,还有一些专为 Raspberry Pi 设计的受支持集线器,例如 UUGear MEGA4。
供参考,受支持的 Raspberry Pi 型号具有以下内部 USB 拓扑:
Raspberry Pi B+,2B,3B
Raspberry Pi 3B+
Raspberry Pi 4B
⚠️ 如果您的 VL805 固件早于 00137ad(使用 sudo rpi-eeprom-update 检查),
您必须更新固件
才能使电源切换在 RPi 4B 上正常工作。
-
USB2 集线器 1,1 个端口,仅连接到下面的集线器 1-1。
-
USB2 集线器 1-1,4 个端口组控,与下面的 USB3 集线器 2 配对:
-
USB3 集线器 2,4 个端口组控,与上面的 USB2 集线器 1-1 配对:
-
USB2 集线器 3,1 个端口,OTG 控制器。电源切换不受支持。
Raspberry Pi 5
Raspberry Pi 5 有两个 USB2 端口和两个 USB3 端口(共 4 个)。
这些端口以非常奇怪的配置连接到 4 个不同的 USB 集线器 1、2、3、4
(但根据操作系统和硬件修订版本,相关集线器可能是 2、3、4、5)。
如果 USB3 设备连接到蓝色插座,它将在 USB3 集线器 2 或 4 上被检测到。
如果 USB2 设备连接到任何插座,或 USB3 设备连接到黑色插座,
它将在 USB2 集线器 1 或 3 上被检测到。
无论 USB2/USB3 连接类型如何,蓝色插座始终是端口 1,
黑色插座始终是端口 2。
4 个板载 USB 集线器中的每一个都宣称支持每端口电源切换,但事实并非如此。
实际上,Raspberry Pi 5 的全部 4 个端口被归到同一个组中一起控制,
尽管它们属于 4 个不同的逻辑 USB 集线器。
要关闭 VBUS 电源,必须使用以下命令在所有板载集线器和端口上将其禁用: ```
uhubctl -l 2 -a 0
uhubctl -l 4 -a 0
若要重新启用: ```
uhubctl -l 2 -a 1
uhubctl -l 4 -a 1
请注意,仅当所有端口都关闭时 VBUS 电源才会关闭 -
启用任意一个端口就会为全部 4 个端口重新启用 VBUS 电源。
使用 uhubctl 的知名项目
版权
版权所有 (C) 2009-2026 Vadim Mikhailov
本文件可根据 GNU 通用公共许可证第 2 版的条款和条件
进行分发。