千家信息网

Linux input怎么使用

发表于:2025-12-02 作者:千家信息网编辑
千家信息网最后更新 2025年12月02日,本篇内容主要讲解"Linux input怎么使用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Linux input怎么使用"吧!输入设备都有共性:中断驱
千家信息网最后更新 2025年12月02日Linux input怎么使用

本篇内容主要讲解"Linux input怎么使用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Linux input怎么使用"吧!

输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主设备号13,同时输入子系统也支持自动创建设备文件,这些文件采用阻塞的IO读写方式,被创建在"/dev/input/"下。如下图所示。内核中的输入子系统自底向上分为设备驱动层,输入核心层,事件处理层。由于每种输入的设备上报的事件都各有不同,所以为了应用层能够很好识别上报的事件,内核中也为应用层封装了标准的接口来描述一个事件,这些接口在"/include/upai/linux/input"中。

  • 设备驱动层是具体硬件相关的实现,也是驱动开发中主要完成的部分,

  • 输入核心层主要提供一些API供设备驱动层调用,通过这些API设备驱动层上报的数据就可以传递到事件处理层,

  • 事件处理层负责创建设备文件以及将上报的事件传递到用户空间,

input的使用

input对象描述了一个输入设备,包括它可能上报的事件,这些事件使用位图来描述,内核提供的相应的工具帮助我们构建一个input对象,大家可以参考内核文档"Documentation/input/input-programming.txt",里面对于input子系统的使用有详细的描述。

//input设备对象  struct input_dev {          const char *name;          unsigned long evbit[BITS_TO_LONGS(EV_CNT)];          unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];          unsigned long relbit[BITS_TO_LONGS(REL_CNT)];          unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];          unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];          unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];          unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];          unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];          unsigned long swbit[BITS_TO_LONGS(SW_CNT)];            unsigned long key[BITS_TO_LONGS(KEY_CNT)];          unsigned long led[BITS_TO_LONGS(LED_CNT)];          unsigned long snd[BITS_TO_LONGS(SND_CNT)];          unsigned long sw[BITS_TO_LONGS(SW_CNT)];            struct input_handle __rcu *grab;            struct device dev;            struct list_head        h_list;          struct list_head        node;  };

struct input_dev

--122--> 这个name不是设备名,input子系统的设备名在子系统源码中指定的,不是这。

--129--> 设备支持的输入事件位图,EV_KEY,EV_REL, etc

--130--> 对于按键事件,设备支持的输入子事件位图

--132--> 对于相对坐标事件,设备支持的相对坐标子事件位图

--133--> 对于绝对坐标事件,设备支持的绝对坐标子事件位图

--134--> 混杂设备的支持的子事件位图

--180-->表示这是一个device。

--182-->h_list是用来链接相关handle的链表

--183-->node用来链接其他input_dev的链表

分配/释放

//drivers/input/input.c //创建一个input对象  struct input_dev *input_allocate_device(void);//释放一个input对象  void input_free_device(struct input_dev *dev);

初始化

初始化一个input对象是使用input子系统编写驱动的主要工作,内核在头文件"include/uapi/linux/input.h"中规定了一些常见输入设备的常见的输入事件,这些宏和数组就是我们初始化input对象的工具。这些宏同时用在用户空间的事件解析和驱动的事件注册,可以看作是驱动和用户空间的通信协议,所以理解其中的意义十分重要。在input子系统中,每一个事件的发生都使用事件(type)->子事件(code)->值(value)三级来描述,比如,按键事件->按键F1子事件->按键F1子事件触发的值是高电平1。注意,事件和子事件和值是相辅相成的,只有注册了事件EV_KEY,才可以注册子事件BTN_0,也只有这样做才是有意义的。

下面就是内核约定的事件类型,对应应用层的事件对象的type域

下面这些是按键子事件的类型,可以看到对PC键值的定义

除了对常用的事件进行描述,内核同样提供了工具将这些事件正确的填充到input对象中描述事件的位图中。

//***种//这种方式非常适合同时注册多个事件  button_dev->evbit[0] = BIT_MASK(EV_KEY);             button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1);

注册/注销

初始化好了一个input对象,接下来就需要将其注册到内核

//注册input对象到内核 int input_register_device(struct input_dev *dev); //从内核注销一个input对象 void input_unregister_device(struct input_dev *dev);

驱动层报告事件

在合适的时机(由于输入最终是中断表示的,所以通常在驱动的中断处理函数中)驱动可以将注册好的事件上报,且可以同时上报多个事件,下面是内核提供的API

//上报指定的事件+子事件+值 void input_event(    struct input_dev *dev,unsigned int type,unsigned int code,int value);//上报键值    void input_report_key(struct input_dev *dev,unsigned int code,int value);//上报绝对坐标    void input_report_abs(struct input_dev *dev,unsigned int code,int value);//报告同步事件    void input_report_rel(struct input_dev *dev,unsigned int code,int value);//同步所有的上报    void input_sync(struct input_dev *dev);

