专栏文章目录传送门:返回专栏目录
目录
开发环境 | EVK Board | Soc | kernel |
---|---|---|---|
Ubuntu 18.04 | i.MX 8M QUAD EVK | i.MX8MQ | kernel 5.10 |
Linux Input子系统是Linux内核中的一个重要子系统,主要用于处理输入设备的输入事件,如键盘、鼠标、触摸屏等。它提供了一种统一的接口和框架,允许用户空间应用程序和设备驱动程序与输入设备交互,实现输入数据的获取和处理。
1. Linux Input 子系统介绍
-
Linux Input子系统是Linux内核中的一个重要组成部分,负责处理输入设备的输入事件。
-
输入设备可以包括键盘、鼠标、触摸屏、游戏手柄等多种类型。
-
Linux Input子系统为用户空间应用程序提供了一种统一的接口,使它们能够与输入设备交互并获取输入数据。
Linux Input 子系统作用
-
提供通用的输入设备接口:Linux Input子系统为不同类型的输入设备提供了统一的接口,使得设备驱动程序能够与内核进行交互,对于各种不一样的设备都可以得到支持。
-
用户层有统一的操作接口:对于应用程序来说提供统一的操作接口,不再去关心底层实现,只需要去管理简单的事件。
-
事件管理与传递:输入子系统管理输入事件,如键盘按键、鼠标移动等,并将事件传递到用户空间应用程序。
-
支持用户空间应用程序:借助Linux Input子系统,用户空间应用程序可以读取输入设备文件,获取输入事件并相应地作出处理。
2. Linux Input 子系统框架
input 子系统框架从hardware , kernel space, user sapce; 主要从内核空间层查看,实现input子系统从下网上看主要是有三层实现:
输入子系统设备驱动层:图中的USB hip/ Touch/GPIO 这些是对硬件进行读写访问,中断设置,负责将输入的变化通过输入事件数据传递给上一层的输入子系统核心层。
输入子系统核心层:核心层是Linux Input子系统中间层,这一层提供了规范接口,输入子系统负责对来自设备驱动层的输入事件进行管理、解码和传递。它定义了输入事件的数据结构和编码方式,确保输入事件能够准确地传递到用户空间应用程序。为驱动层提供设备的注册和操作接口,通知事件处理层对时间处理。
输入子系统事件处理层:输入事件处理层是Linux Input子系统的最高层,也是与用户空间最直接交互的层。在这一层,用户空间应用程序通过读取设备文件(通常位于/dev/input/eventX)从输入子系统核心层获取输入事件数据。
在用户层下提供了接口编程/dev/input/eventX,通常打开设备调用事件处理层进行处理事件。
3. Linux Input子系统实现
input 目录结构
./drivers/input/
├── apm-power.c
├── evbug.c
├── evdev.c
├── ff-core.c
├── ff-memless.c
├── gameport
├── input.c
├── input-compat.c
├── input-compat.h
├── input-leds.c
├── input-mt.c
├── input-polldev.c
├── input-poller.c
├── input-poller.h
├── joydev.c
├── joystick
├── Kconfig
├── keyboard
├── Makefile
├── matrix-keymap.c
├── misc
├── mouse
├── mousedev.c
├── rmi4
├── serio
├── sparse-keymap.c
├── tablet
└── touchscreen
从目录结构看到input 核心层相关代码,input 驱动层代码,包含游戏输入设备驱动(gameport),遥感手柄输入设备驱动(joystick),键盘设备输入驱动(keyboard),鼠标设备输入驱动(mouse),处理serio总线设备输入(serio),处理绘图板设备输入驱动(tablet)等等。
这里先提及三个重要的结构体:
input_dev、input_handler、input_handle;
这三个是具有很大的联系,input_dev这个是用来描述一个input设备,input_handler用来表示事件的具体处理,input_handle用来关联input_dev与input_handler,有了input_handle可以更加灵活,可以动态地去关联他们的关系,比如一个触摸设备可以支持触摸事件处理程序、手势事件处理程序和笔迹事件处理程序等。通过使用 input_handle 来关联 input_dev 和 input_handler,可以动态地为输入设备创建多个处理句柄,每个句柄关联不同的处理程序,从而实现对多个处理程序的支持。
主要核心核心代码从drivers/input/input.c开始
从input_init看到,通过calss_register注册了一个input类,并不是一个设备,在proc创建了相关的文件,注册驱动,从register_chrdev_region 主设备号采用13。
int input_register_handler(struct input_handler *handler)
int input_register_device(struct input_dev *dev)
从上面两个函数来讲,一个提供给上层使用input_register_handler,将具体的handler放入链表中,然后去input_dev_list链表去看是否存在input_dev,若支持则连接;input_register_device这个函数主要用于实际设备驱动,设备驱动将会去注册input设备,主要是将dev放入链表,然后查看handler中有没有,看看是否支持新的input_dev,支持将连接起来;
连接起来主要是依靠input_attach_handler
查看当前内核包含的handler,等于一共有有evdev.c(事件设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备),apm-power.c(电源管理)
4. 实例操作
这里举例一个实际的设备驱动,以i.MX8MQ为例:
设备树:
这是NXP的一个电源控制按键,snvs-powerkey驱动程序会检测到按键时间,并且使用input 子系统进行相应的时间传递给用户空间。
static int imx_snvs_pwrkey_probe(struct platform_device *pdev){
struct input_dev *input;
//省略了代码
//...
input = devm_input_allocate_device(&pdev->dev);
if (!input) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
error = -ENOMEM;
goto error_probe;
}
input->name = pdev->name;
input->phys = "snvs-pwrkey/input0";
input->id.bustype = BUS_HOST;
input_set_capability(input, EV_KEY, pdata->keycode);
/* input customer action to cancel release timer */
error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
if (error) {
dev_err(&pdev->dev, "failed to register remove action\n");
goto error_probe;
}
pdata->input = input;
platform_set_drvdata(pdev, pdata);
error = devm_request_irq(&pdev->dev, pdata->irq,
imx_snvs_pwrkey_interrupt,
0, pdev->name, pdev);
if (error) {
dev_err(&pdev->dev, "interrupt not available.\n");
goto error_probe;
}
//省略了代码
//...
probe中查看到,已经创建了input_dev设备,填充了相关的结构体。
通过input_set_capability设置了设备能力,该设备支持的时间和事件码。
如何调试:
查看input 设备
/sys/class/input下有两种文件,eventX与inputX,eventX代表实际的输入设备事件文件,比如鼠标,键盘等设备。inputX是对应的输入设备的对象目录,对应的设备的input_dev,包含了设备的想详细信息,产品名称,供应商等。
对于比较多的输入input设备,如何找到对应的设备:
evk_8mq:/ # cat /sys/class/input/input1/name
直接可以看到所有input 设备的信息,这里很明显可以看到snvs-pwrkey/input0就是这里提到的设备驱动。
实时调试:通过getevent命令:
可以看到多个event,目前我们电源是的/dev/input/event0,点击按键后可以看到接收到很多信息,设备也进入了休眠;
/dev/input/event0: 0001 0074 00000001
-
0001:表示输入时间的类型,这里的0001表示EV_KEY---按键事件
-
0074:事件码(Code)
-
00000001:表示输入的value (1表示按下,0是释放)
从这里的调试来看都和驱动程序能对应上,所以也是没有问题的。
5. 应用层的使用
应用层如何去操作一个设备的input;
未完!