先看一篇网上摘的总结,写的很清晰:
http://blog.csdn.net/zhangxizhicn/article/details/6642062
Linux Input 设备驱动
1认识和使用 input 事件:
1.1Linux input 驱动分类
Input驱动程序是Linux输入设备的驱动程序,分成游戏杆(joystick)、鼠标(mouse和mice)和事件设备(Event queue)3种驱动程序。其中事件驱动程序是目前通用的驱动程序,可支持键盘、鼠标、触摸屏等多种输入设备。
Input驱动程序的主设备号是13,驱动程序的设备号分配如下所示。
joystick游戏杆:0~31
mouse鼠标:32~62
mice鼠标:63
事件(Event)设备:64~95
1.2查看 input 驱动
cat /proc/bus/input/devices
例如显示以下信息:
I: Bus=0017 Vendor=0001 Product=0001 Version=0100
N: Name="Macintosh mouse button emulation"
P: Phys=
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=mouse0 event0
B: EV=7
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=3:
则说明 /dev/input/mouse0 和 /dev/input/event0 都对应着同一个鼠标设备。
2.理解Input 子系统的模型:
2.1 input 子系统概述
Linux
input 子系统将一个输入设备的输入过程分成了设备驱动(input device driver)和事件驱动(input event
driver)两个层。前者负责从底层硬件采集数据;后者负责与用户程序接口,将采集到的数据分发给不同的用户接口。通过这样的设计,将千差万别的设备统
一到了为数不多的几种驱动接口上。同一种事件驱动可以用来处理多个同类设备;同一个设备也可以和多种事件驱动相衔接。
设
备驱动并不创建文件节点,它只负责将采集到数据通过input.c中的函数input_event()
向上一层汇报;而各个事件驱动则分别将他们各自感兴趣的事件信息提取出来,通过文件节点,提供给用户。在这个过程中,input
子系统的核心负责着这两层的交互工作。并管理和维护着记录了他们各自信息的链表。具体过程如下图所示:
更进一步理解Input 子系统,需要理解以下4个对象:
Input_device , handler, handle , client
Input_device: 代表着具体的输入设备,它直接从硬件中读取数据,并以事件的形式转发
Hanler: 代表接收某一类事件的上层接口,对应于一类事件设备文件
Handle : 用于将input_device 和 handler 连接起来的胶水,对应于某1个具体的设备文件。
Client: 对应于用户程序对文件的访问接口,每open一次事件驱动,就创建一个client.
Input 子系统维护者有两条重要的链表:
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
一
条记录着系统中所有的输入设备,另一条记录着所有的事件驱动。输入设备是通过input_dev 表示,而事件驱动是通过handler
表示。每当一个新的设备(如键盘,鼠标等)或者一个新的事件驱动被系统加载(调用input_register_device()或
input_register_driver()),都会扫描整个连标,并调用函数input_match_device(struct input_handler *handler, struct input_dev *dev) 尝试配对工作。Input_handler-->id_table 记录了需要匹配的特征。
比如eventX事件驱动它的匹配特征存在于
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
就
像代码中注释的一样,任何输入设备都可以和他匹配成功,也就是说任何输入设备都可以映射到/input/dev/eventX 下。
一旦匹配成功就会调用函数handler->connect(handler, dev,
id);将具体的设备(input_dev)和事件驱动(handler)邦定到一起,形成一个handle,将handle
注册到它相关联的input_dev 设备的链表h_list中。设备编号在input_init() 的时候通过调用创建。
2.2 文件节点的建立
Input 子系统在初始化时,首先调用register_chrdev(INPUT_MAJOR, "input", &input_fops); 为自己注册了256个字符设备节点。(这也代表着系统中最多可以存在256个输入设备)这256个设备会被分为8类,分别对应于数组input_table[8]中存放的8个 handler.
static struct input_handler *input_table[8]; // kernel/input/input.c
其中数第1个句柄管理此设备号0-31,第2个句柄管理设备号32-63。。。
每一个句柄,都可以用来实现一类事件驱动(但每类事件驱动最多只能管理32个设备)。
例
如:/dev/input/eventX所表示的设备 和dev /input/mou*** 所表示的设备
就分别使用了最常见的两种事件驱动。以/dev/input/eventX
为例,他们都使用同一个event事件驱动,从对象的角度看,拥有同一个handler。而这个handler
所管理的设备,拥有次设备号64-95,每一个次设备号又可以对应到一个handle.
2.3 事件驱动注册
事件驱动的方法被抽象到特定的 handler结构体中,驱动在初始化时并通过调用input_register_handler()把handler 注册的系统中。
input_register_handler()具体完成3件事情:
1. 把handler 存放到input_table[]中
2. 把 handler 存放到input 核心的链表中
3. 扫描input核心链表上的device ,并试着匹配
2.4 设备驱动
具体设备是用input_dev 表示的,它通过函数input_register_device()注册。
input_register_device()主要做了两件事情:
1. 把 input_dev 存放到input 核心的链表中
2. 扫描input核心链表上的handler ,并试着匹配
2.5 匹配
Dev 和handler 是通过input_match_device(struct input_handler *handler, struct input_dev
*dev) 尝试配的。每当查找到可以相互匹配的dev 和 handler 便回调handler->connect()
,生成一个handle结构体,并 通过handle 把某一个具体的dev 和另一个具体的handler被邦定到一起。最后handle
通过函数input _register_handle()注册到dev 的链表中。以后dev 上有数据要发送时,便可以在dev
中搜索这个链表,找到handle,再调用handle->handler->event() 把数据传到了event 驱动中。
2.6数据传送
当有数据上传时,数据通过input_pass_event () 函数向handle 发送, 这个函数的核心内容如下:
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
handler = handle->handler;
handler->event(handle, type, code, value);
}
根据input_dev 设备的h_list 链表查找与他相关的handle,然后调用event 处理方法,将数据传递给上层 的client。
2.7 clinet
每一个事件驱动(handler),可能对应多个文件节点(handle),而每一个文件节点又可以有多个client, 底层的数据直接复制到上层的client中。对文件句柄的操作则实际上是对这些个client 中的数据进行操作。
以驱动evdev.c 为例,根据次设备号取值范围64-95。可以分别生成/input/event0 ,input/event1,一直到/input/event31总共32个设备文件。
每
一个设备文件又可以同时对应多个client,当有多个应用程序同时调用设备文件时,他们会从不同的client 中取数据。每当应用程序调用open
时,便会调用evdev_open()创建一个client,并被与设备节点邦定 file->private_data = client;
设备被应用层当作文件操作时,通过struct evdev_client
*client = file->private_data; 再将client 找到。直接读/写 client
中的数据。而当底层有数据要传过来时则会通过handler->event() 把数据复制到client
中,如evdev_event()通过client 链便,对每一个client
进行evdev_pass_event()调用。而在evdev_pass_event()中实现数据向client 节点的复制。
下面是调用input_register_device后的一系列执行流程:
------------------------------------------------------------------------------------------
int input_register_device(struct input_dev *dev)
{
...
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
...
}
当我们调用input_register_device时,会把input设备加入到input_dev_list链表,然后循环
input_handler_list链表,找到匹配的handler。
我们看下如何才算匹配的handler:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler, dev);
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
pr_err("failed to attach handler %s to device %s, error: %dn",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
input_match_device可参见源码drivers/input/input.c。
struct input_handler结构体中有个id_table成员,主要匹配规则就是在此id_table中找出与传入的input_dev相同的
BUS类型、供应商信息、版本号、事件类型等。找到之后会调用handler->connect(handler, dev, id)。
如果你的input设备注册了自己的input_handler(drivers/input/mousedev.c),那么会调用你注册的connect;如果你
未注册自己的input_handler,那么会调用input子系统默认的connect函数,即evdev_connect(drivers/input/evdev.c此文件就是event驱动)。
evdev_connect主要工作就是初始化struct evdev *evdev,调用input_register_handle(&evdev->handle)。
int input_register_handle(struct input_handle *handle)
{
...
list_add_rcu(&handle->d_node, &dev->h_list);
...
list_add_tail_rcu(&handle->h_node, &handler->h_list);
...
}
看一个注释(摘自对struct input_handle结构体成员变量d_node、h_node的说明):
* @d_node: used to put the handle on device's list of attached handles
* @h_node: used to put the handle on handler's list of handles from which
* it gets events
从注释中我们知道上面的代码是把handle分别加入到dev->h_list、handler->h_list链表中。
------------------------------------------------------------------------------------------
我们再看调用input_sync后的执行流程:
------------------------------------------------------------------------------------------
input_sync
input_event
input_handle_event
input_pass_event
handler->event(handle, type, code, value)
前面铺垫工作全是为后边处理事件做准备。handler的event在drivers/input/evdev.c文件中,即evdev_event。
static void evdev_event(...)
{
...
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);
...
}
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
...
client->buffer[client->head++] = *event;
...
}
从上面的代码可以看出对event的处理,即把报上来的input事件保存到了client的buffer中。
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
从这个结构体可以看出input子系统把事件值封装成了struct input_event结构体。
那么这个client是什么?
------------------------------------------------------------------------------------------
接下来分析完input_open_file就知道了:
------------------------------------------------------------------------------------------
上层调用open打开input设备文件时,会调用到input_open_file。
static int input_open_file(struct inode *inode, struct file *file)
{
...
/* 由上面文摘的分析,input设备共有8类,每一类有32个设备,所以同类设备的次设备号的低5位是相同的 */
handler = input_table[iminor(inode) >> 5];
if (handler)
new_fops = fops_get(handler->fops);
...
err = new_fops->open(inode, file);
...
}
通过上面的代码我们看到,input_open_file调用了handler->fops的open函数。
也就是:
static struct input_handler evdev_handler = {
...
.fops = &evdev_fops,
...
};
static const struct file_operations evdev_fops = {
...
.open = evdev_open,
...
};
最终调用到了evdev_open。
static int evdev_open(struct inode *inode, struct file *file)
{
...
evdev = evdev_table[i];
...
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL);
...
evdev_attach_client(evdev, client);
error = evdev_open_device(evdev);
...
file->private_data = client; //注意这里把client存到了这里,在后面read时会用到
...
}
这里的client就是上面处理event事件即函数evdev_event中用到的client。
static void evdev_attach_client(struct evdev *evdev,
struct evdev_client *client)
{
...
list_add_tail_rcu(&client->node, &evdev->client_list);
...
}
evdev_attach_client把client加入到evdev的client_list链表中。
evdev_open_device-->input_open_device。
如果你的input设备定义了open接口,那么会调用它,如果未定义,input_open_device也并未做实质性的工作,只把计数器加1。
现在不知道打开一个设备最终要做的实质性工作是什么,也许什么都不用做?这和文件系统相关,以后再研究。open函数分析到此为止。
------------------------------------------------------------------------------------------
下面我们看下用户进程读取event是怎么执行的:
------------------------------------------------------------------------------------------
我们直接从evdev_read分析:
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
/* 用上面存在file结构体的client,每个不同的input设备打开时都会有自己的client,
* 然后保存到file结构体,以作不同设备间的区分。
*/
struct evdev_client *client = file->private_data;
...
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
...
}
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
...
*event = client->buffer[client->tail++];
...
}
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
return 0;
}
input_event_to_user 把数据拷贝到了用户空间。至此,evdev_read分析完。
------------------------------------------------------------------------------------------