S3C2440 输入子系统概念介绍(十三)

https://www.cnblogs.com/lifexy/p/7542989.html


输入子系统概念介绍


    在以前的按键程序上,有个缺点,没办法用在被人的现成的应用程序上,比如QT等应用程序。别的应用程序不会打开/dev/buttons,有可能打开现成的设备/dev/tty。也有可能直接调用scanf(),就可以获取按键的输入。

    以前写的应用程序,只有你知道怎么用,但是其他人不知道怎么用。


写出一个通用的驱动程序,让已经现成的应用程序无缝的移植到你的单板上。(无缝就是不需要修改别人的应用程序)

使用现成的驱动。在内核里面现成的驱动程序,把自己需要的东西融合进去。(现成的驱动程序就是输入input子系统)


以前的字符设备驱动程序步骤:

1、确定主设备号major;

2、构造一个file_operations结构体(open函数、read函数、write函数);

3、告诉内核,register_chrdev,注册字符设备驱动程序; 

4、入口函数调用注册函数; 

5、有入口函数,必然有出口函数;


一、输入(input)子系统框架:(现成的框架,系统已做好,现成的框架也有上面的几个步骤)

打开核心层input.c(中转作用),位于drivers\input目录下

subsys_initcall(input_init);
module_exit(input_exit);

入口函数input_init,出口函数input_exit。


二、显然输入子系统是作为一个模块存在,我们先来分析一下input_init()入口函数

static int __init input_init(void)
{
	int err;

	err = class_register(&input_class);
	if (err) {
		printk(KERN_ERR "input: unable to register input_dev class\n");
		return err;
	}

	err = input_proc_init();
	if (err)
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	if (err) {
		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}

(1)上面第15行,register_chrdev(INPUT_MAJOR, "input", &input_fops);,以前注册设备需要自己写的,现在内核代码里面有了。注册了主设备号为13。看一下input_fops(file_operantions)结构体:


为什么只有一个open函数,不是要读按键吗?

显然open函数做了某些工作。


三、然后进入input_open_file函数里面:

static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler = input_table[iminor(inode) >> 5];    //(1)
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	if (!handler || !(new_fops = fops_get(handler->fops)))    //(2)
		return -ENODEV;

	if (!new_fops->open) {
		fops_put(new_fops);
		return -ENODEV;
	}
	old_fops = file->f_op;
	file->f_op = new_fops;    //(3)

	err = new_fops->open(inode, file);    //(4)

	if (err) {
		fops_put(file->f_op);
		file->f_op = fops_get(old_fops);
	}
	fops_put(old_fops);
	return err;
}

static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,
};

(1)第三行中, iminor(inode)调用了MINOR(inode->i_rdev)来获取次设备号,将次设备号处理32。input_handler输入处理器(处理句柄),handler=input_table。input_table数组,根据次设备号所打开的文件赋值给handler。

(2)第七行中,input_handler有个file_operations结构体,fops_get那个file_operations结构体。

(3)第十五行中,现在所打开的文件f_op 等于 新的f_op(file_operantions结构体)。

(4)第十七行中,调用新的f_op的open函数。

以后应用程序app读read时,最终会调用到file->f_op->read。

input_table数组由谁来构造?


四、input_register_handler会构造input_table数组:

int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5])
			return -EBUSY;

		input_table[handler->minor >> 5] = handler;    //(1)
	}

	list_add_tail(&handler->node, &input_handler_list);

	list_for_each_entry(dev, &input_dev_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();
	return 0;
}


五、搜索一下input_register_handler函数会被谁调用:

我们可以从下图可以知道,evdev.c(事件设备),joydev.c(游戏句柄设备),keyboard.c(键盘设备),mousedev.c(鼠标设备),tsdev.c(触摸屏设备)。


以evdev.c为例子,它在evdev_exit()注册:

static void __exit evdev_exit(void)
{
	input_unregister_handler(&evdev_handler);    //注册
}


六、再来看一下edev_handler结构体:(纯软件)

static struct input_handler evdev_handler = {
	.event =	evdev_event,
	.connect =	evdev_connect,
	.disconnect =	evdev_disconnect,
	.fops =		&evdev_fops,
	.minor =	EVDEV_MINOR_BASE,
	.name =		"evdev",
	.id_table =	evdev_ids,
};

(1)第五中.fops:file_oprations结构体。

(2)第六行中.minor:其中,EVDEV_MINOR_BASE=64,次设备是64。然后调用input_register_handler注册,相当于EVDEV_MINOR_BASE/32=2,所以放在数组的第二项EVDEV_MINOR_BASE[2]。

所以open时,就会调用input_open_file,执行evdev_handler->edev_fops->.open

(3)第八行中.id_table:表示handler能够支持哪一些输入设备。hadler和devices进行比较,handler处理器(软件)能否支持device(设备),如果能够支持设备,则调用.connect函数。

(4)第三行中.connect:连接函数,将input_device和input_handler建立连接。


七、再来看一下input_register_device函数:

