Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150730
  • 博文数量: 49
  • 博客积分: 45
  • 博客等级: 民兵
  • 技术积分: 545
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-23 13:22
文章分类
文章存档

2017年(5)

2016年(18)

2015年(18)

2014年(8)

我的朋友

分类: 嵌入式

2014-02-23 13:36:58

Input 子系统重要数据结构梳理

梳理evdev_client  evdev  input_handle  input_dev  input_handler 这几个结构体的关系

将他们联系在一起的是数据流

1)evdev_client

当用户层调用类似代码 open("/dev/input/event1"O_RDONLY)函数打开设备结点时,会调用evdev_fops中的evdev_open()函数,在这个函数里面构造了一个evdev_client ,并建立client 与具体事件(evdev = evdev_table[i])的相互关系(client->evdev = evdev; evdev_attach_client(evdev, client);)。

evdev_open()函数里面通过file->private_data = client; 赋值给file private ,然后在evdev_read或者evdev_write的时候再通过,client = file->private_data; 拿出来用,这个I2C 子系统的设备模型也是这样子的,这种方法内核通用了。可见,好的代码大都一样,恶心的代码各不相同。

注意client 有个成员buffer ,它是上层和底层数据流的最终存储地方。client->buffer[]; 是用来存放数据的内存地址,buffer[]是个由input_event 结构组成的数组,这个指针是指向内核空间的地址。我再看下evdev_read函数的参数

evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)

它的buffer是一个指向用户(__user)空间的地址,内核用copy_to_user 和 copy_from_user 函数来实现内核地址空间与用户地址空间的数据交换。

2evdev

他在evdev_connect 函数中被构造,又因为这个函数是input_handler 结构的回调,那么他们的关系也就建立了,但不是一种直接的关系,他们是通过input_handle 结构间接联系的(evdev->handle.handler = handler;)。

evdev_table[] 是个evdev结构为元素的数组,这个数组里面放了每个设备(input_dev)对应的事件,设备与事件的对应是通过次设备号minor来标示的。上层请求一来,内核就会通过i = iminor(inode) - EVDEV_MINOR_BASE; 提取次设备号,然后evdev = evdev_table[i];确认是哪个具体事件。然后为该事件绑定一个client ,显然,这个就是在open函数里面做的。

3)input_handle

它是建立 input_dev  和 input_handler 的枢纽,它有一个重要成员open 掌控着整个数据流的打开与关闭。我们看看handle->open这个成员是怎么掌控数据流的开关的。

从上到下:

 open("/dev/input/event1"O_RDONLY) -》 evdev_open  -》 evdev_open_device -》  input_open_device  -》 handle->open++;

从下到上,以按键上报为例子:

input_report_key  -》 input_event -》  input_handle_event  -》  input_pass_event 

input_pass_event 这个函数里面有如下代码:

if (!handle->open) // 判断open 与否 ,没open直接返回,上报事件无效。

    continue;

handler = handle->handler; 

if (!handler->filter) {

if (filtered)

break;

handler->event(handle, type, code, value); // 调用对应handerevent 写数据

4)input_handler

通过上面最后一句的蓝色字我们可以看到,如果一切顺利,驱动上报的数据会input_handler 所对应的event 回调函数处理。

这个函数会把驱动报上来的 数据的 type code value 加上时间戳 打包到input_event 结构里面,然后遍历挂在具体事件(evdev)的client_list链表拿出对应client ,然后调用evdev_pass_event 函数通过client->buffer[client->head++] = *event; 把数据写到具体clientbuffer[]里面,这个buffer[]是个由input_event 结构组成的数组。

当我们上层开始read 的时候,比如read("/dev/input/event1"O_RDONLY)它就会调用到evdev_read (在此之前上层得先open ,在evdev_open 函数里面确认是哪一个event ,前面第2点有说),在这函数里面调用input_event_to_user 这个包装了内核提供的copy_to_user 函数,然后就把内核里面的input_event 的数据,拷贝到用户空间提供的buffer 指针指定的内存区域。

至此,数据流就畅通了。

input_dev

这个是我们最熟悉的结构了,在驱动里面,我们可以给设备设置名字,这个名字就是上层调用时的标识,我们还可以设置一下匹配条件,比如bustype 等用于跟input_handler结构的匹配,匹配函数在input_match_device。匹配流程如下:

 input_register_device -》    input_attach_handler -》   input_match_device


阅读(1681) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~