文章目录
- 概述
- input 子系统框架
- input 子系统相关结构体介绍
- input_dev结构体
- input_handler结构体
- input_handle结构体
- Evdev事件相关结构体
- input_event结构体(标准按键编码信息)
- 设备相关信息结构体
概述
- input子系统就是管理输入的子系统,和Linux其他子系统一样,都是Linux内核针对某一类设备而创建的框架。
- 鼠标、键盘、触摸屏等都属于输入设备,Linux将这些设备的共同特性抽象出来,这就形成了input子系统的框架。
- Linux内核只需要通过input框架向用户层上报输入事件(如:按键值,坐标等),不需要关心应用层的事情
- 输入设备本质上就是字符设备,经过input框架后,最终给用户空间提供可以访问的设备节点
input 子系统框架
Linux Input 子系统框架
硬件输入设备: 最底层具体设备(如:触摸屏、键盘、鼠标等)
内核空间:
- 驱动层: 输入设备的具体驱动程序。负责将底层的硬件输入转化为统一的事件形式,向input核心层传达。
- 核心层: 连接驱动层和事件层之间。负责双向提供接口,向下提供驱动层接口,向上提供事件处理的接口。
- 事件层: 底层的设备抽象出对应的接口提供给应用层。将底层设备的触发的事件通过这个接口传达给应用层。
input 子系统相关结构体介绍
input_dev结构体
input_dev结构体是硬件驱动层,代表一个input设备。
最底层具体设备,都会抽象出一个input_dev结构体。
// include/linux/input.h
struct input_dev {
const char *name; /* 设备名称 */
const char *phys; /* 设备在系统中的路径 */
const char *uniq; /* 设备唯一id */
struct input_id id; /* input设备id号 */
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 设备支持的事件类型,主要有EV_SYNC,EV_KEY,EV_KEY,EV_REL,EV_ABS等*/
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)]; /* 支持声音事件 */
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 支持受力事件 */
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /* 支持开关事件 */
unsigned int hint_events_per_packet; /* 平均事件数*/
unsigned int keycodemax; /* 支持最大按键数 */
unsigned int keycodesize; /* 每个键值字节数 */
void *keycode; /* 存储按键值的数组的首地址 */
int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode);
int (*getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);
struct ff_device *ff; /* 设备关联的反馈结构,如果设备支持 */
unsigned int repeat_key; /* 最近一次按键值,用于连击 */
struct timer_list timer; /* 自动连击计时器 */
int rep[REP_CNT]; /* 自动连击参数 */
struct input_mt *mt; /* 多点触控区域 */
struct input_absinfo *absinfo; /* 存放绝对值坐标的相关参数数组 */
unsigned long key[BITS_TO_LONGS(KEY_CNT)]; /* 反应设备当前的按键状态 */
unsigned long led[BITS_TO_LONGS(LED_CNT)]; /* 反应设备当前的led状态 */
unsigned long snd[BITS_TO_LONGS(SND_CNT)]; /* 反应设备当前的声音状态 */
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; /* 反应设备当前的开关状态 */
int (*open)(struct input_dev *dev); /* 第一次打开设备时调用,初始化设备用 */
void (*close)(struct input_dev *dev); /* 最后一个应用程序释放设备事件,关闭设备 */
int (*flush)(struct input_dev *dev, struct file *file); /* 用于处理传递设备的事件 */
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); /* 事件处理函数,主要是接收用户下发的命令,如点亮led */
struct input_handle __rcu *grab; /* 当前占有设备的input_handle */
spinlock_t event_lock; /* 事件锁 */
struct mutex mutex; /* 互斥体 */
unsigned int users; /* 打开该设备的用户数量(input_handle) */
bool going_away; /* 标记正在销毁的设备 */
struct device dev; /* 一般设备 */
struct list_head h_list; /* 设备所支持的input handle */
struct list_head node; /* 用于将此input_dev连接到input_dev_list */
unsigned int num_vals; /* 当前帧中排队的值数 */
unsigned int max_vals; /* 队列最大的帧数*/
struct input_value *vals; /* 当前帧中排队的数组*/
bool devres_managed; /* 表示设备被devres 框架管理,不需要明确取消和释放*/
};
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 | 事件类型最大个数和提供位掩码支持 |
Linux下定义按键值(列出部分):
#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
input_handler结构体
input_handler结构体是事件层,代表一个事件处理器。
// include/linux/input.h
struct input_handler {
void *private; /* 存放handle数据 */
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
void (*events)(struct input_handle *handle,
const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
bool legacy_minors;
int minor;
const char *name; /* 名字 */
const struct input_device_id *id_table; /* input_dev匹配用的id */
struct list_head h_list; /* 用于链接和handler相关的handle,input_dev与input_handler配对之后就会生成一个input_handle结构 */
struct list_head node; /* 用于将该handler链入input_handler_list,链接所有注册到内核的所有注册到内核的事件处理器 */
};
input_handle结构体
input_handle结构体属于核心层,代表一个配对的input设备与input事件处理器。
// include/linux/input.h
struct input_handle {
void *private; /* 数据指针 */
int open; /* 打开标志,每个input_handle 打开后才能操作 */
const char *name; /* 设备名称 */
struct input_dev *dev; /* 指向所属的input_dev */
struct input_handler *handler; /* 指向所属的input_handler */
struct list_head d_node; /* 用于链入所指向的input_dev的handle链表 */
struct list_head h_node; /* 用于链入所指向的input_handler的handle链表 */
};
以上三个结构体在input子系统扮演重要角色,从结构体成员能看出有两个链表。
input_dev和input_handler链表。三张表连接图如下。
input-dev 、 input-handler、input-handle三结构体关系
input_dev和input_handler各自内部链表图
-
input_handler 通过全局的input_handler_list链接在一起。
-
input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。
-
通过input_dev 和input_handler就可以找到input_handle在设备注册和事件处理器,注册的时候都要进行配对工作,配对后就会实现链接。
-
通过input_handle也可以找到input_dev和input_handler。input_handle是用来关联input_dev和input_handler的。
Evdev事件相关结构体
1、Evdev(字符设备事件)结构体
/* drivers/input/evdev.c */
struct evdev {
int open; /* 设备被打开的计数 */
struct input_handle handle; /* 关联的input_handle */
wait_queue_head_t wait; /* 等待队列,当前进程读取设备,没有事件产生时,
进程就会sleep */
struct evdev_client __rcu *grab; /* event响应 */
struct list_head client_list; /* evdev_client链表,说明evdev设备可以处理多个 evdev _client,可以有多个进程访问evdev设备 */
spinlock_t client_lock;
struct mutex mutex;
struct device dev;
struct cdev cdev;
bool exist; /* 设备存在判断 */
};
2、evdev_client(字符设备事件响应)结构体
struct evdev_client {
unsigned int head; /* 动态索引,每加入一个event到buffer中,head++ */
unsigned int tail; /* 动态索引,每取出一个buffer中到event,tail++ */
unsigned int packet_head; /* 数据包头部 */
spinlock_t buffer_lock;
struct fasync_struct *fasync; /* 异步通知函数 */
struct evdev *evdev;
struct list_head node; /* evdev_client链表项 */
int clkid;
bool revoked;
unsigned int bufsize;
struct input_event buffer[]; /* 用来存放input_dev事件缓冲区 */
};
3、evdev_handler(事件处理函数)结构体
/* drivers/input/input.c */
static struct input_handler evdev_handler = {
.event = evdev_event, /* 事件处理函数, */
.events = evdev_events, /* 事件处理函数, */
.connect = evdev_connect, /* 连接函数,将事件处理和输入设备联系起来 */
.disconnect = evdev_disconnect, /* 断开该链接 */
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev", /* handler名称 */
.id_table = evdev_ids, /* 断开该链接 */
};
input_event结构体(标准按键编码信息)
/* drivers/input/evdev.c */
struct input_event {
struct timeval time; /* 事件发生的时间 */
__u16 type; /* 事件类型 */
__u16 code; /* 事件码 */
__s32 value; /* 事件值 */
};
设备相关信息结构体
/* include/uapi/linux/input.h */
struct input_id {
__u16 bustype; /* 总线类型 */
__u16 vendor; /* 生产厂商 */
__u16 product; /* 产品类型 */
__u16 version; /* 版本 */
};
/* include/uapi/linux/input.h */
struct input_device_id {
kernel_ulong_t flags;
__u16 bustype; /* 总线类型 */
__u16 vendor; /* 生产厂商 */
__u16 product; /* 产品类型 */
__u16 version; /* 版本 */
kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1];
kernel_ulong_t driver_info;
};