上报事件有2点需要注意:

  1. report函数们并不会真的上报,只是准备上报,sync才会真的将刚刚report的事件真的上报搭input核心

  2. input核心会进行裁决再上报的事件处理层,所以对于按键事件,一定要先报1再报0(或者反过来),不能只report 1或0, 这样核心会认为是一个事件被误触发了多次而只上报一次,虽然我们真的按下了多次。

应用层解析

事件处理层最终会将驱动sync一次时所有report的事件组织成一个struct input_value[]的形式上报到应用层,在应用层从相应的设备文件中获取上报的事件的时候,需要注意:

  1. 收到数组元素的数量会比底层多一个空元素,类似于写of_device_id[]时***的空元素,这点应用层在解析的时候需要注意。

  2. 事件处理层并不会缓存收到的事件,如果有新的事件到来,即使旧的事件没有被读取,也会被覆盖,所以应用程序需要及时读取。

前文已经说过,"include/uapi/linux/input.h"中的宏是应用层和驱动层共用的通信协议,所以应用层在解析收到的struct input_value对象的时候,只需要"include "即可使用其中的宏。

/*  * The event structure itself  */  struct input_event {     struct timeval time;     __u16 type;     __u16 code;     __s32 value; };

input分析

上文已经说过,input子系统使用三层结构来实现驱动事件到应用层的传递。具体的,这三个层次每一个层次都由一条结构体链表组成,在设备驱动层,核心结构体是input_dev;在input核心层,是input_handle;在事件处理层,是input_handler。内核通过链表和指针将三者结合到一起,最终实现了input_dev和input_handler的多对多的映射关系,这种关系可用下图简单描述。

模板

下面的这个模板首先使用input子系统上报按键事件,然后在应用层读取。

input按键设备驱动

{            key@26{                       compatible = "xj4412,key";                       interrupt-parent = <&gpx1>;                       interrupts = <2 2>;            }; };
static struct input_dev *button_dev; static int button_irq; static int irqflags; static irqreturn_t button_interrupt(int irq, void *dummy){     input_report_key(button_dev, BTN_0, 0);     input_report_key(button_dev, BTN_0, 1);     input_sync(button_dev);    return IRQ_HANDLED; }  static int button_init(void){     request_irq(button_irq, button_interrupt,irqflags, "button", NULL)) ;          button_dev = input_allocate_device();     button_dev->name = "button";     button_dev->evbit[0] = BIT_MASK(EV_KEY);     button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);          input_register_device(button_dev);    return 0; } static int button_exit(void){     input_free_device(button_dev);     free_irq(button_irq, button_interrupt);    return 0;    } static int key_probe(struct platform_device *pdev){     struct resource *irq_res;     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);     if(irq_res){         button_irq = irq_res->start;         irqflags = irq_res->flags & IRQF_TRIGGER_MASK;     }else{          return -EINVAL;          }    return button_init(); } static int key_remove(struct platform_device *dev){     return button_exit(); } struct of_device_id of_tbl[] = {     {.compatible = "xj4412,key",},     {}, }; MODULE_DEVICE_TABLE(of, of_tbl);struct platform_driver key_drv = {     .probe = key_probe,     .remove = key_remove,     .driver.name = "keydrv",     .driver.of_match_table = of_tbl, }; module_platform_driver_register(key_drv); MODULE_LICENSE("GPL");

应用层获取键值

#include  struct input_event {    struct timeval time;     unsigned short type;     unsigned short code;    int value; }; int main(int argc, char * const argv[]){     int fd = 0;     struct input_event event[3] = {0};      //3!!!,驱动上传了2个事件,第三个用来装空元素      int ret = 0;     fd = open(argv[1],O_RDONLY);     while(1){         ret = read(fd,&event,sizeof(event));         printf("ret:%d,val0:%d,val1:%d,val12:%d\n",ret,event[0].value,event[1].value,event[2].value);          //2!!!,***一个是空         sleep(1);     }     return 0; }

到此,相信大家对"Linux input怎么使用"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

事件 设备 驱动 输入 内核 对象 应用 应用层 子系统 按键 处理 位图 核心 设备驱动 支持 文件 元素 同时 坐标 工具 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全文明阅读 陕西电脑服务器托管虚拟主机 c怎样连接mysql数据库 数据库报警值 桌面式窗口对讲音频管理服务器 网络安全产品售后工程师面试 如何购买亚马逊服务器 有什么好的网络安全软件 软件开发流程生命周期 esp8266手机软件开发 数据决策系统修改内置数据库 查询数据库值 武汉档存网络技术有限公司 河南托管服务器云空间 自定义锁 数据库 互联网云会诊平台医惠科技 皮书数据库怎么免费看 同时查找多种数据库 可以自己搭建服务器的游戏有哪些 上海未来互联网科技有限公司 大树网络安全 完美服务器至少保留两个地区 系统集成软件开发合同模板 网易我的世界服务器飞行脚本下载 软件开发和信息技术服务的区别 数据库安全解决方案 网络技术问题是什么 华为服务器电源电压调整 数据库字段设计说明怎么写 软件开发培训学校黄
0