开始
由于内核中已经申请过home、back按键的中断,首先需要从内核中取消“KEYBOARD_GPIO”,路径是“Device Drivers”–>“Input device support”–>“Keyboards”–>“GPIO Buttons”。取消后,编译内核镜像进行烧录会发现cat /proc/interrupts
中已经没有BUTTON1、BUTTON2中断了。
编写驱动
keys_input_demo_drv.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
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
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <mach/gpio.h>
#include <linux/input.h>
MODULE_LICENSE("Dual BSD/GPL");
#define MY_KEY_HOME EXYNOS4_GPX1(1)
#define MY_KEY_BACK EXYNOS4_GPX1(2)
#define MY_KEY_SLEEP EXYNOS4_GPX3(3)
#define MY_KEY_VOL_U EXYNOS4_GPX2(1)
#define MY_KEY_VOL_D EXYNOS4_GPX2(0)
static struct input_dev *buttons_dev;
static irqreturn_t ehome_interrupt(int irq, void *dev_id)
{
//最后一个参数: 0-松开, 非 0-按下
input_report_key(buttons_dev, KEY_L, 1);
//通知上层,本次事件结束
input_sync(buttons_dev);
//最后一个参数: 0-松开, 非 0-按下
input_report_key(buttons_dev, KEY_L, 0);
//通知上层,本次事件结束
input_sync(buttons_dev);
return IRQ_HANDLED;
}
static int __init keys_input_init(void)
{
int ret;
printk("%s(%d)\n", __FUNCTION__, __LINE__);
//第一步:定义之后,向内核申请一个 input_dev 事件,标准内核函数接口 input_allocate_device 和 input_free_device
buttons_dev = input_allocate_device();
//第二步:配置输入子系统的某一类“事件类型”,例如:按键事件,鼠标事件,触摸事件等等
//在 input.h 头文件中,定义了 10 多种事件类型,这里定义按键事件
set_bit(EV_KEY, buttons_dev->evbit);
//set_bit(EV_REP, buttons_dev->evbit); //重复扫描
//第三步:配置能产生这类事件中的那些具体操作,例如:按键按键事件中,可以输入键盘 a,键盘 b 等
//这里定义产生 l 按键值
set_bit(KEY_L, buttons_dev->keybit);
//[可选]设置input结构的信息
buttons_dev->name="I am simplest input subsystem";//输入设备名字
buttons_dev->id.bustype=BUS_I8042;//设置产品id
buttons_dev->id.vendor=0x1111;
buttons_dev->id.product=0x2222;
buttons_dev->id.version=0x3333;
//第四步:注册输入子系统,标准内核函数接口 input_register_device 和 input_unregister_device
ret = input_register_device(buttons_dev);
if(ret <0){
printk("%s(%d)\n", __FUNCTION__, __LINE__);
goto exit;
}
ret = request_irq(gpio_to_irq(MY_KEY_HOME),ehome_interrupt,IRQ_TYPE_EDGE_FALLING,
"my_key_home", (void *)"my_key_home");
if(ret<0){
printk("Request IRQ %s failed, %d\n","key_home" , ret);
goto exit;
}
return 0;
exit:
return ret;
}
static void __exit keys_input_exit(void)
{
printk("%s(%d)\n", __FUNCTION__, __LINE__);
free_irq(gpio_to_irq(MY_KEY_HOME),(void *)"my_key_home");
//释放申请的 input_dev 事件以及释放输入子系统
input_unregister_device(buttons_dev);
input_free_device(buttons_dev);
}
module_init(keys_input_init);
module_exit(keys_input_exit);
测试结果
使用hexdump测试
加载模块后/dev/input/下多出了event2节点。
或者在/proc/bus/input/devices
文件中也可以找到。
执行hexdump /dev/input/event2
后,每按下一次home键都会有下面4条打印。
分别对应4条语句:
1
2
3
4
input_report_key(buttons_dev, KEY_L, 1);//最后一个参数: 非0 - 按下
input_sync(buttons_dev);//通知上层,本次事件结束
input_report_key(buttons_dev, KEY_L, 0);//最后一个参数: 为0 - 松开
input_sync(buttons_dev);//通知上层,本次事件结束
从打印的第一行第6列看起:0001 0026 0001,分别是按键事件、键盘L的键值(16进制:0026,10进制:38)、按下(value:1)。
所有的事件类型见下表:
1
2
3
4
5
6
7
8
9
10
11
12
13
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 事件类型最大个数和提供位掩码支持
部分键值的宏见下图:
使用用户空间程序测试
test.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
31
32
33
34
35
// 使用方法 : ./this_app /dev/input/eventX // X 为 0 1 2 3
// 注意: 在监听前上报的数据并不会被监听到
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/input.h>
int main(int argc, const char *argv[]){
int fd;
struct input_event env;
int c = 0;
int i;
fd =open(argv[1],0666);
if(fd<=0){
puts("open error");
return -1;
}
while(1){
c=read(fd,&env,sizeof(struct input_event));
if(c<0){
perror("read error");
return -1;
}
printf("---------------------------event begin\n");
printf("type:%d code:%d value:%d\n",
env.type,
env.code,
env.value);
printf("---------------------------event end\n");
}
return 0;
}
加载模块后执行./this_app /dev/input/event2
,每按下一次home键会打印如下4组信息:
额外的内容
input设备主设备号是13。
一般驱动程序是device和driver的匹配,而input子系统是device和handler的id匹配。
- 利用 /proc/bus/input/handlers 查看 注册的 input handlers
- 利用 /proc/bus/input/devices 查看 注册的 input devices
input子系统的handler中的id类似下面的数组:
而device中id就像示例中结构体赋值一样设置。