手柄蓝牙唤醒Steamos
背景
心血来潮把xsx卖了,用闲置的硬盘和内存装了一台steamos。想要用diy设备cos主机,第一步就是解决手柄唤醒的问题。
研究了一下发现通过2.4g接收器唤醒的难度较大,usb设备唤醒依赖于其自身固件的实现。linux在枚举设备的时候会遍历每个设备的描述符,设备声明自己支持remote wakeup才会通过SET_FEATURE(DEVICE_REMOTE_WAKEUP)请求使用USB设备的远程唤醒功能。而大多数手柄的接收器都没有实现这个功能,只好退而求其次通过蓝牙唤醒。
设置蓝牙唤醒
蓝牙手柄的唤醒链路大致是:
蓝牙手柄
↓
Bluetooth 无线连接
↓
Intel Bluetooth Controller
↓
USB Remote Wakeup
↓
USB / xHCI
↓
SteamOS Resume因此完整条件实际上是:
手柄 WakeAllowed = yes
↓
蓝牙 USB Controller power/wakeup = enabled
↓
USB/xHCI 支持 wake
↓
SteamOS 可以被唤醒最初的现象
最开始的现象是:
蓝牙手柄 → 无法唤醒 SteamOS
USB 键盘接收器 → 可以唤醒 SteamOS因为 USB 键盘仍然能唤醒,意味着xHCI/usb唤醒没问题
找到 Bluetooth HCI 对应的 USB 设备
Linux 蓝牙子系统看到的是:
/sys/class/bluetooth/hci0但蓝牙控制器本身实际上通常挂在 USB 总线上。
所以先获取 hci0 的真实设备路径:
HCI=$(readlink -f /sys/class/bluetooth/hci0/device)
echo "$HCI"在本机得到类似:
/sys/devices/pci0000:00/0000:00:14.0/usb1/1-14/1-14:1.0注意这里:
1-14:1.0是 USB Interface,而不是整个 USB Device。
我们真正需要的是它的父级:
1-14所以向父目录逐层查找,直到找到:
idVendor
idProduct完整命令:
HCI=$(readlink -f /sys/class/bluetooth/hci0/device)
USB="$HCI"
while [ "$USB" != "/" ]; do
if [ -f "$USB/idVendor" ] &&
[ -f "$USB/idProduct" ]; then
break
fi
USB=$(dirname "$USB")
done
echo "Bluetooth HCI:"
echo "$HCI"
echo
echo "Bluetooth USB device:"
echo "$USB"
echo
echo "VID:PID"
cat "$USB/idVendor"
cat "$USB/idProduct"
echo
echo "Wakeup:"
cat "$USB/power/wakeup"
echo
echo "Power control:"
cat "$USB/power/control"实际结果:
Bluetooth HCI:
/sys/devices/.../usb1/1-14/1-14:1.0
Bluetooth USB device:
/sys/devices/.../usb1/1-14
VID:PID
8087
0033
Wakeup:
disabled
Power control:
auto于是问题基本定位:
Intel Bluetooth Controller
VID:PID = 8087:0033
power/wakeup = disabled蓝牙唤醒的过程实际上是:
手柄按键
↓
蓝牙无线事件
↓
8087:0033 蓝牙控制器收到事件
↓
蓝牙控制器通过 USB Remote Wakeup
通知 xHCI
↓
系统恢复但是 Linux 给这个 USB Device 设置了 power/wakeup = disabled
相当于告诉内核:
系统进入 suspend 后,不允许这个 USB 设备成为系统唤醒源。
所以手柄按键不会产生系统 Resume
临时开启蓝牙 USB Wakeup
直接执行:
echo enabled | sudo tee "$USB/power/wakeup"然后确认:
cat "$USB/power/wakeup"应该变成:
enabled此时进行验证,手柄成功唤醒steamos
检查 BlueZ 的 WakeAllowed
虽然已经可以成功唤醒了,但以防万一还是看了下bluez
Linux USB wakeup 只是底层的一层。BlueZ 对每一个蓝牙设备还存在一个更高层的控制: WakeAllowed ,需要wkeallowed和linux的power/wakeup都开启。
先列出已经配对的设备:
bluetoothctl devices例如:
Device AA:BB:CC:DD:EE:FF Wireless Controller然后看一下wakeAllowed的状态:
bluetoothctl info AA:BB:CC:DD:EE:FF |
grep -E 'Name|Paired|Bonded|Trusted|WakeAllowed'理想状态:
Paired: yes
Bonded: yes
Trusted: yes
WakeAllowed: yes如果看到:
WakeAllowed: no可以执行:
bluetoothctl wake AA:BB:CC:DD:EE:FF on然后再次检查。
如果当前版本的 bluetoothctl 不识别 wake 命令,可以先:
bluetoothctl help确认当前 BlueZ 是否提供该子命令。
持久化
直接执行:
echo enabled > /sys/.../power/wakeup只修改当前内核中的运行时状态。
/sys 并不是普通磁盘目录,而是内核根据当前设备动态创建sysfs,之前手动写进去的规则不会永久存在,因此需要 udev 持久化规则。
创建:
sudo mkdir -p /etc/udev/rules.d然后:
sudo tee /etc/udev/rules.d/99-bluetooth-wakeup.rules >/dev/null <<'EOF'
ACTION=="add|change", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="8087", ATTR{idProduct}=="0033", TEST=="power/wakeup", ATTR{power/wakeup}="enabled"
EOF加载:
sudo udevadm control --reload-rules可以重新触发对应设备:
sudo udevadm trigger \
--action=change \
--subsystem-match=usb \
--attr-match=idVendor=8087 \
--attr-match=idProduct=0033然后重新定位 Bluetooth Controller:
HCI=$(readlink -f /sys/class/bluetooth/hci0/device)
USB="$HCI"
while [ "$USB" != "/" ]; do
if [ -f "$USB/idVendor" ] &&
[ -f "$USB/idProduct" ]; then
break
fi
USB=$(dirname "$USB")
done检查:
cat "$USB/power/wakeup"应该返回enable。重启后再次验证,同样返回enable,且手柄顺利唤醒。
SteamOS 原子更新导致配置丢失
后来想试试测试版的steamos,切换了分支。更新之后蓝牙唤醒失效。
重新检查:
cat /etc/udev/rules.d/99-bluetooth-wakeup.rules返回 没有那个文件或目录
与此同时:
cat "$USB/power/wakeup"再次是disabled
steamos原子更新会删除白名单外的全部配置
为 SteamOS 原子更新增加保留配置
于是增加:
sudo mkdir -p /etc/atomic-update.conf.dj然后:
sudo tee /etc/atomic-update.conf.d/bluetooth-wakeup.conf >/dev/null <<'EOF'
/etc/udev/rules.d/99-bluetooth-wakeup.rules
/etc/atomic-update.conf.d/bluetooth-wakeup.conf
EOF将所需配置加入白名单,这样steamos在原子化更新时会迁移这些配置文件。
最终蓝牙唤醒配置结构
最后与蓝牙相关的两个文件是:
/etc/
├── udev/
│ └── rules.d/
│ └── 99-bluetooth-wakeup.rules
│
└── atomic-update.conf.d/
└── bluetooth-wakeup.conf99-bluetooth-wakeup.rules:
ACTION=="add|change", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="8087", ATTR{idProduct}=="0033", TEST=="power/wakeup", ATTR{power/wakeup}="enabled"bluetooth-wakeup.conf:
/etc/udev/rules.d/99-bluetooth-wakeup.rules
/etc/atomic-update.conf.d/bluetooth-wakeup.conf