Linux INPUT子系统实验

Input子系统概述

按键、鼠标、键盘、触摸屏等都属于输入 (input) 设备, Linux 内核为此专门做了一个叫做 input
子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了 input
架,用户只需要负责上报输入事件,比如按键值、坐标等信息, input 核心层负责处理这些事件。
input 子系统分为 input 驱动 层、input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点, input 子系统框架如图所示:
左边就是最底层的具体设备,比如按键、 USB 键盘 / 鼠标等,中间部分属于
Linux 内核空间,分为驱动层、核心层和事件层,最右边的就是用户空间,所有的输入设备以文
件的形式供用户应用程序使用。可以看出 input 子系统用到了我们前面讲解的驱动分层模型,我
们编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层,这三个层的分工如下:
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互。

Input驱动编写流程

input 核心层会向 Linux 内核注册一个字符设备,找到  drivers/input/input.c 这个文件,
input.c 就是 input 输入子系统的核心层,此文件里面有如下所示代码:
1767 struct class input_class = {
1768     .name = "input",
1769     .devnode = input_devnode,
1770 };
......
2414 static int __init input_init(void)
2415 {
2416     int err;
2417
2418     err = class_register(&input_class);
2419     if (err) {
2420         pr_err("unable to register input_dev class\n");
2421         return err;
2422     }
2423
2424     err = input_proc_init();
2425     if (err)
2426         goto fail1;
2427
2428     err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
2429         INPUT_MAX_CHAR_DEVICES, "input");
2430     if (err) {
2431         pr_err("unable to register char major %d", INPUT_MAJOR);
2432         goto fail2;
2433     }
2434
2435     return 0;
2436
2437 fail2: input_proc_exit();
2438 fail1: class_unregister(&input_class);
2439     return err;
2440 }
2418 行,注册一个 input 类,这样系统启动以后就会在 /sys/class 目录下有一个 input
目录,如图所示:
2428~2429 行,注册一个字符设备,主设备号为 INPUT_MAJOR INPUT_MAJOR 定义
include/uapi/linux/major.h 文件中,定义如下:
#define INPUT_MAJOR   13
    input 子系统的所有设备主设备号都为 13 ,我们在使用 input 子系统处理输入设备
的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device。

注册 input_dev

。。。

上报输入事件

。。。

input_event 结构体

实验程序编写

修改设备树

使用key按键节点的设备树

按键input驱动程序编写

keyinput.c

1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/of_gpio.h>
14 #include <linux/input.h>
15 #include <linux/semaphore.h>
16 #include <linux/timer.h>
17 #include <linux/of_irq.h>
18 #include <linux/irq.h>
19 #include <asm/mach/map.h>
20 #include <asm/uaccess.h>
21 #include <asm/io.h>