int input_register_device(struct input_dev *dev)
{
... ...
	list_add_tail(&dev->node, &input_dev_list);    //(1)放入链表
... ...
	list_for_each_entry(handler, &input_handler_list, node)    //(2)
	    input_attach_handler(dev, handler);    //(2)
... ...
	return 0;
}

(1)第4行中,将input_devid结构体,放入input_dev_list链表里面。

(2)第6行中,对于每一个input_handler,都调用input_attach_handler,input_attach_handler根据input_handler的id_table判断能否支持那个input_dev(输入设备)。


八、然后再回过头看input_handler的input_register_handler()函数:

int input_register_handler(struct input_handler *handler)
{
... ...
	input_table[handler->minor >> 5] = handler;    //(1)
... ...
	list_add_tail(&handler->node, &input_handler_list);    //(2)

	list_for_each_entry(dev, &input_dev_list, node)    //(3)
		input_attach_handler(dev, handler);
... ...
	return 0;
}

(1)第4行中,首先将input_device放入一个数组里面。

(2)第6行中,再讲input_device放到一个链表input_handler_list里面。

(3)第8行中,对于每一个input_device,都调用input_attach_handler,input_attach_handler根据input_handler的id_table判断能否支持那个input_dev(输入设备)。

所以,不管是先添加input_handler还是input_dev,调用input_attach_handler(),判断两者是否匹配、支持。


九、我们来看一下input_attach_handler()函数:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
... ...
	id = input_match_device(handler->id_table, dev);    //根据handler的id_table和输入设备dev进行比较
	if (!id)    //匹配失败
		return -ENODEV;

	error = handler->connect(handler, dev, id);    //如果匹配调用handler的connect函数
... ...
	return error;
}

根据handler的id_table和输入设备dev进行比较,是否匹配。如果匹配,调用hadler里面的connect函数建立连接。

注册input_dev或者input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立“连接”。


十、举个列子(evdev.c事件驱动):evdev_handler->connect函数

10.1 来分析怎么样建立连接:



10.2 evdev_handler的.connect()函数是evdev_connect():

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
... ...
	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
	if (minor == EVDEV_MINORS) {
		printk(KERN_ERR "evdev: no more free evdev devices\n");
		return -ENFILE;
	}
... ...
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //(1)分配一个input_handle结构体(没有r)
... ...
	evdev->handle.dev = dev;    //(2)设置input_handle,指向左边的input_dev结构体
	evdev->handle.name = evdev->name;
	evdev->handle.handler = handler;    //(2)指向右边的input_handler结构体
	evdev->handle.private = evdev;
	sprintf(evdev->name, "event%d", minor);
... ...
	devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
	cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
... ...
	error = input_register_handle(&evdev->handle);    //(3)注册handle
... ...
}

(1)第10行中,分配一个input_handle结构体(没有r)。

(2)第12行中,设置input_handle,dev指向左边的input_dev结构体,.handler指向右边的input_handler结构体。

(3)第21行中,注册handle。


10.3 最终进入input_register_handle()函数注册handle

int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;

	list_add_tail(&handle->d_node, &handle->dev->h_list);    //(1)
	list_add_tail(&handle->h_node, &handler->h_list);    //(2)

	if (handler->start)
		handler->start(handle);

	return 0;
}

(1)第五行中,把传进来的handle放入一个输入设备dev的链表h_list里面。

(2)第六行中,把handle放入右边handler的链表h_list里面。

连接的时候构造一个input_handle,里面.dev指向input_device,里面.handler指向input_handler。

input_device的.h_list指向input_handle,input_handler的h_.list指向input_handle。

可以从input_device输入设备,通过.h_list找到input_handle,从里面的.handler找到右边的handler处理者。

也可从input_handler,通过.h_list找到input_handle,从里面的.dev找到左边的能支持的设备dev。

总结一下:

1、分配一个input_handle结构体

2、input_handle.dev = input_dev    //指向左边的input_dev

     input_handle.handler = input_handler    //指向右边的 input_handler    

3、注册:

    input_handler->h_list = &input_handle;

    input_dev->h_list = &input_handle;


十一、怎么读取按键evdev_read(时间驱动)?

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
... ...
	if (count < evdev_event_size())
		return -EINVAL;

	if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))//无数据,并且是非阻塞方式打开,则立刻返回
		return -EAGAIN;

	retval = wait_event_interruptible(evdev->wait,    //否则休眠
		client->head != client->tail || !evdev->exist);
... ...
}


十二、read函数进入休眠,被谁唤醒呢?evdev_event事件处理函数

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
... ...
	wake_up_interruptible(&evdev->wait);//事件发生,唤醒
}


十三、分下一下,evdev_event被谁调用?gpio_keys_isr(在drivers\input\keyboard\gpio_keys.c文件)

在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数。

static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
... ...
    input_event(input, type, button->code, !!state);    ///上报事件
    input_sync(input);
... ...
}


通过input_event调用.event事件函数:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
	struct input_handle *handle;

	list_for_each_entry(handle, &dev->h_list, d_node)//对链表的每一项handle
		if (handle->open)//如果handle已经打开
			handle->handler->event(handle, type, code, value);
}



猜你喜欢

转载自blog.csdn.net/xiaodingqq/article/details/80389000
今日推荐