linux驱动-input子系统

input系统,内核里面比较简单的子系统,也比较常用,

一、框架

    input 子系统 linux 为输入设备(键盘,鼠标,触摸屏等)提供了统一的接口。它的框架有三层组成,设备驱动层(drivers),核心层,事件处理层,如下图所示。其实看了源码就会发现,所谓的 input 子系统本质也就是字符设备而已,不过内核将常用的设备接口封装在里面了,直接使用就行了。

input 子系统的系统框架

二、输入核心层

输入核心层在整个框架中处于承上启下的作用,它提供的功能

  1. 在/sys/class/ 下创建一个类 名字叫做 input,在/proc下面建立相关的文件,注册一个字符设备 input
  2. 向设备驱动层提供注册函数 input_register_device
  3. 向事件处理层提供注册函数 input_register_handler
  4. 提供匹配函数 input_attach_handler 用于建立 input_dev 和 input_handler 之间的连接,并对其进行管理。
  5. 提供上报中转接口 input_event

1、相关数据结构

1) input_handler

每一个 input_handler 结构都是一类事件处理接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct input_handler {

void *private;

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);

// 匹配时调用这个函数,如果返回 0 则匹配失败
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; //handler 的名字

/* 能够支持哪一些输入设备 */
const struct input_device_id *id_table;

struct list_head h_list; //用来存放input_handle->h_node
struct list_head node; //链接进入 input_handler_list
};

    在老式的 2.6 内核, input_handler 在内核中由 input_table 管理,它是在 input.c 中的全局数组,表示支持的输入设备类型最多有8类,其中包含 mousedev,joydev,evdev 等。 input 子系统的主设备号固定为 INPUT_MAJOR 这个宏被初始化为 13 ,次设备号为 0~255,input_table 数组有8个 input_handler,这 8 个input_handler 将 256 个设备号划分为8份,即每一个 input_handler 最多支持 32个字符设备节点。evdev 的次设备号起始为 EVDEV_MINOR_BASE ,这个宏被初始化为 64,因此 evdev 对应的 table 数组项为 input_table[2]。
    而新的内核则使用了更加方便的方式,直接将 input_handler 的管理由核心层移交给事件处理层来管理,使用 input_handle 来动态管理。每个连接成功的 input_handler 和 input_dev 都会动态创建一个 input_handle 来管理,当卸载时释放掉这个结构就可以了。

2) input_device_id

能够支持哪一些输入设备,用于匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct input_device_id {


#define INPUT_DEVICE_ID_MATCH_BUS 1
#define INPUT_DEVICE_ID_MATCH_VENDOR 2
#define INPUT_DEVICE_ID_MATCH_PRODUCT 4
#define INPUT_DEVICE_ID_MATCH_VERSION 8
// 用于设置需要匹配的内容,虽然内核提供了许多选择
// 但是 input_match_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; //设置为 1 则表示默认进行匹配。
};

3) input_dev

每一个 input_dev 都是一个具体的硬件设备,里面包含该硬件设备的信息和处理函数,该结构也是驱动要处理的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
struct input_dev {
const char *name; // 设备名
const char *phys; // 设备节点名称
const char *uniq; // 唯一的ID号,
struct input_id id; // 输入设备ID,用于 handler 匹配

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)]; //按键 ex:上下左右 home
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //相对坐标 ex 鼠标
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //绝对坐标 ex 触摸屏
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 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)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];

int (*open)(struct input_dev *dev); //open 回调接口
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);

struct input_handle __rcu *grab;

spinlock_t event_lock;
struct mutex mutex;

unsigned int users; //设备使用计数
bool going_away;

struct device dev; //设备结构

struct list_head h_list; // handle list
struct list_head node; // input dev list

unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;

bool devres_managed;
};

4) input_handle

用于连接上面的 input_dev 和 input_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
struct input_handle {

void *private;

int open;
const char *name;

struct input_dev *dev;
struct input_handler *handler;

struct list_head d_node; // 链接到 handle->dev->h_list
struct list_head h_node; // 链接到 handler->h_list
};

5) input_event

上报的事件由 input_event 描述

1
2
3
4
5
6
struct input_event {
struct timeval time;
__u16 type; //事件类型
__u16 code; //事件
__s32 value; //事件值
};

2、三角关系