32 #define KEYINPUT_CNT 1 /* 设备号个数 */
33 #define KEYINPUT_NAME "keyinput" /* 名字 */
34 #define KEY0VALUE 0X01 /* KEY0 按键值 */
35 #define INVAKEY 0XFF /* 无效的按键值 */
36 #define KEY_NUM 1 /* 按键数量 */
37 
38 /* 中断 IO 描述结构体 */
39 struct irq_keydesc {
40     int gpio; /* gpio */
41     int irqnum; /* 中断号 */
42     unsigned char value; /* 按键对应的键值 */
43     char name[10]; /* 名字 */
44     irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
45 };
46 
47 /* keyinput 设备结构体 */
48 struct keyinput_dev{
49     dev_t devid; /* 设备号 */
50     struct cdev cdev; /* cdev */
51     struct class *class; /* 类 */
52     struct device *device; /* 设备 */
53     struct device_node *nd; /* 设备节点 */
54     struct timer_list timer; /* 定义一个定时器 */
55     struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */
56     unsigned char curkeynum; /* 当前的按键号 */
57     struct input_dev *inputdev; /* input 结构体 */
58 };
59 
60 struct keyinput_dev keyinputdev; /* key input 设备 */
61 
62 /* @description : 中断服务函数,开启定时器,延时 10ms,
63 * 定时器用于按键消抖。
64 * @param - irq : 中断号
65 * @param - dev_id : 设备结构。
66 * @return : 中断执行结果
67 */
68 static irqreturn_t key0_handler(int irq, void *dev_id)
69 {
70     struct keyinput_dev *dev = (struct keyinput_dev *)dev_id;
71 
72     dev->curkeynum = 0;
73     dev->timer.data = (volatile long)dev_id;
74     mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
75     return IRQ_RETVAL(IRQ_HANDLED);
76 }
77 
78 /* @description : 定时器服务函数,用于按键消抖,定时器到了以后
79 * 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
80 * @param - arg : 设备结构变量
81 * @return : 无
82 */
83 void timer_function(unsigned long arg)
84 {
85     unsigned char value;
86     unsigned char num;
87     struct irq_keydesc *keydesc;
88     struct keyinput_dev *dev = (struct keyinput_dev *)arg;
89 
90     num = dev->curkeynum;
91     keydesc = &dev->irqkeydesc[num];
92     value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
93     if(value == 0){ /* 按下按键 */
94         /* 上报按键值 */
95         //input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
96         input_report_key(dev->inputdev, keydesc->value, 1);/*1,按下*/
97         input_sync(dev->inputdev);
98     } else { /* 按键松开 */
99         //input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
100     input_report_key(dev->inputdev, keydesc->value, 0);
101     input_sync(dev->inputdev);
102     } 
103 }
104
105 /*
106 * @description : 按键 IO 初始化
107 * @param : 无
108 * @return : 无
109 */
110 static int keyio_init(void)
111 {
112     unsigned char i = 0;
113     char name[10];
114     int ret = 0;
115 
116     keyinputdev.nd = of_find_node_by_path("/key");
117     if (keyinputdev.nd== NULL){
118         printk("key node not find!\r\n");
119         return -EINVAL;
120     }
121
122     /* 提取 GPIO */
123     for (i = 0; i < KEY_NUM; i++) {
124         keyinputdev.irqkeydesc[i].gpio = 
            of_get_named_gpio(keyinputdev.nd,"key-gpio", i);
125         if (keyinputdev.irqkeydesc[i].gpio < 0) {
126             printk("can't get key%d\r\n", i);
127         }
128     }
129 
130     /* 初始化 key 所使用的 IO,并且设置成中断模式 */
131     for (i = 0; i < KEY_NUM; i++) {
132         memset(keyinputdev.irqkeydesc[i].name, 0, sizeof(name)); 
133         sprintf(keyinputdev.irqkeydesc[i].name, "KEY%d", i); 
134         gpio_request(keyinputdev.irqkeydesc[i].gpio,
            keyinputdev.irqkeydesc[i].name);
135         gpio_direction_input(keyinputdev.irqkeydesc[i].gpio); 
136         keyinputdev.irqkeydesc[i].irqnum =irq_of_parse_and_map(keyinputdev.nd, i);
137     }
138     /* 申请中断 */
139     keyinputdev.irqkeydesc[0].handler = key0_handler;
140     keyinputdev.irqkeydesc[0].value = KEY_0;
141 
142     for (i = 0; i < KEY_NUM; i++) {
143         ret = request_irq(keyinputdev.irqkeydesc[i].irqnum,
            keyinputdev.irqkeydesc[i].handler,
144         IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                keyinputdev.irqkeydesc[i].name, &keyinputdev);
145         if(ret < 0){
146             printk("irq %d request failed!\r\n",keyinputdev.irqkeydesc[i].irqnum);
147             return -EFAULT;
148         }
149     }
150
151     /* 创建定时器 */
152     init_timer(&keyinputdev.timer);
153     keyinputdev.timer.function = timer_function;
154
155     /* 申请 input_dev */
156     keyinputdev.inputdev = input_allocate_device();
157     keyinputdev.inputdev->name = KEYINPUT_NAME;
158 #if 0
159     /* 初始化 input_dev,设置产生哪些事件 */
160     __set_bit(EV_KEY, keyinputdev.inputdev->evbit); /*按键事件 */
161     __set_bit(EV_REP, keyinputdev.inputdev->evbit); /* 重复事件 */
162
163     /* 初始化 input_dev,设置产生哪些按键 */
164     __set_bit(KEY_0, keyinputdev.inputdev->keybit);
165 #endif
166
167 #if 0
168     keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_    MASK(EV_REP);
169     keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=BIT_MASK(KEY_0);
170 #endif
171
172 keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_MASK(EV_REP);
173 input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
174
175 /* 注册输入设备 */
176 ret = input_register_device(keyinputdev.inputdev);
177 if (ret) {
178 printk("register input device failed!\r\n");
179 return ret;
180 }
181 return 0;
182 }
183
184 /*
185 * @description : 驱动入口函数
186 * @param : 无
187 * @return : 无
188 */
189 static int __init keyinput_init(void)
190 {
191 keyio_init();
192 return 0;
193 }
194
195 /*
196 * @description : 驱动出口函数
197 * @param : 无
198 * @return : 无
199 */
200 static void __exit keyinput_exit(void)
201 {
202     unsigned int i = 0;
203     /* 删除定时器 */
204     del_timer_sync(&keyinputdev.timer);
205 
206     /* 释放中断*/
207     for (i = 0; i < KEY_NUM; i++) {
208         free_irq(keyinputdev.irqkeydesc[i].irqnum, &keyinputdev);
209     }
210     /* 释放 IO */
211     for (i = 0; i < KEY_NUM; i++) {
212         gpio_free(keyinputdev.irqkeydesc[i].gpio);
213     }
214     /* 释放 input_dev */
215     input_unregister_device(keyinputdev.inputdev);
216     input_free_device(keyinputdev.inputdev);
217 }
218
219 module_init(keyinput_init);
220 module_exit(keyinput_exit);
221 MODULE_LICENSE("GPL");
222 MODULE_AUTHOR("zipeng");

