stm32mp157 盘古开发板 Linux内核版本4.19
目录
1、朱有鹏老师的视频课程笔记和应用测试代码:
2、input子系统架构分析
2.1、输入核心层源码分析
2.1.1、首先是核心模块注册input_init
2.1.2、提供给设备驱动层的接口函数input_allocate_device、input_set_capability、input_register_device
2.1.3、handler和device的匹配
2.1.4、事件驱动层的接口函数input_register_handler、input_register_handle
2.2、最上层:输入事件驱动层源码分析
2.3、最下层:输入设备驱动层
1、朱有鹏老师的视频课程笔记和应用测试代码:
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151***********《朱有鹏老师嵌入式linux核心课程》 *********** 《5.linux驱动开发-第5部分-5.8.input子系统基础之按键》 -------------------------------------------------------- 本课程由朱老师物联网大讲堂推出并提供技术支持,课件可打包下载 网盘地址:http://yunpan.cn/cjVy3RAgfDufK 访问密码 4ad7 技术交流QQ群:朱老师物联网讲堂1群 397164505 朱老师个人QQ:1264671872 -------------------------------------------------------- 第一部分、章节目录 5.8.1.什么是input子系统 5.8.2.input设备应用层编程实践1 5.8.3.input设备应用层编程实践2 5.8.4.input子系统架构总览1 5.8.5.input子系统架构总览2 5.8.6.输入核心层源码分析1 5.8.7.输入核心层源码分析2 5.8.8.输入事件驱动层源码分析 5.8.9.输入设备驱动层源码分析1 5.8.10.输入设备驱动层源码分析2 5.8.11.中断方式按键驱动实战1 5.8.12.中断方式按键驱动实战2 第二部分、章节介绍 5.8.1.什么是input子系统 本节全面介绍input子系统的概念和来源、解决的主要问题,目的是让大家对linux中输入类设备有一个全面了解 5.8.2.input设备应用层编程实践1 本节实践编写应用层程序,操作键盘和鼠标这些常见input类设备,目的是让大家先学会使用输入类设备,后面再来分析驱动。 5.8.3.input设备应用层编程实践2 本节接着上节对读上来的数据进行解析,分析其规律并且和设备本身特性进行关联分析。 5.8.4.input子系统架构总览1 本节详细介绍input子系统的三层结构以及各层的功能特点。 5.8.5.input子系统架构总览2 本节介绍input子系统下编写驱动的路线和方法。 5.8.6.输入核心层源码分析1 本节分析输入核心层,主要是模块装载和开放给其他层的接口的分析。 5.8.7.输入核心层源码分析2 本节接着分析输入核心层,主要是handler和device的匹配、安装部分的源码分析。 5.8.8.输入事件驱动层源码分析 本节对输入事件层源码分析,主要以evdev.c为例分析了event handler的安装函数、数据上报函数的实现。 5.8.9.输入设备驱动层源码分析1 本节分析输入设备驱动层,以x210自带的按键驱动为例进行分析。 5.8.10.输入设备驱动层源码分析2 本节接着分析按键驱动,主要是一些源码细节探究。 5.8.11.中断方式按键驱动实战1 本节开始按键驱动实战,先找到内核提供的模版,并且对模版程序进行分析讲解。 5.8.12.中断方式按键驱动实战2 本节以模版驱动为基础,结合x210开发板的情况进行驱动移植、编译、测试、修改。 第三部分、随堂记录 5.8.1.什么是input子系统 5.8.1.1、何为输入设备 5.8.1.2、linux中输入设备的编程模型 (1)命令行界面的输入类设备应用接口 (2)GUI界面带来的麻烦、不同的输入类设备也会带来麻烦 (3)struct input_event 5.8.1.3、input子系统简介 (1)linux的input子系统解决了什么问题 (2)input子系统分4个部分:应用层 + input event + input core + 硬件驱动 (3)input子系统如何工作 (4)事件驱动型GUI框架,如QT、VC等。 5.8.2.input设备应用层编程实践1 5.8.2.1、确定设备文件名 (1)应用层操作驱动有2条路:/dev目录下的设备文件,/sys目录下的属性文件 (2)input子系统用的/dev目录下的设备文件,具体一般都是在 /dev/input/eventn (3)用cat命令来确认某个设备文件名对应哪个具体设备。我在自己的ubuntu中实测的键盘是event1,而鼠标是event3. 5.8.2.2、标准接口打开并读取文件 5.8.2.3、解析struct input_event 5.8.3.input设备应用层编程实践2 5.8.3.1、解析键盘事件数据 5.8.3.2、解析鼠标事件数据 5.8.4.input子系统架构总览1 5.8.4.1、input子系统分为三层 (1)最上层:输入事件驱动层,evdev.c和mousedev.c和joydev.c属于这一层 (2)中间层:输入核心层,input.c属于这一层 (3)最下层:输入设备驱动层,drivers/input/xxx 文件夹下 5.8.4.2、input类设备驱动开发方法 (1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层 (2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。 (3)input子系统不算复杂,学习时要注意“标准模式”四个字。 5.8.5.input子系统架构总览2 5.8.6.输入核心层源码分析1 5.8.6.1、核心模块注册input_init (1)class_register (2)input_proc_init (3)register_chrdev 5.8.6.2、设备驱动层的接口函数 (1)input_allocate_device (2)input_set_capability (3)input_register_device 5.8.7.输入核心层源码分析2 5.8.7.1、handler和device的匹配 (1)input_attach_handler input_match_device 匹配device和handler handler->connect(handler, dev, id) 连接device和handler 5.8.7.2、事件驱动层的接口函数 (1)input_register_handler (2)input_register_handle 5.8.8.输入事件驱动层源码分析 5.8.8.1、input_handler 5.8.8.2、evdev_connect 5.8.8.3、evdev_event 5.8.9_10.输入设备驱动层源码分析1_2 5.8.9.1、先找到bsp中按键驱动源码 (1)锁定目标:板载按键驱动 (2)确认厂家提供的BSP是否已经有驱动 (3)找到bsp中的驱动源码 5.8.9.2、按键驱动源码初步分析 (1)模块装载分析 (2)平台总线相关分析 (3)确定重点:probe函数 5.8.9.3、源码细节实现分析 (1)gpio_request (2)input_allocate_device (3)input_register_device (4)timer 5.8.11.中断方式按键驱动实战1 5.8.11.1、模板 (1)input类设备驱动模式非常固定,用参考模版修改即可 (2)新建驱动项目并粘贴模版内容 5.8.11.2、模板驱动的解析 5.8.11.3、着手移植驱动 5.8.12.中断方式按键驱动实战2 5.8.12.1、驱动移植细节 5.8.12.2、驱动实践
上图即为input子系统架构的三层:drivers(最下层,输入设备驱动层)、core(中间层,核心层)、handlers(最上层,输入事件驱动层)
应用程序测试代码:
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#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <string.h> #define DEVICE_KEY "/dev/input/event1" #define DEVICE_MOUSE "/dev/input/event3" #define X210_KEY "/dev/input/event1" int main(void) { int fd = -1, ret = -1; struct input_event ev; // 第1步:打开设备文件 fd = open(X210_KEY, O_RDONLY); if (fd < 0) { perror("open"); return -1; } while (1) { // 第2步:读取一个event事件包 memset(&ev, 0, sizeof(struct input_event)); ret = read(fd, &ev, sizeof(struct input_event)); if (ret != sizeof(struct input_event)) { perror("read"); close(fd); return -1; } // 第3步:解析event包,才知道发生了什么样的输入事件 printf("-------------------------n"); printf("type: %hdn", ev.type); printf("code: %hdn", ev.code); printf("value: %dn", ev.value); printf("n"); } // 第4步:关闭设备 close(fd); return 0; }
2、input子系统架构分析
2.1、输入核心层源码分析
相关代码linux_kernellinux-stdriversinput目录下input.c
2.1.1、首先是核心模块注册input_init
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
41static int __init input_init(void) { int err; //在/sys/class下面注册一个input类 err = class_register(&input_class); if (err) { pr_err("unable to register input_dev classn"); return err; } //proc目录下面 err = input_proc_init(); if (err) goto fail1; //注册字符设备驱动,主设备号是13 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; } static void __exit input_exit(void) { input_proc_exit(); unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES); class_unregister(&input_class); } subsys_initcall(input_init); module_exit(input_exit);
2.1.2、提供给设备驱动层的接口函数input_allocate_device、input_set_capability、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/** * input_allocate_device - allocate memory for new input device * * Returns prepared struct input_dev or %NULL. * * NOTE: Use input_free_device() to free devices that have not been * registered; input_unregister_device() should be used for already * registered devices. */ //相当于动态申请一个input dev设备变量,并对其进行初步必要的初始化 struct input_dev *input_allocate_device(void) { static atomic_t input_no = ATOMIC_INIT(-1); struct input_dev *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev) { dev->dev.type = &input_dev_type; dev->dev.class = &input_class; device_initialize(&dev->dev); mutex_init(&dev->mutex); spin_lock_init(&dev->event_lock); timer_setup(&dev->timer, NULL, 0); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); dev_set_name(&dev->dev, "input%lu", (unsigned long)atomic_inc_return(&input_no)); __module_get(THIS_MODULE); } return dev; } EXPORT_SYMBOL(input_allocate_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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125/** * input_register_device - register device with input core * @dev: device to be registered * * This function registers device with input core. The device must be * allocated with input_allocate_device() and all it's capabilities * set up before registering. * If function fails the device must be freed with input_free_device(). * Once device has been successfully registered it can be unregistered * with input_unregister_device(); input_free_device() should not be * called in this case. * * Note that this function is also used to register managed input devices * (ones allocated with devm_input_allocate_device()). Such managed input * devices need not be explicitly unregistered or freed, their tear down * is controlled by the devres infrastructure. It is also worth noting * that tear down of managed input devices is internally a 2-step process: * registered managed input device is first unregistered, but stays in * memory and can still handle input_event() calls (although events will * not be delivered anywhere). The freeing of managed input device will * happen later, when devres stack is unwound to the point where device * allocation was made. */ //将input_dev类型的变量注册进系统 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; if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) { dev_err(&dev->dev, "Absolute device without dev->absinfo, refusing to registern"); return -EINVAL; } if (dev->devres_managed) { devres = devres_alloc(devm_input_device_unregister, sizeof(*devres), GFP_KERNEL); if (!devres) return -ENOMEM; devres->input = dev; } /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ 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; } /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ 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; error = device_add(&dev->dev); if (error) goto err_free_vals; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); pr_info("%s as %sn", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) goto err_device_del; //添加到输入设备列表里面 list_add_tail(&dev->node, &input_dev_list); //遍历所有的handler,看哪个和刚注册进来的设备驱动能够匹配, //如果匹配就会将其绑定,并且生成应用层可以操作的文件接口 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler);//负责匹配和链接的 //handler和device的挂接是handle结构体,即在handle结构体 //中记录配对的device和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);
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/** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event * @type: type of the event (EV_KEY, EV_REL, etc...) * @code: event code * * In addition to setting up corresponding bit in appropriate capability * bitmap the function also adjusts dev->evbit. */ //声明驱动向上层应用上报哪些信息,在设置驱动向上上报事件的能力 //专用的能力方面设置 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) { switch (type) { case EV_KEY: __set_bit(code, dev->keybit); break; case EV_REL: __set_bit(code, dev->relbit); break; case EV_ABS: input_alloc_absinfo(dev); if (!dev->absinfo) return; __set_bit(code, dev->absbit); break; case EV_MSC: __set_bit(code, dev->mscbit); break; case EV_SW: __set_bit(code, dev->swbit); break; case EV_LED: __set_bit(code, dev->ledbit); break; case EV_SND: __set_bit(code, dev->sndbit); break; case EV_FF: __set_bit(code, dev->ffbit); break; case EV_PWR: /* do nothing */ break; default: pr_err("%s: unknown type %u (code %u)n", __func__, type, code); dump_stack(); return; } __set_bit(type, dev->evbit); } EXPORT_SYMBOL(input_set_capability);
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
73static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { int disposition = input_get_disposition(dev, type, code, &value); if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) add_input_randomness(type, code, value); if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); if (!dev->vals) return; if (disposition & INPUT_PASS_TO_HANDLERS) { struct input_value *v; if (disposition & INPUT_SLOT) { v = &dev->vals[dev->num_vals++]; v->type = EV_ABS; v->code = ABS_MT_SLOT; v->value = dev->mt->slot; } v = &dev->vals[dev->num_vals++]; v->type = type; v->code = code; v->value = value; } if (disposition & INPUT_FLUSH) { if (dev->num_vals >= 2) input_pass_values(dev, dev->vals, dev->num_vals); // 最终执行了handle->handler里面的event函数 dev->num_vals = 0; } else if (dev->num_vals >= dev->max_vals - 2) { dev->vals[dev->num_vals++] = input_value_sync; input_pass_values(dev, dev->vals, dev->num_vals); dev->num_vals = 0; } } /** * 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 * * This function should be used by drivers implementing various input * devices to report input events. See also input_inject_event(). * * NOTE: input_event() may be safely used right after input device was * allocated with input_allocate_device(), even before it is registered * with input_register_device(), but the event will not reach any of the * input handlers. Such early invocation of input_event() may be used * to 'seed' initial state of a switch or initial position of absolute * axis, etc. */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { unsigned long flags; if (is_event_supported(type, dev->evbit, EV_MAX)) { spin_lock_irqsave(&dev->event_lock, flags); input_handle_event(dev, type, code, value); // 上报事件 spin_unlock_irqrestore(&dev->event_lock, flags); } } EXPORT_SYMBOL(input_event);
2.1.3、handler和device的匹配
input_attach_handler
input_match_device 匹配device和handler
handler->connect(handler, dev, id) 连接device和handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; //匹配device和handler id = input_match_device(handler, dev); if (!id) return -ENODEV; //链接device和handler error = handler->connect(handler, dev, id); if (error && error != -ENODEV) pr_err("failed to attach handler %s to device %s, error: %dn", handler->name, kobject_name(&dev->dev.kobj), error); return error; }
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/** * struct input_handler - implements one of interfaces for input devices * @private: driver-specific data * @event: event handler. This method is being called by input core with * interrupts disabled and dev->event_lock spinlock held and so * it may not sleep * @events: event sequence handler. This method is being called by * input core with interrupts disabled and dev->event_lock * spinlock held and so it may not sleep * @filter: similar to @event; separates normal event handlers from * "filters". * @match: called after comparing device's id with handler's id_table * to perform fine-grained matching between device and handler * @connect: called when attaching a handler to an input device * @disconnect: disconnects a handler from input device * @start: starts handler for given handle. This function is called by * input core right after connect() method and also when a process * that "grabbed" a device releases it * @legacy_minors: set to %true by drivers using legacy minor ranges * @minor: beginning of range of 32 legacy minors for devices this driver * can provide * @name: name of the handler, to be shown in /proc/bus/input/handlers * @id_table: pointer to a table of input_device_ids this driver can * handle * @h_list: list of input handles associated with the handler * @node: for placing the driver onto input_handler_list * * Input handlers attach to input devices and create input handles. There * are likely several handlers attached to any given input device at the * same time. All of them will get their copy of input event generated by * the device. * * The very same structure is used to implement input filters. Input core * allows filters to run first and will not pass event to regular handlers * if any of the filters indicate that the event should be filtered (by * returning %true from their filter() method). * * Note that input core serializes calls to connect() and disconnect() * methods. */ 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); // dev和handler匹配 bool (*match)(struct input_handler *handler, struct input_dev *dev); // 将dev和handler链接 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; struct list_head h_list; struct list_head node; };
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
74struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; // BITS_TO_LONGS的作用是计算EV_CNT这么多需要多少个32位的bit来存放记录 // 用来标注自己的驱动有哪些没有哪些,比如只有3个按键...但是系统支持的有很多 // 比如鼠标、触摸屏、键盘等,想鼠标的左右击也需要标注,这就是用其中的一个bit // 来标注。 去标注自己支持的事件 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)]; 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); 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; struct list_head node; unsigned int num_vals; unsigned int max_vals; struct input_value *vals; bool devres_managed; };
2.1.4、事件驱动层的接口函数input_register_handler、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
27
28
29
30
31/** * input_register_handler - register a new input handler * @handler: handler to be registered * * This function registers a new input handler (interface) for input * devices in the system and attaches it to all input devices that * are compatible with the handler. */ 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); list_add_tail(&handler->node, &input_handler_list); 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);
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/** * input_register_handle - register a new input handle * @handle: handle to register * * This function puts a new input handle onto device's * and handler's lists so that events can flow through * it once it is opened using input_open_device(). * * This function is supposed to be called from handler's * connect() method. */ int input_register_handle(struct input_handle *handle) { struct input_handler *handler = handle->handler; struct input_dev *dev = handle->dev; int error; /* * We take dev->mutex here to prevent race with * input_release_device(). */ error = mutex_lock_interruptible(&dev->mutex); if (error) return error; /* * Filters go to the head of the list, normal handlers * to the tail. */ if (handler->filter) list_add_rcu(&handle->d_node, &dev->h_list); else list_add_tail_rcu(&handle->d_node, &dev->h_list); mutex_unlock(&dev->mutex); /* * Since we are supposed to be called from ->connect() * which is mutually exclusive with ->disconnect() * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); return 0; } EXPORT_SYMBOL(input_register_handle);
即在input_handle中记录input_handler和dev的配对情况,民政局。
总结,核心层主要的功能是为设备驱动层(drivers层)和输入事件驱动层(handler层)提供注册接口,同时将这两层能够配对的设备做好配对绑定,然后提供从drivers到handlers数据传输通道,即将dev的数据直接传输给已经配对的handle。
2.2、最上层:输入事件驱动层源码分析
这一层的相关代码linux_kernellinux-stdriversinput目录下evdev.c和joydev.c
evdev.c文件里面,首先是模块的注册:
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
31static struct input_handler evdev_handler = { .event = evdev_event, .events = evdev_events,//将数据事件数据放进buffer以及发信号通知应用层,都是这里面做的 .connect = evdev_connect, .disconnect = evdev_disconnect, .legacy_minors = true, //次设备号的基地址,有8个,均是32的整数倍,一个handler对应 //多个dev的时候,第一个dev的次设备号是32*n,第二个是32*n+1... .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, }; static int __init evdev_init(void) { //将上面声明的handler注册到核心层 return input_register_handler(&evdev_handler); } static void __exit evdev_exit(void) { input_unregister_handler(&evdev_handler); } module_init(evdev_init); module_exit(evdev_exit); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Input driver event char devices"); MODULE_LICENSE("GPL");
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/* * Create new evdev device. Note that input core serializes calls * to connect and disconnect. */ 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: %dn", error); return error; } 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; // 设备文件名的产生,event0,event1...次设备号是32*n+event后面的数字 dev_set_name(&evdev->dev, "event%d", dev_no); // dev和handler配对时,需要先到handle这里登记,相当于民政局 evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; // 这里的主设备是13,次设备号是32*n+minor,moinor在上面有赋值计算 evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev);// 这个函数和下面的cdev_device_add函数一起构成device_register函数 // device_register就是注册设备,在input.c中class_register是注册class下面的input类 // 这里是注册input类里面的设备,如event1 , event2 , ... error = input_register_handle(&evdev->handle); // 注册handle,民政局的登记册 if (error) goto err_free_evdev; cdev_init(&evdev->cdev, &evdev_fops); 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
15
16
17static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, // 应用层调用的read,其实就是这个read // 这个evdev_read里面调用了capy_to_user // 传到用户空间 .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, .flush = evdev_flush, .llseek = no_llseek, };
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
50static 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; /* * count == 0 is special - no IO is done but we check * for error conditions (see above). */ 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; 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; }
总结,这一层主要向应用层提供操作接口。
2.3、最下层:输入设备驱动层
代码目录linux-stdriversinputkeyboard gpio_keys.c
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
27static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .driver = { .name = "gpio-keys", .pm = &gpio_keys_pm_ops, .of_match_table = gpio_keys_of_match, } }; static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver); } static void __exit gpio_keys_exit(void) { platform_driver_unregister(&gpio_keys_device_driver); } late_initcall(gpio_keys_init); module_exit(gpio_keys_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>"); MODULE_DESCRIPTION("Keyboard driver for GPIOs"); MODULE_ALIAS("platform:gpio-keys");
1
2
3
4
5static const struct of_device_id gpio_keys_of_match[] = { { .compatible = "gpio-keys", }, { }, }; MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
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
105
106
107
108
109
110
111static int gpio_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); struct fwnode_handle *child = NULL; struct gpio_keys_drvdata *ddata; struct input_dev *input; size_t size; int i, error; int wakeup = 0; if (!pdata) { pdata = gpio_keys_get_devtree_pdata(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); } size = sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data); ddata = devm_kzalloc(dev, size, GFP_KERNEL); if (!ddata) { dev_err(dev, "failed to allocate staten"); return -ENOMEM; } ddata->keymap = devm_kcalloc(dev, pdata->nbuttons, sizeof(ddata->keymap[0]), GFP_KERNEL); if (!ddata->keymap) return -ENOMEM; // input.c中给input_dev分配内存的函数 input = devm_input_allocate_device(dev); if (!input) { dev_err(dev, "failed to allocate input devicen"); return -ENOMEM; } ddata->pdata = pdata; ddata->input = input; mutex_init(&ddata->disable_lock); platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = dev; input->open = gpio_keys_open; input->close = gpio_keys_close; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; input->keycode = ddata->keymap; input->keycodesize = sizeof(ddata->keymap[0]); input->keycodemax = pdata->nbuttons; /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); // 设置input里面的evbit支持EV_REP事件 for (i = 0; i < pdata->nbuttons; i++) { const struct gpio_keys_button *button = &pdata->buttons[i]; if (!dev_get_platdata(dev)) { // 从设备树取出对应的节点 child = device_get_next_child_node(dev, child); if (!child) { dev_err(dev, "missing child device node for entry %dn", i); return -EINVAL; } } // 设置相关的按键引脚,在这里面有中断设置 error = gpio_keys_setup_key(pdev, input, ddata, button, i, child); if (error) { fwnode_handle_put(child); return error; } if (button->wakeup) wakeup = 1; } fwnode_handle_put(child); error = devm_device_add_group(dev, &gpio_keys_attr_group); if (error) { dev_err(dev, "Unable to export keys/switches, error: %dn", error); return error; } error = input_register_device(input); // 注册设备 if (error) { dev_err(dev, "Unable to register input device, error: %dn", error); return error; } device_init_wakeup(dev, wakeup); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 上报事件 static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state; state = gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { dev_err(input->dev.parent, "failed to get gpio state: %dn", state); return; } if (type == EV_ABS) { if (state) input_event(input, type, button->code, button->value);// 上报事件 } else { input_event(input, type, *bdata->code, state); } input_sync(input); }
最后
以上就是美满红酒最近收集整理的关于Linux驱动分析——input输入子系统1、朱有鹏老师的视频课程笔记和应用测试代码:2、input子系统架构分析的全部内容,更多相关Linux驱动分析——input输入子系统1、朱有鹏老师内容请搜索靠谱客的其他文章。
发表评论 取消回复