首页 > 代码库 > input子系统——架构、驱动、应用程序

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

一、input子系统架构

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

一个输入事件,通过输入设备发给系统如鼠标移动,键盘按键按下等通过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上获得了xy的值,要使作为一次事件,那么将input_sync加在report xy值得后面。

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 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)事件处理过程



四、例子对应的应用程序

/*
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上报的按键的消息。

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


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