一、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_dev_list,input_handler_list 以上部分摘自:http://blog.csdn.net/zhangxizhicn/article/details/6642062
二、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)
将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驱动例子
- /*
- drivers->input core->event handler
- function: this file is button driver
- date: 20150101
- author: lei_wang
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/irq.h>
- #include <asm/irq.h>
- #include <asm/io.h>
- #include <mach/regs-gpio.h>
- #include <mach/hardware.h>
- #include <linux/interrupt.h>
- #include <linux/gpio.h>
- #include <linux/input.h>
- static struct input_dev *button_dev;
- static irqreturn_t button_intr(int irq, void *dev_id)
- {
- int val;
- val = s3c2410_gpio_getpin(S3C2410_GPG(0));
- // printk(KERN_INFO "key value is %d\n", val);
- input_report_key(button_dev, BTN_0, val);
- input_sync(button_dev);
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- static int __init button_init(void)
- {
- int ret;
- ret = request_irq(IRQ_EINT8, button_intr, IRQ_TYPE_EDGE_BOTH, "button0", NULL);
- if (ret) {
- printk(KERN_ERR "%s request failed\n", __func__);
- return -ENODEV;
- }
- button_dev = input_allocate_device();
- if (!button_dev) {
- printk(KERN_ERR "button.c: Not enough memory\n");
- free_irq(IRQ_EINT8, NULL);
- return -ENOMEM;
- }
- button_dev->name = "button0";
- button_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY);
- button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
- ret = input_register_device(button_dev);
- if (ret) {
- printk(KERN_ERR "button.c: Failed to register device\n");
- input_free_device(button_dev);
- free_irq(IRQ_EINT8, NULL);
- return -ENODEV;
- }
- printk(KERN_INFO "button init ok!\n");
- return 0;
- }
- static void __exit button_exit(void)
- {
- input_unregister_device(button_dev);
- input_free_device(button_dev);
- free_irq(IRQ_EINT8, NULL);
- printk(KERN_INFO "button exit ok!\n");
- }
- module_init(button_init);
- module_exit(button_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Realsil Luckywang");
Makefile如下:
- obj-m = button.o
- KERNELDIR ?=/home/lei/linux-2.6.32.2
- modules:
- $(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules
- clean:
- rm -rf *.o *.mod.c *.order *.symvers
Include/linux/bitops.h中定义了
#define BIT(nr) (1UL << (nr))
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BTN_0 0x100
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
说明:
1)上面的0x100表示BTN_0这个bit在所有的bit中是0x100(bit 256)位,那么
BIT_WORD(BTN_0)代表bit 256在keybit这个数组的第几个数组(第8个)
BIT_MASK(BTN_0)代表bit 256在keybit这个数组的第几个数组里面的值(第8个数组的bit0)
2)事件类型type——编码code——值value
evbit是事件数组,evbit这个事件数组里面可以放很多事件类型,比如key、abs等
事件key里面又有很多具体编码BTN_0、BTN_TOUCH等
事件abs里面也有很多具体编码ABS_X、ABS_Y等
不同编码有不同的值
另外http://blog.csdn.net/ylyuanlu/article/details/6704744 这篇博客对以下说的挺详细的
1)input子系统的struct input_dev、struct handler的注册
2)struct input_dev与struct input_handler怎么互相匹配(类似于device和driver匹配)
3)事件处理过程
五、例子对应的应用程序
- /*
- 20150101
- just a simple input test code
- lei_wang
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
- #include <linux/input.h>
- int main()
- {
- int fd;
- int version;
- int ret;
- struct input_event ev;
- fd = open("/dev/input/event1", O_RDONLY);
- if (fd < 0) {
- printf("open file failed\n");
- exit(1);
- }
- ioctl(fd, EVIOCGVERSION, &version);
- printf("evdev driver version is 0x%x: %d.%d.%d\n",
- version, version>>16, (version>>8) & 0xff, version & 0xff);
- while (1) {
- ret = read(fd, &ev, sizeof(struct input_event));
- if (ret < 0) {
- printf("read event error!\n");
- exit(1);
- }
- if (ev.type == EV_KEY)
- printf("type %d,code %d, value %d\n", ev.type, ev.code, ev.value);
- }
- return 0;
- }
以上只是一个简单的应用程序测试。当你按下K1的时候,串口终端会有显示的input dev上报的按键的消息。
另外,编写应用程序的时候如何确定是哪个eventX呢,cat /proc/bus/input/devices,输出打印消息如下:
这里插入了鼠标,通过比较VID、PID来找到对应的usb mouse设备,然后找到对应的mouse0、event1
其实也可以不写应用程序,直接通过cat /dev/input/mouse0 | hexdump来获取鼠标的数据。
另外还有很多里面ioctl调用的内容没有实验,具体可以参考这篇博客http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html,对ioctl的每个case以及read调用都试一遍,找到自己的体会。