对输入子系统分析总结
2012-08-26 21:55
218 查看
这两天学习了看了韦东山的第二期视频关于输入子系统部分,做了相关记录,分享给大家 在drivers/input/input.c中: 进入模块入口函数input_init
:
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
复制代码
而input_fops只有open和llseek函数:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
复制代码
那么没有read函数的话该怎么读数据呢? 进入 input_open_file函数:
struct input_handler *handler;//定义一个input_handler指针
handler = input_table[iminor(inode) >> 5];//根据传入文件的次设备号从
//input_table中提取出一个input_handler
if (handler)
new_fops = fops_get(handler->fops);//从input_handler中提取出new_fops
file->f_op = new_fops;//将new_fops赋值给当前文件的file_operations
err = new_fops->open(inode, file);
复制代码
经过这些操作后,当app再来调用read,write,open等函数时,最终都会调用到file->f_op 中的read,write,open等函数。 那么input_open_file函数中的input_table 又是从何而来的呢? 搜索代码可知, 在input.c的input_register_handler函数中构造了input_table:
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
//给input_table中的 第handler->minor >> 5项赋值
input_table[handler->minor >> 5] = handler;
}
复制代码
又是谁在调用input_register_handler呢? 搜索内核可知: evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件调用了 input_register_handler,我们以evdev.c为例 在drivers/input/evdev.c中: 进入模块入口函数evdev_init:
return input_register_handler(&evdev_handler);
evdev_handler的定义如下:
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,//前面所用到的 new_fops指的就是这里定义的的fops
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
复制代码
这里需要插入的讲解一下输入子系统的架构!如图: 当通过input_register_device注册一个input_dev设备或者通过 input_register_handler注册一个input_handler时,input_dev与input_handler 会进行匹配,如何匹配? 在input_handler中有一个id_table,里面包含的就是这个input_handler能处 理的input_dev,当input_handler与input_dev匹配成功的话,则会调用 input_handler里 的connect函数。
下面我们来看下input_register_device和input_register_handler分别做了什么:
input_register_device:
//将刚注册的input_dev放入input_dev_list链表
list_add_tail(&dev->node, &input_dev_list);
/*对 input_handler_list中的每一个input_handler都调用
*input_attach_handler(dev, handler);这个函数,来查看是否有input_handler *合适该input_dev
*/
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_register_handler:
//将刚注册的input_handler放入input_table中
input_table[handler->minor >> 5] = handler;
//将刚注册的input_handler放入 input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);
/*对 input_dev_list 中的每一个input_dev都调用
*input_attach_handler(dev, handler);这个函数,来查看是否有input_dev合 *适该input_handler
*/
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
复制代码
由此可以看出,无论是先注册input_handler还是先注册input_dev最终 都 会调用来input_attach_handler(dev, handler);来进行两两匹配。现在我们来看看 input_attach_handler是如何将input_handler和input_dev进行匹配的。 在input_attach_handler函数中: /*先进行匹配,匹配的依据就是input_handler中的id_table与 *input_dev中的id里的信息是否相同
*/ id = input_match_device(handler, dev); /*再调用input_handler中的connect函数完成连接,具体如何连接, *需要分析connect函数 */ error = handler->connect(handler, dev, id); 我们以evdev.c中的 input_handler结构中的connect函数为例,分析connect 函数做了些什么。
在evdev_connect函数中:
/*分配一个evdev结构体,该结构体中含一个input_handle结构
*注意:不同于input_handler结构
*/
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
/*设置evdev结构中的input_handle
*@input_handle的dev变量指向input_dev结构
*@input_handle的handler变量指向input_handler结构
*/
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
/*向内核注册input_handle*/
error = input_register_handle(&evdev->handle);
复制代码
下面我们来看注册input_handle做了些什么:
/*将input_handle添加到input_dev中的h_list链表中
*以后当input_dev需要使用对应的input_handler时就可以通过自身的 *h_list链表找到input_handle,从而找到匹配的input_handler。
*/
list_add_tail_rcu(&handle->d_node, &dev->h_list);
/*将input_handle添加到input_dev中的h_list链表中,
*以后当input_handler需要使用对应的input_dev时就可以通过自身的
*h_list链表找到input_handle,从而找到匹配的input_dev
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
复制代码
对于输入设备,我们知道一般需要读取其数据,那么经过上述的一些列动作 后又是如何读取到数据的呢?我们来看下evdev.c中的input_handler中的fops 中的read函数: 在evdev_read函数中: //如果没有数据且nonblock的话,则EAGAIN
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);
复制代码
既然有睡眠,那么何时被唤醒,搜索代码。 在evdev_event函数中: //唤醒 wake_up_interruptible(&evdev->wait); evdev_event是input_handler中的成员,当有数据可读(如触摸屏被按下,按键 被按下)时,event函数会被调用。而event函数是怎么被调用到的?这就得看设 备层了,设备层的驱动做了如下工作: <1>在中断函数中确定判断发生了什么事件,并且相应的值是多少
<2>通过input_event()函数上报事件 <3>通过input_sync()函数表明上报结束 分析input_event函数我们就可以知道input_handler中的event函数是如何被调用到的了。 在input_event中,调用了input_handle_event函数,在input_handle_event函数 中调用了input_pass_event函数; 在input_pass_event函数中:
struct input_handler *handler;
/*从注册的input_dev的h_list中将input_handle一个个拿出来*/
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
//如果该input_handle被打开,则该input_handle->input_handler即为可 //处理该input_dev的handler
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
/*最终调用到event函数*/
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
复制代码
最后,回归到如何写符合输入子系统框架的驱动程序,四个步骤: <1>分配一个input_dev结构体 <2>设置input_dev <3> 注册input_dev <4>在中断函数中上报事件 输入子系统记录完毕 估计看到这的人已经疯了
:
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
复制代码
而input_fops只有open和llseek函数:
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
复制代码
那么没有read函数的话该怎么读数据呢? 进入 input_open_file函数:
struct input_handler *handler;//定义一个input_handler指针
handler = input_table[iminor(inode) >> 5];//根据传入文件的次设备号从
//input_table中提取出一个input_handler
if (handler)
new_fops = fops_get(handler->fops);//从input_handler中提取出new_fops
file->f_op = new_fops;//将new_fops赋值给当前文件的file_operations
err = new_fops->open(inode, file);
复制代码
经过这些操作后,当app再来调用read,write,open等函数时,最终都会调用到file->f_op 中的read,write,open等函数。 那么input_open_file函数中的input_table 又是从何而来的呢? 搜索代码可知, 在input.c的input_register_handler函数中构造了input_table:
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
//给input_table中的 第handler->minor >> 5项赋值
input_table[handler->minor >> 5] = handler;
}
复制代码
又是谁在调用input_register_handler呢? 搜索内核可知: evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件调用了 input_register_handler,我们以evdev.c为例 在drivers/input/evdev.c中: 进入模块入口函数evdev_init:
return input_register_handler(&evdev_handler);
evdev_handler的定义如下:
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,//前面所用到的 new_fops指的就是这里定义的的fops
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
复制代码
这里需要插入的讲解一下输入子系统的架构!如图: 当通过input_register_device注册一个input_dev设备或者通过 input_register_handler注册一个input_handler时,input_dev与input_handler 会进行匹配,如何匹配? 在input_handler中有一个id_table,里面包含的就是这个input_handler能处 理的input_dev,当input_handler与input_dev匹配成功的话,则会调用 input_handler里 的connect函数。
下面我们来看下input_register_device和input_register_handler分别做了什么:
input_register_device:
//将刚注册的input_dev放入input_dev_list链表
list_add_tail(&dev->node, &input_dev_list);
/*对 input_handler_list中的每一个input_handler都调用
*input_attach_handler(dev, handler);这个函数,来查看是否有input_handler *合适该input_dev
*/
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_register_handler:
//将刚注册的input_handler放入input_table中
input_table[handler->minor >> 5] = handler;
//将刚注册的input_handler放入 input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);
/*对 input_dev_list 中的每一个input_dev都调用
*input_attach_handler(dev, handler);这个函数,来查看是否有input_dev合 *适该input_handler
*/
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
复制代码
由此可以看出,无论是先注册input_handler还是先注册input_dev最终 都 会调用来input_attach_handler(dev, handler);来进行两两匹配。现在我们来看看 input_attach_handler是如何将input_handler和input_dev进行匹配的。 在input_attach_handler函数中: /*先进行匹配,匹配的依据就是input_handler中的id_table与 *input_dev中的id里的信息是否相同
*/ id = input_match_device(handler, dev); /*再调用input_handler中的connect函数完成连接,具体如何连接, *需要分析connect函数 */ error = handler->connect(handler, dev, id); 我们以evdev.c中的 input_handler结构中的connect函数为例,分析connect 函数做了些什么。
在evdev_connect函数中:
/*分配一个evdev结构体,该结构体中含一个input_handle结构
*注意:不同于input_handler结构
*/
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
/*设置evdev结构中的input_handle
*@input_handle的dev变量指向input_dev结构
*@input_handle的handler变量指向input_handler结构
*/
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
/*向内核注册input_handle*/
error = input_register_handle(&evdev->handle);
复制代码
下面我们来看注册input_handle做了些什么:
/*将input_handle添加到input_dev中的h_list链表中
*以后当input_dev需要使用对应的input_handler时就可以通过自身的 *h_list链表找到input_handle,从而找到匹配的input_handler。
*/
list_add_tail_rcu(&handle->d_node, &dev->h_list);
/*将input_handle添加到input_dev中的h_list链表中,
*以后当input_handler需要使用对应的input_dev时就可以通过自身的
*h_list链表找到input_handle,从而找到匹配的input_dev
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
复制代码
对于输入设备,我们知道一般需要读取其数据,那么经过上述的一些列动作 后又是如何读取到数据的呢?我们来看下evdev.c中的input_handler中的fops 中的read函数: 在evdev_read函数中: //如果没有数据且nonblock的话,则EAGAIN
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);
复制代码
既然有睡眠,那么何时被唤醒,搜索代码。 在evdev_event函数中: //唤醒 wake_up_interruptible(&evdev->wait); evdev_event是input_handler中的成员,当有数据可读(如触摸屏被按下,按键 被按下)时,event函数会被调用。而event函数是怎么被调用到的?这就得看设 备层了,设备层的驱动做了如下工作: <1>在中断函数中确定判断发生了什么事件,并且相应的值是多少
<2>通过input_event()函数上报事件 <3>通过input_sync()函数表明上报结束 分析input_event函数我们就可以知道input_handler中的event函数是如何被调用到的了。 在input_event中,调用了input_handle_event函数,在input_handle_event函数 中调用了input_pass_event函数; 在input_pass_event函数中:
struct input_handler *handler;
/*从注册的input_dev的h_list中将input_handle一个个拿出来*/
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
//如果该input_handle被打开,则该input_handle->input_handler即为可 //处理该input_dev的handler
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
/*最终调用到event函数*/
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
复制代码
最后,回归到如何写符合输入子系统框架的驱动程序,四个步骤: <1>分配一个input_dev结构体 <2>设置input_dev <3> 注册input_dev <4>在中断函数中上报事件 输入子系统记录完毕 估计看到这的人已经疯了
相关文章推荐
- 输入服务子系统实例分析(韦东山的视频总结及针对linux-2.6.30.4)
- 对输入子系统分析总结
- 对输入子系统分析总结
- 【分享】对输入子系统分析总结
- 输入服务子系统框架代码分析(韦东山的视频总结及针对linux-2.6.30.4)
- linux input输入子系统分析《三》:S3C2440的触摸屏驱动实例
- input输入子系统分析
- i.mx android6 输入子系统分析(未完)
- Linux input子系统分析 输入子系统核心分析
- 输入子系统--event层分析
- linux 输入子系统(2)----简单实例分析系统结构(input_dev层)
- Linux输入子系统简要分析
- 输入子系统架构分析
- linux输入子系统驱动工作原理分析
- **输入子系统架构分析
- linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(2)
- linux input输入子系统分析《一》:初识input输入子系统
- Input输入子系统个人总结
- 2.6内核输入子系统分析
- 输入子系统--event层分析【转】