千家信息网

Linux设备模型之input子系统的示例分析

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍Linux设备模型之input子系统的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!本节重点:输入子系统的框架结构各层对应内核中的文件位置输入子系统的事
千家信息网最后更新 2026年11月03日Linux设备模型之input子系统的示例分析

这篇文章主要介绍Linux设备模型之input子系统的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

本节重点:

  • 输入子系统的框架结构

  • 各层对应内核中的文件位置

  • 输入子系统的事件处理机制

  • 输入子系统的驱动层基本操作流程

  • 输入子系统的驱动层常用函数

本节难点:

输入子系统的事件处理机制

输入子系统的驱动工作流程

1 初识linux输入子系统

linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。

对于核心层而言,为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。

对于事件处理层而言,则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

对于linux输入子系统的框架结构如下图1所示:

图1 linux输入子系统框架结构

由上图所展现的内容就是linux输入子系统的分层结构。

/dev/input目录下显示的是已经注册在内核中的设备编程接口,用户通过open这些设备文件来打开不同的输入设备进行硬件操作。

事件处理层为不同硬件类型提供了用户访问及处理接口。例如当我们打开设备/dev/input/mice时,会调用到事件处理层的Mouse Handler来处理输入事件,这也使得设备驱动层无需关心设备文件的操作,因为Mouse Handler已经有了对应事件处理的方法。

输入子系统由内核代码drivers/input/input.c构成,它的存在屏蔽了用户到设备驱动的交互细节,为设备驱动层和事件处理层提供了相互通信的统一界面。

下图2简单描述了linux输入子系统的事件处理机制:

图2 linux输入子系统事件处理机制

由上图可知输入子系统核心层提供的支持以及如何上报事件到input event drivers。

作为输入设备的驱动开发者,需要做以下几步:

1、在驱动加载模块中,设置你的input设备支持的事件类型,类型参见表1设置

2、 注册中断处理函数,例如键盘设备需要编写按键的抬起、放下,触摸屏设备需要编写按下、抬起、绝对移动,鼠标设备需要编写单击、抬起、相对移动,并且需要在必要的时候提交硬件数据(键值/坐标/状态等等)

3、 将输入设备注册到输入子系统中

表1 Linux输入子系统支持的数据类型

EV_SYN     0x00    同步事件EV_KEY     0x01    按键事件EV_REL     0x02    相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)EV_ABS     0x03    绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)EV_MSC     0x04    其它EV_SW      0x05    开关EV_LED     0x11    按键/设备灯EV_SND     0x12    声音/警报EV_REP     0x14    重复EV_FF      0x15    力反馈EV_PWR    0x16    电源EV_FF_STATUS    0x17   力反馈状态EV_MAX    0x1f    事件类型最大个数和提供位掩码支持

由表1可知,设备所能表示的事件种类,一个设备可以选择一个或多个事件类型上报给输入子系统。

Linux输入子系统提供了设备驱动层上报输入事件的函数,在include/linux/input.h中:

voidinput_report_key(struct input_dev *dev, unsigned int code, int value);      //上报按键事件voidinput_report_rel(struct input_dev *dev, unsigned int code, int value);       //上报相对坐标事件voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);              //上报绝对坐标事件

当提交输入设备产生的输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:

void input_sync(struct input_dev *dev);

2 输入设备驱动的简单案例

在Linux内核文档的documentation/input下,有一个input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。

提供的案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:

#include                                                                                                            #include    #include      #include    #include      static struct input_dev *button_dev;     static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)   {          input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);          input_sync(button_dev);   }           static int __init button_init(void)   {          int error;                    if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) {                   printk(KERN_ERR"button.c: Can't allocate irq %d\n", button_irq);                   return -EBUSY;          }                           button_dev = input_allocate_device();          if (!button_dev) {                   printk(KERN_ERR"button.c: Not enough memory\n");                   error = -ENOMEM;                   goto err_free_irq;          }            button_dev->evbit[0] = BIT(EV_KEY);          button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);            error = input_register_device(button_dev);          if (error) {                   printk(KERN_ERR"button.c: Failed to register device\n");                   goto err_free_dev;          }            return 0;     err_free_dev:          input_free_device(button_dev);   err_free_irq:          free_irq(BUTTON_IRQ, button_interrupt);          return error;   }     static void __exit button_exit(void)   {         input_unregister_device(button_dev);          free_irq(BUTTON_IRQ, button_interrupt);  }    module_init(button_init);  module_exit(button_exit);

