程序员人生 网站导航

input子系统――架构、驱动、应用程序

栏目:综合技术时间:2015-01-17 09:41:02

1、input子系统架构

input子系统由驱动层drivers,输入子系统核心层input core,事件处理层event handler组成。

1个输入事件,通过输入装备发给系统如鼠标移动,键盘按键按下等通过device driver->input core(handler->event函数)->event handler->user space的顺序到达用户空间传给利用程序。

1个输失事件,通过系统发给输入装备,通过user space->event handler->input core(dev->event函数)->device driver

1、驱动功能层:负责和底层的硬件装备打交道,将底层硬件装备对用户输入的响应转换为标准的输入事件以后再向上发送给输入系统核心层

2、Input系统核心层:由driver/input/input.c及相干头文件实现,他对下提供了装备驱动层的接口,对上提供了事件处理层的变成接口。

3、事件处理层将硬件装备上报的事件分发到用户空间和内核。


结构图以下:



2、编写input驱动需要的函数

1)包括头文件<linux/input.h>,他是input子系统的接口,提供了必要的定义消息

2)Input_allocate_device()

分配了1个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)

1旦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)

告知事件的接收者,到此为止为1次完全的消息。比如我们在touch screen上取得了xy的值,要使作为1次事件,那末将input_sync加在report xy值得后面。

6)其他的事件type,输失事件处理

其他的事件有:

EV_LED:用作键盘的LED

EV_SND:用作键盘的蜂鸣器

他和键盘事件很相似,只不过键盘事件是INPUT_PASS_TO_DEVICE,而输失事件是INPUT_PASS_TO_HANDLERS,从系统到输入装备的驱动程序,如果你的驱动程序要处理这些事件,必须设置evbit中相应位,而且要实现1个回调函数。

struct input_dev *button_dev;

button_dev->event = button_event;这个便是处理输失事件的回调函数



3、普通按键实现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 ", 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 ", __func__); return -ENODEV; } button_dev = input_allocate_device(); if (!button_dev) { printk(KERN_ERR "button.c: Not enough memory "); 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 "); input_free_device(button_dev); free_irq(IRQ_EINT8, NULL); return -ENODEV; } printk(KERN_INFO "button init ok! "); 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! "); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Realsil Luckywang");


Makefile以下:

obj-m = button.o KERNELDIR ?=/home/lei/linux⑵.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 256keybit这个数组的第几个数组(第8个)

BIT_MASK(BTN_0)代表bit 256keybit这个数组的第几个数组里面的值(第8个数组的bit0)

2)事件类型type――编码code――值value

evbit是事件数组,evbit这个事件数组里面可以放很多事件类型,比如keyabs

事件key里面又有很多具体编码BTN_0BTN_TOUCH

事件abs里面也有很多具体编码ABS_XABS_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)事件处理进程



4、例子对应的利用程序

/* 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 "); exit(1); } ioctl(fd, EVIOCGVERSION, &version); printf("evdev driver version is 0x%x: %d.%d.%d ", 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! "); exit(1); } if (ev.type == EV_KEY) printf("type %d,code %d, value %d ", ev.type, ev.code, ev.value); } return 0; }

以上只是1个简单的利用程序测试。当你按下K1的时候,串口终端会有显示的input dev上报的按键的消息。

编写利用程序的时候如何肯定是哪一个eventX呢,cat /proc/bus/input/devices,输出打印消息以下:


这里插入了鼠标,通过比较VID、PID来找到对应的usb mouse装备,然后找到对应的mouse0、event1


另外还有很多里面ioctl调用的内容没有实验,具体可以参考这篇博客http://www.cnblogs.com/leaven/archive/2011/02/12/1952793.html,对ioctl的每一个case和read调用都试1遍,找到自己的体会。


------分隔线----------------------------
------分隔线----------------------------

最新技术推荐