对于每一个和 input_handler 匹配成功的 input_dev 内核都会在connect函数中创建一个 input_handle 结构,用于建立以及维护input_handler和input_dev之间的联系。input_handle作为 input_dev 和 input_handler 沟通的桥梁。建立联系的过程如下:

  1. 调用 input_register_handler 注册 input_dev 结构
  2. 在 input_register_handler 中遍历 input_handler_list 中所有的 input_handler,对每一个 input_hander 调用 input_attach_handler 进行匹配 (提示:这说明input_dev可以和多个input_hander 匹配成功)
  3. 匹配成功则调用 input_handler.connect函数
  4. connect 函数中将 input_dev 和 input_handler 赋值给 input_handle.dev 和 input_handle.handler
  5. 调用 input_register_handle 将 handle->d_node 链入 handle->dev->h_list 同时将 handle->h_node 链入 handler->h_list

于是 input_dev 可以利用 input_dev.h_list 中保存的d_node 找到 input_handle 结构从而找到对应的 input_handle.handler 中保存的 input_handler 结构,同理 input_hander 也可以借助 input_handle 找到对应 的input_dev。上述只是简单的描述了三者的关系,而input_dev和input_handler,并不是简单的一对一的关系,而是多对多的关系。

3、核心层相关接口

1) input_register_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int error;

error = mutex_lock_interruptible(&input_mutex);
if (error)
return error;

INIT_LIST_HEAD(&handler->h_list);

//将 handler 链接到 input_handler_list
list_add_tail(&handler->node, &input_handler_list);

//对遍历 input_dev_list 上的所有 dev 对每一个 dev 都调用 input_attach_handler 函数
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_handler);

2) input_attach_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
// 调用匹配接口
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;

//匹配成功则调用 handler->connect 函数建立连接
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);

return error;
}

3) input_match_device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;

// 判断是否有 flag 或者 driver_info, 如果有设置则进行匹配
// 匹配成功如果有 handler->match 函数时,只有该函数返回为真时表示匹配成功
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;
}
}

return NULL;
}

4) input_match_device_id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
bool input_match_device_id(const struct input_dev *dev,
const struct input_device_id *id)
{
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) // 匹配总线
if (id->bustype != dev->id.bustype)
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor) // 匹配供应商
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product) // 匹配产品
return false;

if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version) // 匹配版本
return false;

// bitmap_subset 这个函数的算法也是挺有意思的,感兴趣可以看看源码,下面直接给出结论
// static inline int bitmap_subset(const unsigned long *src1, const unsigned long *src2, unsigned int nbits)
// 当 src1 的第 n 位 (n < nbits) 为 0 时,src2 的第 n 位为任何数结果为真
// 当 src1 的第 n 位 (n < nbits) 为 1 时,src2 的第 n 位为 1 是结果为真,否则结果为假
// 只有当两者的 nbits 描述的位都位真时返回真
// 举例说明1: bitmap_subset(5,3,3);
// 5 二进制表示: 1 0 1
// 3 二进制表示: 0 1 1
// 计算结果 0 & 1 & 1 ==> 0
//
// 举例说明2: bitmap_subset(5,7,2); //只计算两位忽略最高位
// 5 二进制表示: 1 0 1
// 3 二进制表示: 0 1 1
// 计算结果 1 & 1 ==> 1
//
// 引申到这个函数的功能就是,当 input_handler 的事件类型某一位设置位 0 则表示兼容。
// 如果设置位 1 则表示,当设备不支持时则无法匹配成功。

if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || // 匹配支持的事件类型
!bitmap_subset(id->keybit, dev->keybit, KEY_MAX) || // 匹配支持的按键事件
!bitmap_subset(id->relbit, dev->relbit, REL_MAX) || // 匹配支持的相对坐标事件
!bitmap_subset(id->absbit, dev->absbit, ABS_MAX) || // 匹配支持的绝对坐标事件
!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) || // 匹配支持的其他功能
!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) || // 匹配支持的指示灯
!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
!bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
!bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
return false;
}

return true;
}
EXPORT_SYMBOL(input_match_device_id);

4) input_register_device

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
int input_register_device(struct input_dev *dev)
{
struct input_devres *devres = NULL;
struct input_handler *handler;
unsigned int packet_size;
const char *path;
int error;

// 测试是否设置了 EV_ABS ,如果设置的同时 dev->absinfo 为空则返回
if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
dev_err(&dev->dev,
"Absolute device without dev->absinfo, refusing to register\n");
return -EINVAL;
}

