要怎样夸一个网站做的好看,万能素材网站下载,电商网站建设要多少钱,郑州网站seo厂家目录
一、input子系统简介
二、input驱动API
2.1 input字符设备
2.2 input_dev结构体
2.3 上报输入事件
2.4 input_event结构体
三、代码
3.1 驱动代码
3.2 测试代码
四、平台测试 一、input子系统简介
input子系统是管理输入的子系统#xff0c;和pinctrl、gpio子…目录
一、input子系统简介
二、input驱动API
2.1 input字符设备
2.2 input_dev结构体
2.3 上报输入事件
2.4 input_event结构体
三、代码
3.1 驱动代码
3.2 测试代码
四、平台测试 一、input子系统简介
input子系统是管理输入的子系统和pinctrl、gpio子系统一样都是Linux内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备不同的输入设备所代表的含义不同按键和键盘就是代表按键信息鼠标和触摸屏代表坐标信息因此在应用层的处理就不同。
input子系统分为input驱动层、input核心层、input事件处理层最终给用户空间提供可访问的设备节点 最左边是最底层的具体设备比如按键、USB键盘/鼠标等中间部分是Linux内核空间分为驱动层、核心层和事件层最右边是用户空间所有的输入设备以文件的形式供用户应用程序使用 驱动层输入设备的具体驱动程序比如按键驱动程序向内核层报告输入内容。 核心层为驱动层提供输入设备注册和操作接口通知事件层对输入事件进行处理。 事件层主要和用户空间进行交互。 二、input驱动API
2.1 input字符设备
input核心层会向Linux内核注册一个字符设备——drivers/input/input.c即input输入子系统的核心层
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类
err class_register(input_class);
这样系统启动以后就会在/sys/class目录下有一个input子目录 其中以下代码是注册一个字符设备
err register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, input);
主设备号为INPUT_MAJOR INPUT_MAJOR定义在include/uapi/linux/major.h文件中
#define INPUT_MAJOR 13
因此input子系统的所有设备主设备号都为13。在使用input子系统处理输入设备时不需要注册字符设备只需要向系统注册一个input_device结构体即可。
2.2 input_dev结构体
input_dev结构体表示input设备定义在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)]; /*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表示输入事件类型可选的事件类型定义在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 /* 压力状态事件 */如果要使用按键就要注册EV_KEY事件如果要实现连按还需要注册EV_REP事件。
其中keybit表示按键事件使用的位图。Linux内核定义的keybit定义在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 BTN_TRIGGER_HAPPY39 0x2e6
#define BTN_TRIGGER_HAPPY40 0x2e7编写input设备驱动则需要先申请一个input_dev结构体变量使用input_allocate_device函数来申请一个input_dev
struct input_dev *input_allocate_device(void)
返回值要申请的input_dev。
如果要释放的input设备则需要使用input_free_device函数来释放掉申请到的input_dev
void input_free_device(struct input_dev *dev)
dev要释放的input_dev。
返回值无。
申请好input_dev后要初始化input_dev需要初始化的内容主要为事件类型(evbit)和事件值(keybit)。input_dev初始化好后则需要使用input_register_device函数向Linux内核注册input_dev
int input_register_device(struct input_dev *dev)
dev要注册的input_dev。
返回值0input_dev注册成功负值input_dev注册失败。
如果要注销input设备则需要使用input_unregister_device函数来注销掉注册到的input_dev
void input_unregister_device(struct input_dev *dev)
dev要注销的input_dev 。
返回值无。
综上按键功能的input_dev注册流程如下
struct input_dev *inputdev; /* input结构体变量 */ /* 驱动入口函数 */
static int __init xxx_init(void)
{ ...... /* 初始化input_dev */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_set_capability函数表示设置输入设备可以上报的输入事件——该函数一次只能设置一个具体事件如果设备可以上报多个事件则需要重复调用该函数来进行设置
input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
dev该设备的input_dev结构体变量。
type设备可以上报的事件类型即evbit的值。
code设备可以上报的具体事件本文即keybit的值。
2.3 上报输入事件
input设备都具有输入功能但Linux内核并不知道具体的输入值。在向Linux内核注册好input_dev后还需要获取具体的输入值作为输入事件上报给Linux内核。
不同的事件其上报事件的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_SYN等等。
code事件码如EV_KEY事件中的KEY_0、KEY_1等等。
value事件值。如EV_KEY事件中0表示按键松开1表示按键按下2表示按键连按。
返回值无。
如果是上报按键事件则可以使用input_report_key函数此函数本质即input_event函数
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{ input_event(dev, EV_KEY, code, !!value);
}还有一些其他的事件上报函数如
void input_report_rel(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_ff_status(struct input_dev *dev, unsigned int code, int value)
void input_report_switch(struct input_dev *dev, unsigned int code, int value)
void input_mt_sync(struct input_dev *dev)上报事件后还要使用input_sync函数告知Linux内核input子系统上报结束该函数本质即上报一个同步事件
void input_sync(struct input_dev *dev)
dev需要上报同步事件的input_dev。
返回值无。
综上按键的上报事件代码流程如下
unsigned char value; value gpio_get_value(keydesc-gpio); /* 读取IO值 */
if(value 0){ /* 按下按键 */ /* 上报按键值 */ input_report_key(inputdev, KEY_0, 1); /* 最后一个参数1按下 */ input_sync(inputdev); /* 同步事件 */
} else { /* 按键松开 */ input_report_key(inputdev, KEY_0, 0); /* 最后一个参数0松开 */ input_sync(inputdev); /* 同步事件 */
} 2.4 input_event结构体
Linux内核使用input_event结构体区别于上文的input_event函数表示所有的输入事件定义在include/uapi/linux/input.h文件中
struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value;
};time此事件发生时的时间为timeval结构体类型
typedef long __kernel_long_t;
typedef __kernel_long_t __kernel_time_t;
typedef __kernel_long_t __kernel_suseconds_t; struct timeval { __kernel_time_t tv_sec; /* 秒 */ __kernel_suseconds_t tv_usec; /* 微秒 */
};type: 上报的事件类型如EV_KEY、EV_SYN等等。
code事件码。如EV_KEY事件中的KEY_0、KEY_1等等。
value事件值。如EV_KEY事件中0表示按键松开1表示按键按下2表示按键连按。
所有的输入设备最终都按照input_event结构体呈现给用户用户应用程序可以通过input_event结构体来获取到具体的输入事件或相关的值比如按键值等。 三、代码
配合Linux驱动开发——六按键中断实验讲解。
3.1 驱动代码
在设备结构体里添加input结构体
struct keyinput_dev{......struct input_dev *inputdev; /* input结构体 */
}
在定时器服务函数里更改按键按下和释放代码为
if(value 0){ /* 按下按键 */ /* 上报按键值 */ input_report_key(inputdev, KEY_0, 1); /* 最后一个参数1按下 */ input_sync(inputdev); /* 同步事件 */
} else { /* 按键松开 */ input_report_key(inputdev, KEY_0, 0); /* 最后一个参数0松开 */ input_sync(inputdev); /* 同步事件 */
} 在按键初始化函数里添加 /* 初始化input_dev */
inputdev input_allocate_device(); /* 申请input_dev */
inputdev-name test_inputdev; /* 设置input_dev名字 */ /* 设置事件和事件值 */
keyinputdev.inputdev-evbit[0] BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 注册输入设备 */
ret input_register_device(keyinputdev.inputdev);
if (ret) { printk(register input device failed!\r\n); return ret;
}return 0;
在驱动出口函数里添加
/* 释放input_dev */
input_unregister_device(keyinputdev.inputdev);
input_free_device(keyinputdev.inputdev);3.2 测试代码
定义一个input_event变量存放输入事件信息
static struct input_event inputevent;
更改主函数中while函数的代码为
while (1) { err read(fd, inputevent, sizeof(inputevent)); if (err 0) { /* 读取数据成功 */ switch (inputevent.type) { case EV_KEY: if (inputevent.code BTN_MISC) { /* 键盘键值 */ printf(key %d %s\r\n, inputevent.code,inputevent.value ? press : release); } else { printf(button %d %s\r\n, inputevent.code, inputevent.value ? press : release); } break; /* 其他类型的事件自行处理 */ case EV_REL: break; case EV_ABS: break; case EV_MSC: break; case EV_SW: break; } } else { printf(读取数据失败\r\n); }
} 向Linux内核成功注册input_dev设备后会在/dev/input目录下生成一个名为“ eventX(X0….n)”的文件对应的input设备文件。使用read函数读取该输入设备文件读取到的数据如按键值等等按照input_event结构体组织起来。获取到输入事件以后input_event结构体类型再使用 switch case语句来判断事件类型。 四、平台测试
在加载该驱动模块之前/dev/input目录下只有以下两个文件 加载该驱动模块后/dev/input目录下有以下三个文件 因此/dev/input/event1即注册的驱动所对应的设备文件。使用测试代码读取/dev/input/event1该文件然后按下按键查看获取的输入事件信息 也可以使用hexdump命令来直接查看/dev/input/event1input_event结构体类型的原始事件数据值 原始事件数据值的含义如下
编号tv_sectv_usectypecodevalue000000052f7 0000be6b 00010001000b0001 0000000001052f7 0000be6b 0001000000000000 0000000002052f7 0000451d 00030001000b0000 0000000003052f7 0000451d 0003000000000000 0000