编写基于输入子系统的设备驱动程序需要包含,因为它包含了输入子系统的接口和所有的宏定义,这些内容在编写输入设备驱动程序时需要用到。

button_init函数说明:

当模块加载(insmod)或内核引导过程中,button_init函数会被调用。首先做的工作是获取能够正确控制硬件设备的硬件资源(例如内存、IO内存、中断和DMA),在代码中BUTTON_IRQ作为BUTTON设备的中断资源,通过request_irq()函数被申请注册。当有按键按下/释放时,调用button_interrupt()中断处理函数获取按键值BUTTON_PORT(BUTTON设备的I/O资源)。

那么输入子系统怎么能够知道这个设备为输入设备呢?通过第8行为设备定义一个用于描述一个输入设备对象。

static struct input_dev *button_dev;

定义了button_dev之后,如何通知输入子系统有新的输入设备了呢?或者说如何把一个新的输入设备加入到输入子系统中呢?可以通过输入子系统核心层input.c中提供的函数分配一个输入设备,在代码的第25行。

button_dev= input_allocate_device();

有了输入设备的描述,当事件产生时,输入子系统怎么能够知道设备产生的事件类型呢?通过32和33行的代码。

button_dev->evbit[0]= BIT(EV_KEY);  button_dev->keybit[LONG(BTN_0)]= BIT(BTN_0);

其中evbit和keybit成员分别代表设备产生的事件类型和上报的按键值。其中输入子系统的一些位操作NBITS、BIT、LONG经常被用到:

#defineNBITS(x) (((x)/BITS_PER_LONG)+1)                 //通过位x获取数组的长度  #defineBIT(x)       (1UL<<((x)%BITS_PER_LONG))       //返回位x在数组中的位域  #defineLONG(x) ((x)/BITS_PER_LONG)                        //返回位x的索引

以上的工作做完之后,即可注册为输入设备了,代码的35行。

input_register_device(button_dev);

这个函数把button_dev输入设备挂入输入设备链表中,并且通知事件处理层调用connect函数完成设备和事件处理的绑定,当用户打开设备时,便能够调用到相应的事件处理接口获得硬件上报的数据了。input_register_device()函数是会睡眠的函数,因此不能够在中断上下文和持有自旋锁的代码中调用。

当我们把上面的工作做完之后,设备驱动中唯一值得关注的就是button_interrupt()中断处理函数了。当按键动作发生,button_interrupt()函数被调用,完成事件的上报由其中的两条语句完成。

input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);  input_sync(button_dev);

其中input_report_key上报了这是一个按键事件,且它的值为inb(BUTTON_PORT) & 1,由于案例代码只产生一个按键的值,因此input_sync()在这里不起关键作用。但如果是一个触摸屏,即有x坐标和y坐标,则需要通过input_sync()函数把x和y坐标完整地传递给输入子系统。

以上是"Linux设备模型之input子系统的示例分析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

设备 输入 事件 子系统 处理 驱动 函数 按键 硬件 设备驱动 坐标 代码 核心 类型 接口 数据 核心层 内核 用户 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 山东特亿宝互联网科技有限公司 麻工互联网科技 壕鑫互联网络科技 天扬科技互联网广告 恒生科技互联网指数分析 海南科技产业互联网平台 富国互联网科技创新基金 青岛中康互联网医疗科技有限公司 科技互联网的估值 互联网科技教育视频 茂名市互联网科技产业园 宁夏众和广鑫互联网科技有限公司 荆州市昕露互联网络科技有限公司 花神(南京)互联网科技有限公司 民营互联网公司国防科技 2021互联网科技公司排名 济南小医互联网科技有限公司招聘 互联网公司属于科技吗 安徽零酒互联网科技有限公司 广东猫王互联网科技有限公司电话 互联网科技集团 互联网科技作文 科技互联网创业计划书 顶级互联网科技成果有哪些 科技互联网大厂 移动互联网年度科技创新 长沙互联网科技 江苏互联网智能科技软件 风投北京互联网科技 保山互联网科技靠谱吗
0