作者cuello (cuello)
看板Linux
标题Re: [问题] 2.4G 无线滑鼠键盘凭甚麽关我笔电?
时间Sat Apr 10 01:39:52 2021
上一篇我所提的问题, 有了部份解答, 回文整理一下
我讲的琐碎一点, 大家比较容易发现我的盲点
以後有碰到类似问题的人也可以省下一点时间
搞不好, 最常跑来回顾细节的, 就是我自己
因为我们会游走在 "kernel 边缘" (我现在是 5.10.28)
所以我们所讨论的这些, 基本上 *应该要* 适用所有人
先大致复习一下有点复杂的问题
我有个无线滑鼠直键盘, 称为 空中飞鼠
它身上长了一个电源键, 但是按下去会同时
1. 透过 IR LED 关掉电视 2. 透过 USB 关掉笔电
但是我不要它关我笔电...
前情提要: (抄过来)
> 1. 它凭啥关我笔电?甚麽管道?我自己都还要 sudo, 它谁?
> 2. 如何告诉我的系统,不准接受 hid 来的 shutdown 命令
===============================================
我们从第二个问题开始, 再回到第一个问题
===============================================
实务上, 会有不少人碰到类似的问题, 例如
USB 键盘右上角长了一颗电源键, 因为很接近常用键
动不动就会无意间按到, 导致无预警关机, 例如:
https://i.stack.imgur.com/9EgBZ.jpg
也有人用的 USB HID 长的像这样
https://www.orbsmart.de/wp-content/uploads/2018/09/orbsmart-WA-1_1-1.jpg
这比较像我这支所谓的 "空中飞鼠", 大概因为它有陀螺仪,
可以像玩 Wii 那样在空中挥舞, 就带动萤幕上的鼠标
https://i.imgur.com/7mJwOoi.jpg
另外, 要是有一天, 你突然发现机器是关机状态,
不要排除是你家的猫, 这时也会想要 disable 那个键
或是, 有时会需要把 sleep/suspend/hibernate 键停用
https://i.stack.imgur.com/524Oj.jpg