// 是否使用设备资源管理,
// 驱动注册失败时自动调用 devm_input_device_unregister 函数释放资源
if (dev->devres_managed) {
devres = devres_alloc(devm_input_device_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;

devres->input = dev;
}

// 每一个设备都支持 EV_SYN/SYN_REPORT 事件
__set_bit(EV_SYN, dev->evbit);

// KEY_RESERVED 事件不应该被传输到应用空间
__clear_bit(KEY_RESERVED, dev->keybit);

// 清空其他事件,也就是说在 input_dev 注册前设置的其他事件是没有意义的会被清空
input_cleanse_bitmasks(dev);

// 获取每个包多少个事件
packet_size = input_estimate_events_per_packet(dev);
if (dev->hint_events_per_packet < packet_size)
dev->hint_events_per_packet = packet_size;

// 计算出每个包最大的事件数
dev->max_vals = dev->hint_events_per_packet + 2;
// 动态分出需要的内存空间
dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
if (!dev->vals) {
error = -ENOMEM;
goto err_devres_free;
}

// 重复按键相关
// REP_PERIOD 秒之后每个 REP_DELAY 秒检测一次按键是否被按下。
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
input_enable_softrepeat(dev, 250, 33);

if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

// 注册设备创建出设备节点 /sys/devices/xxx
error = device_add(&dev->dev);
if (error)
goto err_free_vals;

//打印设备所在路径
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);

error = mutex_lock_interruptible(&input_mutex);
if (error)
goto err_device_del;

// 将 input_dev 链接到 input_dev_list
list_add_tail(&dev->node, &input_dev_list);

//对于 input_handler_list 上的每一个 handler 调用 input_attach_handler 进行匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

if (dev->devres_managed) { // 如果设置了设备资源管理则注册对应
dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&dev->dev));
devres_add(dev->dev.parent, devres);
}
return 0;

err_device_del:
device_del(&dev->dev);
err_free_vals:
kfree(dev->vals);
dev->vals = NULL;
err_devres_free:
devres_free(devres);
return error;
}
EXPORT_SYMBOL(input_register_device);

5) input_register_handle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;

error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;

if (handler->filter) //将 d_node 节点连接到 dev->h_list
list_add_rcu(&handle->d_node, &dev->h_list);
else
list_add_tail_rcu(&handle->d_node, &dev->h_list);

mutex_unlock(&dev->mutex);

// 将 handle->h_node 链接到 handler->h_list
list_add_tail_rcu(&handle->h_node, &handler->h_list);

if (handler->start)
handler->start(handle);

return 0;
}
EXPORT_SYMBOL(input_register_handle);

三、evdev 分析

常用的 input_handler 内核都已经为我们做好了,这里来分析最最常用的 evdev。

1、数据结构

1) evdev

1
2
3
4
5
6
7
8
9
10
11
12
struct evdev {
int open; / 对被打开的设备进行计数
struct input_handle handle; // handle 被嵌入到 evdev,用于建立 input_dev 和 input_handler 之间的链接
wait_queue_head_t wait; //等待队列头
struct evdev_client __rcu *grab;
struct list_head client_list; //连接 client
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; // 设备
struct cdev cdev; // 字符设备结构
bool exist;
};

2)evdev_client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev; // 指向所属的 evdev
struct list_head node;
unsigned int clk_type;
bool revoked;
unsigned long *evmasks[EV_CNT];
unsigned int bufsize; // 数据大小
struct input_event buffer[]; // 需要上报的数据存储在这里,这是一个环形数组缓存
};

2、evdev 的注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* 对所有的设备进行匹配 */
{ }, /* 其他项为 0 则支持所有的事件 */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
.event = evdev_event, // 事件处理回调接口
.events = evdev_events,
.connect = evdev_connect, // 当有 input_dev 匹配成功时调用,建立连接
.disconnect = evdev_disconnect, // 断开连接
.legacy_minors = true,
.minor = EVDEV_MINOR_BASE, // 次设备号的基地址 64
.name = "evdev", // handler 名字
.id_table = evdev_ids, // id_table 用于 input_dev 的匹配
};

static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

3、与设备建立连接

当有设备匹配成功时,直接调用 evdev_connect 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int dev_no;
int error;

// 获取次设备号
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
if (minor < 0) {
error = minor;
pr_err("failed to reserve new minor: %d\n", error);
return error;
}

// 创建一个 evdev
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev) {
error = -ENOMEM;
goto err_free_minor;
}

// 初始化链表
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);

//初始化等待队列头
init_waitqueue_head(&evdev->wait);
evdev->exist = true;

dev_no = minor;
/* Normalize device number if it falls into legacy range */
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
dev_no -= EVDEV_MINOR_BASE;

// 设置设备名字为 event + 次设备号
dev_set_name(&evdev->dev, "event%d", dev_no);

