较好帖子:https://blog.csdn.net/luckywang1103/article/details/42324229
https://blog.csdn.net/zhangxizhicn/article/details/6642062
一、input子系统简介
1、Input驱动程序是linux输入设备的驱动程序,分成游戏杆(joystick)、鼠标(mouse和mice)、事件设备(event)。其中事件驱动程序是目前通用的驱动程序,可支持键盘、鼠标、触摸屏等多种输入设备。
2、input驱动程序的主设备号是13、次设备号的分布如下:
joystick游戏杆:0~16
mouse鼠标: 32~62
mice鼠标: 63
事件设备: 64~95
3、主要的结构体
Input_device:代表着具体的输入设备,他直接从硬件中读取数据,并以事件的形式转发
Handler:代表接收某一类事件的上层接口,对应于一类事件设备文件
Handle:用于将input_device和handler连接起来,对应于某个具体的设备文件。
Client:对应于用户程序对文件的访问接口,每open一次事件驱动,就创建一个client
Handler:struct input_handler *input_table[8],最多有8中input驱动,比如/dev/input/eventX和/dev/input/mouseX就是两种常用的input驱动。
Handle:以evdev.c为例,根据次设备号取值范围64-95,可以分别生成input/event0、input/event1,一直到input/event31共32个设备文件。每个设备文件对应一个handle
Client:每个设备文件又可以同时对应多个client,当有多个应用程序同时调用设备文件时,他们会从不同的client中取数据。
二、input子系统架构
input子系统由驱动层drivers,输入子系统核心层input core,事件处理层event handler组成。
驱动层并不创建文件节点,他只负责将采集到的数据通过input.c提供的函数input_event向上一层汇报。而各个事件驱动则分别将他们感兴趣的事件信息提取出来,通过文件节点传给用户空间。
一个输入事件,通过输入设备发给系统如鼠标移动,键盘按键按下等通过device driver->input core(handler->event函数)->event handler->user space的顺序到达用户空间传给应用程序。
一个输出事件,通过系统发给输入设备,通过user space->event handler->input core(dev->event函数)->device driver
1、驱动功能层:负责和底层的硬件设备打交道,将底层硬件设备对用户输入的响应转换为标准的输入事件以后再向上发送给输入系统核心层
2、Input系统核心层:由driver/input/input.c及相关头文件实现,他对下提供了设备驱动层的接口,对上提供了事件处理层的变成接口。
3、事件处理层将硬件设备上报的事件分发到用户空间和内核。
三、编写input驱动需要的函数
1)包含头文件<linux/input.h>,他是input子系统的接口,提供了必要的定义消息
2)Input_allocate_device()
分配了一个Input device的结构,设置他的bit field来告诉input子系统他能产生或者接收什么事件。
3)input_register_device(struct input_dev *dev)
具体设备是用input_dev 表示的,它通过函数input_register_device()注册。
input_register_device()主要做了两件事情:
1. 把 input_dev 存放到input 核心的链表中
2. 扫描input核心链表上的handler ,并试着匹配
将dev结构体添加到input_dev_list全局链表中去
通过input_attach_handler(struct input_dev *dev, struct input_handler *handler)来查找对应的handler,
input_attach_handler里面实际调用了input_match_device(const struct input_device_id *id,struct input_dev *dev)
一旦input_attach_handler找到了对应的handler,就执行handler->connect
4)input_report_key(struct input_dev *dev, unsigned int code, int value)//上报按键值
5)input_sync(struct input_dev *dev)
告诉事件的接收者,到此为止为一次完整的消息。比如我们在touch screen上获得了x、y的值,要使作为一次事件,那么将input_sync加在report x、y值得后面。
6)其他的事件type,输出事件处理
其他的事件有:
EV_LED:用作键盘的LED灯
EV_SND:用作键盘的蜂鸣器
他和键盘事件很相似,只不过键盘事件是INPUT_PASS_TO_DEVICE,而输出事件是INPUT_PASS_TO_HANDLERS,从系统到输入设备的驱动程序,如果你的驱动程序要处理这些事件,必须设置evbit中相应位,而且要实现一个回调函数。
struct input_dev *button_dev;
button_dev->event = button_event;这个便是处理输出事件的回调函数
过程
input_register_device()函数注册输入设备结构体。
input_register_device()函数是输入子系统核心(input core)提供的函数。
该函数将input_dev结构体注册到输入子系统核心中,input_dev结构体必须由前面讲的input_allocate_device()函数来分配。input_register_device()函数如果注册失败,必须调用input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用input_unregister_device()函数来注销输入设备结构体。
/****实现对手机左侧按键功能的改变,使其按下时,在输入界面输入相应值
-------------------------------------------程序分析--------------------------------------
--------------------------------------------key_input.c-------------------------
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/major.h>
4 #include <linux/cdev.h>
5 #include <linux/device.h>
6 #include <linux/types.h>
7 #include <linux/fs.h>
8 #include <asm/uaccess.h>
9 #include <linux/io.h>
10 #include <linux/gpio.h>
11 #include <linux/sched.h>
12 #include <linux/interrupt.h>
13 #include <linux/input.h>
14
15 #define TAG "input_log " //标志log
16 #define INT_GPIO 91 //手机左侧按键中断号
17 #define MY_KEY_CODE KEY_B //按键值 按下时在屏幕输入的值
18
19 static struct input_dev *kinput;
20 volatile unsigned long *tlmm_gpio_cfg;
21 volatile unsigned long *tlmm_in_out;
22
23 static int irq;
24
25 static irqreturn_t key_irq_thread(int irq, void *data)
26 {
27 int value;
28 printk(TAG" func:%s \n", __func__);
29 value = *tlmm_in_out;
30 value &= 0x1;
31 if (value ) {
32 input_report_key(kinput, MY_KEY_CODE, 0); ///上报按键值
33 input_sync(kinput); //告诉事件的接收者,到此为止为一次完整的消息
34 printk(TAG"key is release\n");
35 } else {
36 input_report_key(kinput, MY_KEY_CODE, 1);
37 input_sync(kinput);
38 printk(TAG"key is press\n");
39 }
40
41 return IRQ_HANDLED;
42 }
43
45 static int my_key_init(void)
46 {
47 int retval;
48
49 printk(TAG" FUNC:%s LINE:%d\n", __func__, __LINE__);
50 /*分配了一个Input device的结构,设置他的bit field来告诉input子系统他能产生或者接收什么事件。*/
51 kinput = input_allocate_device();
52 if (!kinput)
53 return -ENOMEM;
54 kinput->name = "keyt";
55 __set_bit(EV_KEY, kinput->evbit); //将一个指针指向的数据的第nr位置 这里nr=EV_KEY,
56 __set_bit(MY_KEY_CODE, kinput->keybit);
57 /*将kinput结构体添加到input_dev_list全局链表中去
58 retval = input_register_device(kinput);
59 if(retval)
60 goto input_register_error;
61
62 tlmm_gpio_cfg = (volatile unsigned long *)ioremap(0x105B000, 8); //
63 tlmm_in_out = tlmm_gpio_cfg + 1; //
64 *tlmm_gpio_cfg |= 0x3; //
65 /*
66 返回的中断编号可以传给request_threaded_irq()和free_irq();///把INT_GPIO的PIN值转换为相应的IRQ值,并赋值给变量gpioToIrq。 返回中断号
*/
67 irq = gpio_to_irq(INT_GPIO);
68 printk(TAG"%s irq is %d\n", __func__, irq);
69 retval = request_threaded_irq(irq, NULL, key_irq_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |IRQF_ONESHOT, "vol_k ey", NULL);
70 printk(TAG"%s ret is %d\n", __func__, retval);
72 return 0;
73
74 input_register_error:
75 input_free_device(kinput); //input_register_device()函数如果注册失败,必须调用input_free_device()函数释放分配的空间
76 return retval;
77 }
78
79 static void key_exit(void)
80 {
81 free_irq(irq, NULL); //释放中断号
82 input_unregister_device(kinput); //,在卸载函数中应该调用input_unregister_device()函数来注销输入设备结构体
83 printk(TAG" FUNC:%s LINE:%d\n", __func__, __LINE__);
84 }
85
86 module_init(my_key_init);
87 module_exit(key_exit);
88 MODULE_LICENSE("GPL");
二、不需要应用程序测试