最简单的方法就是修改 /etc/systemd/logind.conf
把 HandlePowerKey=poweroff 改为 =ignore
或是把 HandleLidSwitch 改为 =ignore
(...)
(至於 /etc/acpi/.... 我到现在还是一头雾水,
这中间大概也夹杂了一些过渡性的 kernel 问题
还有我的笔电硬体特别的状况... 所以不想再去想了)
这是 Bencrie 一开始就想到的, 但是他也没忘记有个前提
就是 *如果你用 systemd 的话*, 换句话说,
并不是所有人, 所有系统, 都可以从这边着手
而且改 logind.conf 的话, 只能让所有的电源键都失效.
因为它在很上层, 应该无法分辨 interrupt 是来自哪个电源键.
我的情形是, 最好只停用 "空中飞鼠" 的电源键 (不含 IR LED),
保留笔电上面电源键的功能.
我所能找到的是:
方法一, Vojtech Pavlik 写的 evtest(1) 是个好朋友.
**************************************************
(不是 xev(1), xinput(1), showkey(1)... 等, 选对工具是关键)
$ sudo apt-get install evtest
$ sudo evtest --grab # 需要 root, 要记得 --grab
/dev/input/event0: AT Translated Set 2 keyboard
(...)
/dev/input/event12: Power Button <== 这两个是笔电的电源键
/dev/input/event13: Power Button <== 但不知为何有两个一样的
(...)
/dev/input/event18: FREEWAY TECHNOLOGY RFIC-MOUSE Keyboard
/dev/input/event19: FREEWAY TECHNOLOGY RFIC-MOUSE Consumer Control
/dev/input/event20: FREEWAY TECHNOLOGY RFIC-MOUSE System Control
/dev/input/event21: FREEWAY TECHNOLOGY RFIC-MOUSE
/dev/input/event22: FREEWAY TECHNOLOGY RFIC-MOUSE
(...)
Select the device event number [0-27]:
先确定想要停用的是哪一行的, 我要的是 "event20 ... System Control"
所以就打 "20", 然後那个电原键就已经没用了... (直到 Ctrl-C)
下一个画面如下:
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x25a7 product 0x2402 version 0x101
Input device name: "FREEWAY TECHNOLOGY RFIC-MOUSE System Control"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 116 (KEY_POWER) <==== 这个才是标的
Event code 139 (KEY_MENU) <---+
Event code 142 (KEY_SLEEP) <---+ 这三个我不懂, 没反应...
Event code 143 (KEY_WAKEUP) <---+
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit) <-- 敲 Ctrl-C 结束
试按一下红色 [电源键]
Event: time 1617615928.788579, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10081
Event: time 1617615928.788579, type 1 (EV_KEY), code 116 (KEY_POWER), value 1
Event: time 1617615928.788579, -------------- SYN_REPORT ------------
Event: time 1617615928.900571, type 4 (EV_MSC), code 4 (MSC_SCAN), value 10081
Event: time 1617615928.900571, type 1 (EV_KEY), code 116 (KEY_POWER), value 0
Event: time 1617615928.900571, -------------- SYN_REPORT ------------
^C $
按 Ctrl-C 出来之後就不要再按电源键了, 会关机的.
那~~~ 我是怎麽确定是 event20 的呢?是一个一个试出来的!
用 sudo evtest --grab 测试其实很方便, 因为 --grab 就是叫
evtest 把接到的 input event "据为己有", 或是换句话说,
让这个 interrupt "到此为止", 不再传播下去, 或"上去"
所以其它的 handlers 都不会知到发生过这个按键的动作.
所以, "空中飞鼠" 来的 poweroff event 就被拦截在一个很低的位阶
不涉及 X, 或 WM, 或 desktop.
NB 1: 最好不要去 --grab 那个 event0, 因为会不能打 Ctrl-C
只能由外面 ssh 进来 killall evtest.
NB 2: 这时, 笔电跟空中飞鼠上其它所有的键似乎都没受到影响.
NB 3. 不能直接去移除不想要的 /dev/nput/event20, 没有用的
因为它是 opened 的, 如果在另外一个 terminal 做
$ sudo fuser -v /dev/input/event20
会看到:
USER PID ACCESS COMMAND
/dev/input/event20: root 1 F.... systemd
root 264 F.... systemd-logind
我 2860 F.... Xorg
root 8679 f.... evtest
如果用另一个 evtest 的 instance, 会看到:
$ sudo evtest /dev/input/event20
(...)
***********************************************
This device is grabbed by another process.
No events are available to evtest while the
other grab is active.
In most cases, this is caused by an X driver,
try VT-switching and re-run evtest again.
Run the following command to see processes with
an open fd on this device
"fuser -v /dev/input/event20"
***********************************************
(...)
所以问题初步解决! 但是必须让 evtest(1) 一直跑下去
让它抓住这个按键 event, 丢到垃圾筒:
$ sudo evtest --grab /dev/input/event20 >/dev/null
所以, 写了一个小 script 来帮忙找到那个对的 device.
想试试看的人, 只要改一下 KEY 跟 NAME:
----- begin 我把它叫做 disable-airmouse-powerbtn -----
PROG=`basename -- "$0"`
KEY="FREEWAY TECHNOLOGY RFIC-MOUSE System Control"
NAME="KEY_POWER/空中飞鼠"
FILE="/proc/bus/input/devices"
EV=`grep -B1 -A7 "$KEY" "$FILE" | \
sed -ne 's/^H: .*\(\<[a-z0-9]*\).*$/\1/p'`
if [ "$EV" ]
then
echo "# $PROG: got \"$EV\" from $FILE"
else
echo "*** $PROG: device of $NAME not found"
exit 157
fi
EVDEV="/dev/input/$EV"
if [ "$1" = "do" ]
then
echo "# $PROG: sudo evtest --grab $EVDEV >/dev/null"
sudo evtest --grab "$EVDEV" >/dev/null
else
echo "# $PROG: $NAME found as $EVDEV"
echo "# $PROG: append \"do\" as argv1 to actually disable it"
fi
--- end of disable-airmouse-powerbtn ---
然後执行看看
$ disable-airmouse-powerbtn
# disable-airmouse-powerbtn: got "event20" from /proc/bus/input/devices
# disable-airmouse-powerbtn: KEY_POWER/空中飞鼠 found as /dev/input/event20
# disable-airmouse-powerbtn: append "do" as argv1 to actually disable it
或是直接特让它到背景执行 (这是几乎不消耗资源的):
$ setsid disable-airmouse-powerbtn do
我不晓得有没有必要 detach, 反正我是这麽做了
反正, 斩断一切的牵连, 免得被循着 cgroup 追杀...
後来, 我又把它放到 crontab 里
@reboot /我家/sh/disable-airmouse-powerbtn do &
感兴趣的人, 我推荐下载 Vojtech Pavlik 的原始码看看,
$ apt-get source evtest
它只有一个单一的 .c 档, 连个 .h 都没有
$ gcc evtest.c # 确认 ok 之後意就随你怎麽玩了
关键在
ioctl(fd, EVIOCGRAB, (void*)1);
我把必要的几行尽量节省空间地浓缩为, 感兴趣可直接 gcc:
(credit --> Vojtech Pavlik)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
int main(int argc, char* argv[])
{
struct input_event ev;
char *device, name[256];
int fd_evdev, n_bytes;
if ( NULL != argv[1] ) device = argv[1];
else {
fprintf(stderr, "usage: %s </dev/input/eventX>\n", *argv);
return EXIT_FAILURE; }
if ( -1 == ( fd_evdev = open(device, O_RDONLY) ) ) {
fprintf(stderr, "failed opening \"%s\": ", device); perror("");
return EXIT_FAILURE; }
(void) ioctl(fd_evdev, EVIOCGNAME(sizeof(name)), name);
printf("ioctl(fd_evdev, EVIOCGRAB, (void*)1)... ");
if ( 0 == ioctl(fd_evdev, EVIOCGRAB, (void*)1) ) printf("SUCCESS\n");
else { printf("FAILURE\n"); return EXIT_FAILURE; }
printf("grabbing \"%s\" at %s, Ctrl-C to quit\n", name, device);
while (1) if ((n_bytes = read(fd_evdev, &ev, sizeof ev)) < sizeof ev) break;
return EXIT_SUCCESS;
}
另一方面, 从一开始, 我就一直有个没说出来的愿望
是一个模模糊糊, 长得大概像这样的愿望:
方法二, # echo "咒术" > "/sys/回战/../的/../路径/unbind"
*********************************************************
就是, 彻底跟 "那一个" 按键解约的办法
只是一直没找到 (还是没耐心找到), 但几天前试了一下
只是, 结果很容易忘记, 要写个 script 记录下来
就像我们在写 Makefile 当作是在做 documentation 一样
因为 script 有点大, 我把他的输出 paste 过来就好
里面已经吐露了足够多的细节 (这次以 Lid Switch 为例)
$ unbind Lid
# unbind: given keyword "Lid"
# unbind: inspecting "/proc/bus/input/devices"...
# unbind: unbinding "/dev/input/event10"
# unbind: with full name "Lid Switch"
# unbind: via "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/driver/unbind"
# unbind: agenda: echo -n "PNP0C0D:00" | sudo tee "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/driver/unbind"
# unbind: append "do" in the comamnd line to carry it out
# unbind: sudo fuser -v /dev/input/10
USER PID ACCESS COMMAND
/dev/input/event10: root 261 F.... systemd-logind
或直接 $ unbind Lid do 之後, 面板开开关关就没有效用了.
这样, 我就不需要 logind.conf 改 logind.conf 了
因为, 那个档案在更新的时候常会被"更新", 就又要写个 script...
这样, 是不是很美好....? 不, 发生了一个意外...
那就是, 虽然对於 "Lid Switch", 或是笔电上的电源键来说很好用
但是对我那支 "空中飞鼠" 不行!
因为它的好几个 event handler 是绑在一起的, 我把它 unbind 掉
就会连 "空中飞鼠" 的其它按键都解约了.... :(
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 59.115.221.229 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Linux/M.1617989996.A.946.html
1F:推 in09: 感谢 04/10 07:00
2F:→ OrzOGC: 好硬核啊... 04/10 09:36
3F:→ tomsawyer: 看起来是南桥帮你注册input fd,kernel也随之生成相关 04/10 10:41
4F:→ tomsawyer: event 04/10 10:41
5F:推 Bencrie: 应该是算 systemd 设计上的不足,毕竟都它在处理的 04/10 12:45
6F:→ Bencrie: config 没办法个别设定要不要反应 power button 04/10 12:46
7F:→ Bencrie: 至於遮断 event20 我想应该可以用 udev rule 去处理 04/10 12:46
8F:推 LinBuoRen: 推解决方案 04/10 19:15
9F:推 ptrpan: 推 04/11 09:14
10F:推 goldie: 推 04/11 11:13
11F:推 H2b2t: 推 04/16 05:18