// 设置 handle 结构的 dev 为传入的 input_dev, 同时增加相关设备的引用计数
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev); // 设置 name 为 evdev
evdev->handle.handler = handler; // 设置 handler
evdev->handle.private = evdev; // 设置私有数据

evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); // 设置设备号
evdev->dev.class = &input_class; // 设置所属类 /sys/class/input
evdev->dev.parent = &dev->dev; // 设置父节点为 input_dev->dev
evdev->dev.release = evdev_free; // 设置 release 回调函数
device_initialize(&evdev->dev); // 初始化设备结构

// 注册 handle 结构,就是将对应节点链接到对应的 input_dev 和 input_handler
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;

// 初始化字符设备,evdev 的本质就是字符设备
cdev_init(&evdev->cdev, &evdev_fops);

// 注册字符设备,同时也会在 /dev 下创建出对应的设备 dev/intput0
// 创建 /sys/class/input/input0
// 创建 /sys/devices/注册的设备名/input0
error = cdev_device_add(&evdev->cdev, &evdev->dev);
if (error)
goto err_cleanup_evdev;

return 0;

err_cleanup_evdev:
evdev_cleanup(evdev);
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
err_free_minor:
input_free_minor(minor);
return error;
}

在建立连接时将创建字符设备,提供的接口如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.llseek = no_llseek,
};

4、打开设备

当上层打开对应的字符设备接口时回调流程如下

1
2
3
4
static int evdev_open
--> static int evdev_open_device
--> int input_open_device
--> dev->open(dev); //如果存在则调用,不存在直接返回

当我们打开一个 evdev 节点的时候最终会调用对应的设备的 open 回调函数

5、读取数据

当 以 O_NONBLOCK 读取数据时,读取不到数据进入休眠。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
size_t read = 0;
int error;

// 判读读取的数据是否过长
if (count != 0 && count < input_event_size())
return -EINVAL;

for (;;) {
if (!evdev->exist || client->revoked)
return -ENODEV;

if (client->packet_head == client->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;

// 读取的长度为 0 直接返回
if (count == 0)
break;

while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {

if (input_event_to_user(buffer + read, &event))
return -EFAULT;

read += input_event_size();
} // 循环读取数据

if (read) // 读取到则返回
break;

// 如果未读取到数据同时状态是 O_NONBLOCK 则进入休眠。
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}

return read;
}

四、事件上报

所谓事件上报,不过是将要上报的事件准备好,然后唤醒前面 evdev_read 休眠的进程,将数据返回给用户空间。调用流程如下感兴趣可以跟一下源码。

1
2
3
4
5
6
7
8
9
void input_event
--> static void input_handle_event
--> static void input_pass_values
--> static unsigned int input_to_handler
--> handler->event
--> static void evdev_event
--> static void evdev_events
--> static void evdev_pass_values
--> wake_up_interruptible(&evdev->wait);

内核已经将上报的接口封装好了,如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 向输入子系统报告产生的按键事件类型的事件code,以及事件的值value
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_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value);
}

// 通知子系统处理上报的事件
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}

// 多点触摸事件
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}

// 设置 type 事件的事件值
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);

五、应用程序处理 input 事件

参考问章 https://www.136.la/tech/show-60258.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
int main(int argc, char **argv)
{
int fd;
struct input_event ev;
char name[80];

// 打开设备
fd = open("/dev/input/event5", O_RDWR);
if(fd < 0) {
printf("open err\n");
return 0;
}

ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name); //获取设备名
printf("find device name = %s\n", name);

// 循环读取
while(1) {
// 读取数据
read(fd, &ev, sizeof(struct input_event));
// 打印当前触发类型
// printf("ev == %x \n",ev.type );
switch(ev.type)
{
case EV_SYN:
printf("-------------------------\n");
break;

// 按键
case EV_KEY:
printf("key down / up: %d \n",ev.code );
break;

// 鼠标
case EV_REL:
printf("mouse: ");
if (ev.code == REL_X) {
printf(" x -- %d\n", ev.value);
} else if (ev.code == REL_Y) {
printf(" y -- %d\n", ev.value);
}
break;

// 触摸屏
case EV_ABS:
printf("ts: ");
if(ev.code == ABS_MT_POSITION_X) {
printf(" x -- %d\n", ev.value);
x = ev.value;
} else if (ev.code == ABS_MT_POSITION_Y) {
printf(" y -- %d\n", ev.value);
y = ev.value;
} else if (ev.code == ABS_PRESSURE) {
printf(" pressure: %d\n", ev.value);
}
break;
}
}
close(fd);
return 0;
}