2.4G 無線滑鼠鍵盤憑甚麼關我筆電? - Linux

Callum avatar
By Callum
at 2021-04-10T01:39

Table of Contents

上一篇我所提的問題, 有了部份解答, 回文整理一下
我講的瑣碎一點, 大家比較容易發現我的盲點
以後有碰到類似問題的人也可以省下一點時間
搞不好, 最常跑來回顧細節的, 就是我自己

因為我們會游走在 "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 掉
就會連 "空中飛鼠" 的其它按鍵都解約了.... :(








--
Tags: Linux

All Comments

Necoo avatar
By Necoo
at 2021-04-12T21:51
感謝
Ina avatar
By Ina
at 2021-04-15T18:02
好硬核啊...
Jacky avatar
By Jacky
at 2021-04-18T14:13
看起來是南橋幫你註冊input fd,kernel也隨之生成相關
event
Ursula avatar
By Ursula
at 2021-04-21T10:25
應該是算 systemd 設計上的不足,畢竟都它在處理的
Iris avatar
By Iris
at 2021-04-24T06:36
config 沒辦法個別設定要不要反應 power button
至於遮斷 event20 我想應該可以用 udev rule 去處理
Necoo avatar
By Necoo
at 2021-04-27T02:47
推解決方案
Ingrid avatar
By Ingrid
at 2021-04-29T22:58
Sierra Rose avatar
By Sierra Rose
at 2021-05-02T19:10
Agatha avatar
By Agatha
at 2021-05-05T15:21

想問user權限問題

Emma avatar
By Emma
at 2021-04-09T20:26
各位大大好 目前home底下有2名user A 是administrator B 是 standard 目前是希望能讓B能使用sudo 但是B不能用sudo chmod 修改A目錄的權限 進而進入A的目錄內做任何操作 但A B都能用sudo對系統做一些管理或安裝東西 不同user之間沒辦法干涉對方的檔案 ...

centos8替代 almalinux出正式版

Yedda avatar
By Yedda
at 2021-04-08T23:37
由cloudlinux公司維護的RHEL8 fork almalinux8 正式release https://almalinux.org/ https://repo.almalinux.org/almalinux/8/isos/x86_64/ 目前只有x64版本 ARM要再等等 doc ...

麥克風沒聲音

Elma avatar
By Elma
at 2021-04-08T18:55
各位好 小弟使用的是Xubuntu20.04 主板是b550m mortar 使用denon d1200的耳麥線 接在NR400前面板的耳麥孔 pavucontrol裡看到耳機麥克風都有偵測到 但麥克風始終沒聲音 把增益拉大後看起來像是雜訊 想到可能是CTIA/OMTP的問題? 想請教各位有沒有不用轉 ...

版上用gentoo的人多嗎?

Hardy avatar
By Hardy
at 2021-03-30T03:42
我今天灌了Gentoo並試著安裝KDE桌面環境,只能說累死我了。 我硬體是Ryzen 3 3300X,32G RAM,以amd64/desktop/plasma/systemd的profile安裝, 使用自動編譯核心。 從29號下午開始安裝到現在30號清晨三點還在編譯KDE apps,可能要天亮才會結束吧, ...

apt與dpkg顯示套件安裝版本不一致

Erin avatar
By Erin
at 2021-03-22T17:29
請教各位高手,是否有遇過 ubuntu 中,使用以下兩種指令查詢套件安裝資訊時 顯示的套件安裝版本不同的情況 或者是可能發生的原因? 謝謝 1. dpkg -l |grep and#39;iiand#39; | grep and#39;sudoand#39; 2. apt list --installed ...