input子系统是管理系统输入的子系统,比如鼠标、键盘、触摸屏等等。不同设备所代表的输入信息不同,对于驱动开发人员而言,不需要关心应用层的事情,通过input子系统按照既定规则上报输入事件即可。为此 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点。
系统定义主设备号头文件,input子系统主设备号13.
include/upai/linux/major.h
input核心层通过向linux系统注册字符设备。如下代码所示,注册一个input类,系统启动后,会在/sys/class/中生成一个input目录。
driver/input/input.c
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
static int __init input_init(void)
{
int err;
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();
if (err)
goto fail1;
err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
input_dev 表示input设备,使用input设备通常只需要注册一个input_dev即可。
include/linux/input.h
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)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
.....
};
输入事件类型定义,根据需求事件功能注册相应事件。
include/uapi/linux/input.h
#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 /* 压力状态事件 */
事件值
include/uapi/linux/input.h
#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 KEY_MINUS 12
#define KEY_EQUAL 13
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_Q 16
#define KEY_W 17
#define KEY_E 18
#define KEY_R 19
#define KEY_T 20
#define KEY_Y 21
#define KEY_U 22
#define KEY_I 23
#define KEY_O 24
#define KEY_P 25
.....
.....
在编写input设备驱动,一般先申请一个input_dev结构体变量,通过如下函数申请与释放:
struct input_dev *input_allocate_device(void)
void input_free_device(struct input_dev *dev)
申请到input_dev,需要初始化设备内容,主要内容包括事件类型evbit和事件值keybit,通过如下函数注册与注销:
int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
input设备注册流程:
- 使用 input_allocate_device 函数申请一个 input_dev。
- 初始化 input_dev 的事件类型以及事件值。
- 使用 input_unregister_device 函数向 Linux 系统注册前面初始化好的 input_dev。
- 卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev,然后使用input_free_device 函数释放掉前面申请的 input_dev。
程序流程:
struct input_dev *inputdev; /* input 结构体变量 */
/* 驱动入口函数 */
static int __init xxx_init(void)
{
......
inputdev = input_allocate_device(); /* 申请 input_dev */
inputdev->name = "test_inputdev"; /* 设置 input_dev 名字 */
/*********第一种设置事件和事件值的方法***********/
__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);
......
return 0;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
input_unregister_device(inputdev); /* 注销 input_dev */
input_free_device(inputdev); /* 删除 input_dev */
}
设备注册完毕后,还无法使用input设备。需要获取具体的输入值或输入事件,上报到linux内核。比如按键抖动,中断将按键值上报到linux内核,linux才能获取到正确的输入值。不同事件类型,上报函数不一样。
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event(事件类型)
* @code: event code(事件值)
* @value: value of the event
**/
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
input_event原理上可以上报任何函数,danlinux相继封装了针对不同事件类型的上报函数,其本质上还是input_event.
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}
上报结束后需要告诉linux上报结束,使用如下函数
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
用户层如何处理上报信息?
linux定义了一个上报事件结构体,用户层只需要处理这个结构体。
include/uapi/linux/input.h
struct input_event {
struct timeval time;
__u16 type; // 事件类型
__u16 code; // 事件值
__s32 value;
};
input系统按键案例(来源于正点原子):
input 驱动:
https://github.com/Jiongyu/linux_driver_example/blob/master/20_input/keyinput.c
input 应用层测试:
https://github.com/Jiongyu/linux_driver_example/blob/master/20_input/keyinputApp.c