Linux下除/dev/input/eventx外,字符级访问类键盘HID设备的方法?
看起来你已经摸透了底层input_event的痛点——确实,手动解析按键按下/抬起、处理Shift组合这些工作繁琐又容易出错。既然你的设备已经被内核识别为键盘(Handlers里的kbd就是信号),我们完全可以利用系统现成的键盘处理层来直接拿到ASCII字符流,同时解决输入流向终端导致的登录失败问题。
下面是几个实用的方案,按推荐优先级排序:
方案一:用agetty绑定专属终端(最省心)
这个方法直接让系统帮你把扫描器的输入转换成ASCII流,你只需要从一个虚拟终端文件读取即可,完全不用碰底层事件:
创建udev规则:
编辑/etc/udev/rules.d/99-barcode-scanner.rules(文件名任意,只要以99-开头保证优先级),替换成你的扫描器的硬件属性(可以从udevadm info -a /dev/input/event9获取):SUBSYSTEM=="input", ATTRS{idVendor}=="04b8", ATTRS{idProduct}=="0202", ATTRS{name}=="EPSON TM-T88V Receipt Printer", SYMLINK+="barcode-scanner", RUN+="/usr/bin/agetty -L /dev/barcode-scanner 9600 vt100"SYMLINK+="barcode-scanner"会给设备创建一个友好的软链接/dev/barcode-scanneragetty会启动一个轻量终端会话,自动处理键盘输入到ASCII的转换
生效规则:
执行命令重新加载udev规则并触发设备识别:udevadm control --reload-rules && udevadm trigger在守护进程中读取:
你的代码可以直接打开/dev/barcode-scanner,用普通的文件读取方式(比如read())就能拿到扫描器输出的ASCII条码,连换行符(扫描器通常会附加)都帮你处理好了。而且因为agetty独占了设备,输入不会再流向tty1,也就不会出现登录失败的日志。
方案二:用libevdev简化事件处理(灵活可控)
如果你需要对扫描过程有更多控制(比如实时判断扫描状态),libevdev比直接解析struct input_event轻松太多,它帮你封装了事件处理和键码转换:
安装依赖:
先安装libevdev开发包:sudo apt install libevdev-dev # Debian/Ubuntu # 或者 sudo dnf install libevdev-devel # RHEL/CentOS核心代码示例:
大致逻辑如下(简化版):#include <libevdev/libevdev.h> #include <fcntl.h> #include <unistd.h> int main() { struct libevdev *dev = NULL; int fd = open("/dev/input/event9", O_RDONLY|O_NONBLOCK); int rc = libevdev_new_from_fd(fd, &dev); // 独占设备,阻止输入流向终端 libevdev_grab(dev, LIBEVDEV_GRAB); struct input_event ev; while (1) { rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { // 只处理按键释放事件(扫描器通常是按下即发送,释放完成) if (ev.type == EV_KEY && ev.value == 0) { const char *key_name = libevdev_event_get_key_name(ev.code); // 这里可以用系统keymap转换键码到ASCII,避免手动处理Shift组合 printf("扫描到字符: %s\n", key_name); } } usleep(1000); } libevdev_free(dev); close(fd); return 0; }这里的关键是
libevdev_grab会帮你独占设备,而且你可以利用系统的keymap来转换键码到ASCII(比如结合libkbd的相关函数),不用自己处理Shift这些组合键。
方案三:解绑设备与默认终端(手动读取)
如果你不想用额外工具或库,可以通过udev规则阻止设备输入流向tty1,然后自己利用kbd子系统的转换:
创建udev规则:
在/etc/udev/rules.d/99-barcode-scanner.rules中添加:SUBSYSTEM=="input", ATTRS{name}=="你的扫描器名称", ENV{CONSOLE}=""这条规则会告诉系统不要把这个设备的输入发送到默认console(
tty1),避免登录失败日志。读取转换后的字符:
你可以使用kbd工具的kbd_mode设置设备为字符模式,然后通过读取/dev/input/event9的事件,再用kbd_translate函数(来自libkbd)转换为ASCII字符。不过这个方法需要链接libkbd,代码量比方案二多一些,优先级不如前两个。
注意事项
- 所有操作都需要root权限,或者给你的守护进程添加
CAP_SYS_ADMIN能力(用setcap cap_sys_admin+ep your_daemon) - 一定要替换规则中的设备属性(
idVendor、idProduct、name),可以用udevadm info -a /dev/input/event9获取准确值
内容的提问来源于stack exchange,提问作者code9016