编写测试 APP

keyinputApp.c

1 #include "stdio.h"
2 #include "unistd.h"
3 #include "sys/types.h"
4 #include "sys/stat.h"
5 #include "sys/ioctl.h"
6 #include "fcntl.h"
7 #include "stdlib.h"
8 #include "string.h"
9 #include <poll.h>
10 #include <sys/select.h>
11 #include <sys/time.h>
12 #include <signal.h>
13 #include <fcntl.h>
14 #include <linux/input.h>
27 /* 定义一个 input_event 变量,存放输入事件信息 */
28 static struct input_event inputevent;
29
30 /*
31 * @description : main 主程序
32 * @param - argc : argv 数组元素个数
33 * @param - argv : 具体参数
34 * @return : 0 成功;其他 失败
35 */
36 int main(int argc, char *argv[])
37 {
38     int fd;
39     int err = 0;
40     char *filename;
41
42     filename = argv[1];
43
44     if(argc != 2) {
45         printf("Error Usage!\r\n");
46         return -1;
47     }
48
49     fd = open(filename, O_RDWR);
50     if (fd < 0) {
51         printf("Can't open file %s\r\n", filename);
52         return -1;
53     }
54
55     while (1) {
56         err = read(fd, &inputevent, sizeof(inputevent));
57         if (err > 0) { /* 读取数据成功 */
58             switch (inputevent.type) {
59                 case EV_KEY:
60                     if (inputevent.code < BTN_MISC) { /* 键盘键值 */
61                         printf("key %d %s\r\n", inputevent.code,
                                inputevent.value ? "press" : "release");
62                     } else {
63                         printf("button %d %s\r\n", inputevent.code,
                                inputevent.value ? "press" : "release");
64                     }
65                 break;
66
67                 /* 其他类型的事件,自行处理 */
68                 case EV_REL:
69                 break;
70                 case EV_ABS:
71                 break;
72                 case EV_MSC:
73                 break;
74                 case EV_SW:
75                 break;
76             }
77         } else {
78             printf("读取数据失败\r\n");
79         }
80     }
81     return 0;
82 }

运行测试

编译驱动程序

KERNELDIR := /home/zipeng/linux/myKernel/linux-imxrel_imx_4.1.15_2.1.0_ga_alientek
...... 
4 obj-m := keyinput.o
......
11 clean:
12 $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

make -j16

 编译测试 APP

 arm-linux-gnueabihf-gcc keyinputApp.c -o keyinputApp

运行测试

将前面编译出来 keyinput.ko keyinputApp 这两个文件拷贝到rootfs/lib/modules/4.1.15

目录中,重启开发板,进入到目录 lib/modules/4.1.15 中。在加载 keyinput.ko 驱动模块之前,先
看一下 /dev/input 目录下都有哪些文件,结果如图   所示。

 

当前 /dev/input 目录只有 event0 mice 这两个文件。输入 如下命令加载 keyinput.ko 这个驱动模块。
depmod     //第一次加载驱动的时候需要运行此命令
modprobe keyinput.ko // 加载驱动模块
当驱动模块加载成功以后再来看一下 /dev/input 目录下有哪些文件,结果如图:

 输入如下测试命令:

./keyinputApp /dev/input/event1

 Linux 自带按键驱动程序的使用

-> Device Drivers
        -> Input device support
                -> Generic input layer (needed for keyboard, mouse, ...) (INPUT [=y])
                        -> Keyboards (INPUT_KEYBOARD [=y])
                                ->GPIO Buttons
  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值