【IMX6ULL驱动学习】INPUT子系统
13. INPUT子系统
13.1. INPUT子系统框架
- INPUT子系统和
pinctrl
,gpio
子系统类似,都是 Linux 内核针对某一类设备而创建的框架。INPUT子系统针对的是输入设备,比如键盘、鼠标、触摸屏等。
INPUT子系统主要包含以下几个部分
- 内核空间-驱动层:探测输入设备,初始化硬件,轮询输入事件,并将采集到的数据初始化为INPUT子系统的标准事件,调用
input_event(), input_report_key()
等函数将事件上报给INPUT子系统 - 内核空间-核心层:提供API,定义标准的输入事件
- 内核空间-事件层:创建INPUT设备节点,实现文件操作接口
read
,write
,ioctl
等函数,供用户空间程序访问,将从核心层得到的事件转换为用户空间的标准数据格式 - 用户空间:调用
open
,read
,ioctl
等函数访问INPUT设备节点,获取输入事件
13.2. INPUT子系统驱动编写
在核心层,INPUT子系统已经注册了一个字符设备,并得到了一个主设备号,在驱动文件中,就不需要专门给INPUT子系统创建字符设备和设备节点了,只需要构建input_dev
结构体变量,设置好设备的属性,然后通过input_register_device
函数将设备注册到INPUT子系统即可
13.2.1. 构建input_dev结构体变量
- 构建数据结构,存放驱动运行所需的所有数据
其中struct input_dev {const char *name;const char *phys;const char *uniq;struct input_id id;unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];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)]; /* LED 相关的位图 */unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; /* sound 有关的位图 */unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /* 开关状态的位图 */bool devres_managed; };
evbit
表示设备支持的事件类型,keybit
表示设备支持的按键值,relbit
表示设备支持的相对坐标,absbit
表示设备支持的绝对坐标等。evbit
可选的事件类型如下
如果需要使用某个事件,就需要向INPUT子系统注册这个事件,比如要使用到按键,按键的按键值可选值如下**(所有事件通用)**#define EV_SYN 0x00 /* 同步事件 */ #define EV_KEY 0x01 /* 按键事件 */ #define EV_REL 0x02 /* 相对坐标事件 */ #define EV_ABS 0x03 /* 绝对坐标事件 */ #define EV_MSC 0x04 /* 杂项(其他)事件 */ #define EV_SW 0x05 /* 开关事件 */ #define EV_LED 0x11 /* LED */ #define EV_SND 0x12 /* sound(声音) */ #define EV REP 0x14 /* 重复事件 */ #define EV_FF 0x15 /* 压力事件 */ #define EV_PWR 0x16 /* 电源事件 */ #define EV_FF_STATUS 0x17 /* 压力状态事件 */
按键事件需要注册#define KEY_RESERVED 0 #define KEY_ESC 1 #define KEY_1 2 #define KEY_2 3 #define KEY_3 4 #define KEY_4 5 #define KEY_5 6 #define KEY_6 7 #define KEY_7 8 #define KEY_8 9 #define KEY_9 10 #define KEY_0 11... #define BTN_TRIGGER_HAPPY39 0x2e6 #define BTN_TRIGGER_HAPPY40 0x2e7
EV_KEY
事件,如果要使用连按功能的话还需要注册EV_REP
事件,以下有三种注册方式
完整的驱动文件中,构建/************* 第一种设置事件和事件值的方法 *************/ __set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */ __set_bit(EV_REP, inputdev->evbit); /* 重复事件 */ __set_bit(KEY_0, inputdev->keybit); /* 设置产生哪些按键值 *//************* 第二种设置事件和事件值的方法 *************/ keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);/************* 第三种设置事件和事件值的方法 *************/ keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);/* 注册 input_dev */ input_register_device(inputdev);
input_dev
结构体变量的代码如下struct input_dev *inputdev; /* 定义一个input_dev结构体指针 */ static __init xxx_init(Void){int ret;.../* 1. 分配input_dev结构体 */inputdev = input_allocate_device();if(!inputdev){printk("input_allocate_device err\n");return -ENOMEM;}/* 2. 设置设备属性 */inputdev->name = "my_input"; /* 设备名称 *//* 3. 设置事件类型和事件值 */inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); /* 设置产生按键事件和重复事件 */inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0); /* 设置产生哪些按键值 *//* 4. 注册 input_dev */ret = input_register_device(inputdev);if(ret){printk("input_register_device err\n");input_free_device(inputdev);} return 0; } static __exit xxx_exit(void){/* 注销 input_dev */input_unregister_device(inputdev);/* 释放 input_dev 结构体 */input_free_device(inputdev); }
13.2.2. 上报事件
注册完input_dev
结构体变量后,只是构建了一个可能发生事件的框架,当事件真正发生,需要调用API进行上报
-
input_event
函数void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
dev
:input_dev结构体变量type
:事件类型,比如EV_KEY,EV_REL等code
:事件值,比如KEY_0,REL_X等value
:事件值的状态,比如按下为1,松开为0,保持为2
input_event 函数可以上报所有的事件类型和事件值,比如检测到了按下按键KEY_0,可以调用以下代码上报事件
input_event(inputdev, EV_KEY, KEY_0, 1); /* 按下 */ input_event(inputdev, EV_SYN, SYN_REPORT, 0); /* 同步 */
还有其他上报函数针对特定事件,但都是调用
input_event
函数实现的 -
input_sync
函数void input_sync(struct input_dev *dev);
dev
:input_dev结构体变量
input_sync
函数用于同步事件,因为一次上报的事件不一定只有一件事,可能有多件事,比如按下按键KEY_0,同时移动鼠标,这时需要调用input_event
函数上报多次事件,最后调用input_sync
函数进行同步,告知内核,此次事件上报结束。