linux内核的input子系统是对分散的,多种不同类别的输入设备(如键盘,鼠标,跟踪球,操纵杆,触摸屏,加速计和手写板)等字符设备进行统一处理的一层抽象,就是在字符设备驱动上抽象出的一层。input子系统包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。鼠标事件生成文件mousedev属于事件驱动程序,而PS/2鼠标驱动程序是设备驱动程序。事件驱动程序是标准的,对所有的输入类都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序。设备驱动程序可以利用一个已经存在的,合适的事件驱动程序通过输入核心和用户应用程序接口。
linux系统为input系统做了很好的封装,将所有input系统接口都提供出来了,做为像tp这样的输入设备,其实使用input系统很简单,定义一个输入设备结构体,struct input_dev*input_dev,然后利用linux提供的api接口自动为该结构体申请内存空间等,input_dev = input_allocate_device(),申请成功了就往input_dev结构体里面填充一些东西,如input设备的name,所支持的输入设备等等,最后就是通过input_register_device(input_dev)注册input_dev设备,然后在向相对于的结点上报芯片所读到的数据给android上层调用。在所有input外围模块驱动中对input系统的使用就是这样几步。但如果要好好的运用输入系统,就得着重分析里面的流程。
先来看一下输入子系统体系架构图:
Input子系统分为三层,从下至上分别是输入设备驱动层,输入核心层以及输入事件驱动层。一个大致的工作流程就是,input device向上层报告-->input core接收报告,并根据在注册input device时建立好的连接选择哪一类handler来处理事件-->通过handler将数据存放在相应的dev(evdev,mousedev…)实例的缓冲区中,等待应用程序来读取。
首先,先来看看几个结构体的定义:
1、input_dev
<span style="font-size:14px;"><strong> </strong>struct input_dev {
const char *name;
const char *phys; //设备在系统中的物理路径
const char *uniq; //设备唯一识别符
struct input_idid; //用于匹配事件处理层handler
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsignedlong evbit[BITS_TO_LONGS(EV_CNT)]; //用于记录支持的事件类型的位图
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned longabsbit[BITS_TO_LONGS(ABS_CNT)]; //记录支持的绝对坐标的位图
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
unsigned inthint_events_per_packet;
unsigned int keycodemax; //keycode表的大小
unsigned int keycodesize; //keycode表中元素个数
void *keycode; //设备的键盘表
int (*setkeycode)(structinput_dev *dev,
const struct input_keymap_entry *ke,
unsigned int *old_keycode); //配置keycode表
int (*getkeycode)(structinput_dev *dev,
struct input_keymap_entry *ke); //获取keycode表
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int rep[REP_CNT];
struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;
struct input_absinfo *absinfo;
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned longsnd[BITS_TO_LONGS(SND_CNT)];
unsigned longsw[BITS_TO_LONGS(SW_CNT)];
//操作接口
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev*dev);
int (*flush)(struct input_dev*dev, struct file *file);
int (*event)(struct input_dev*dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu*grab; //当前使用的handle
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
bool sync;
struct device dev;
structlist_head h_list; // handle链表
structlist_head node; //input_dev链表
};</span>
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];表示对input设备支持的事件,在TP驱动程序中会有对input设备支持的函数设置,如input->evbit[0] =BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);表示支持绝对事件跟按键事件。类似的还有以下这些事件:EV_KEY -按键, EV_REL -相对坐标,EV_ABS -绝对坐标,EV_LED - LED,EV_FF- 力反馈。
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];设置相应的位以支持某一类绝对值坐标,在驱动程序中会用input_set_abs_params(input,ABS_MT_POSITION_X, 0, max_x+2, 0, 0);这个函数进行设置,如上,该函数会在最后调用dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);对相对应的位进行设置。
struct list_head h_list;表示的是和该设备相关的所有input handle的结构体链表。struct list_head node;所有input设备组成的链表结构(就是后面对应的input_dev_list结构体)。
2、input_handler
<span style="font-size:14px;">struct input_handler {
void *private;
void(*event)(struct input_handle *handle, unsigned int type, unsigned int code, intvalue);
bool(*filter)(struct input_handle *handle, unsigned int type, unsigned int code,int value);
bool(*match)(struct input_handler *handler, struct input_dev *dev);
int(*connect)(struct input_handler *handler, struct input_dev *dev, const structinput_device_id *id);
void(*disconnect)(struct input_handle *handle);
void(*start)(struct input_handle *handle);
const structfile_operations *fops;
int minor;
const char*name;
const structinput_device_id *id_table;
structlist_head h_list;
structlist_head node;
};</span>
顾名思义:用来处理input设备的一个结构体。struct list_head h_list表示的是和该设备相关的所有input handle的结构体链表和input_dev那个一样;struct list_head node所有input_handle组成的结构体连链表(后面将会知道它对应于input_handler_list)。每一个input设备在注册时,他都会遍历input_handler_list链表上的每一个input_handler,去找寻input_dev与之相对应的input_handler,同理每一个input_handler在注册时,他也会去input_dev_list上找寻与之相对应的input_dev。有时候事情往往不会那么尽如人意,当input_handler还产生时,你这个input_dev就一直在那等吧。注意这里的input_handler和input_dev并不是一一对应的关系,有时一个input_handler对应好几个input_dev。
3、input_handle
<span style="font-size:14px;">struct input_handle {
void *private;
int open;
const char*name;
structinput_dev *dev;
structinput_handler *handler;
struct list_head d_node;
structlist_head h_node;
};</span>
input_Handle其实也好理解,它就是input_dev和 input_handler粘合剂,通过Input_Handle这么一搅和,input_dev就和 input_handler发生了点关系,至于什么样的关系我们后文将会知道的一清二楚。struct input_dev *dev对应的input设备。struct input_handler *handler对应该设备的handler。struct list_head d_node和struct list_headh_node则分别关联上前面两个结构体中的struct list_head h_list。
如下图代表了input_dev,input_handler,input_handle,3者之间的关系。一类handler可以和多个硬件设备相关联,一个硬件设备可以和多个handler相关联。例如:一个触摸屏设备可以作为一个event设备,作为一个鼠标设备,也可以作为一个触摸设备,所以一个设备需要与多个平台驱动进行连接。而一个平台驱动也不只为一个设备服务,一个触摸平台驱动可能要为A,B,C3个触摸设备提供上层驱动,所以需要这样一对多的连接。
上面介绍完了输入系统中三个重要的结构体后,下面做的如何申请并注册一个input设备。当在模块驱动中定义好了一个input_dev的结构体中,接着要做的是为这个结构体分配内存空间以及做一些初始化操作。Input系统中利用input_allocate_device来完成这一步,i2c.input_dev = input_allocate_device()。
<span style="font-size:14px;">struct input_dev *input_allocate_device(void)
{
struct input_dev *dev; // 申明一个input_dev结构体变量
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 在内核空间开辟一段大小为sizeof(struct input_dev)大小的内存区,并把它初始化为0
if (dev) { // 判断内存是否分配成功
dev->dev.type = &input_dev_type; // 设备属性设置 创建sys系统的一些属性
dev->dev.class = &input_class; //设备类设置
device_initialize(&dev->dev); // 初始化device 结构体
mutex_init(&dev->mutex); // 初始化该dev的互斥量和锁,为防止对dev的并发访问
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list); //对input设备中的两个链表结构头进行初始化,注意都是双向链表
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}</span><span style="font-size:18px;">
</span>
input_allocate_device主要是申请一个input_dev结构体,并为之分配内核空间,同时还进行一些初始化动作,对创建sys目录进行初始化。在申请好了一个input_dev结构体后,在模块驱动程序中会对结构体里面的一些变量进行填充,例如:input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS), input->keybit[BIT_WORD(BTN_TOUCH)]= BIT_MASK(BTN_TOUCH),input->name = ILITEK_I2C_DRIVER_NAME; input->id.bustype= BUS_I2C;input->dev.parent = &(i2c.client)->dev等等。
程序中的dev->dev.type =&input_dev_type表示为设置一些input设备的属性,下面看看这个结构体怎样赋值的。
<span style="font-size:14px;">static struct device_type input_dev_type = {
.groups = input_dev_attr_groups, // 如下
.release = input_dev_release,
.uevent = input_dev_uevent,
#ifdef CONFIG_PM
.pm = &input_dev_pm_ops,
#endif
};
static const struct attribute_group *input_dev_attr_groups[] = {
&input_dev_attr_group, // 如下
&input_dev_id_attr_group,
&input_dev_caps_attr_group,
NULL
};
static struct attribute_group input_dev_attr_group = {
.attrs = input_dev_attrs, // 如下
};
static struct attribute *input_dev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_phys.attr,
&dev_attr_uniq.attr,
&dev_attr_modalias.attr,
&dev_attr_properties.attr,
NULL
};
static ssize_t input_dev_show_properties(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct input_dev *input_dev = to_input_dev(dev);
int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit,
INPUT_PROP_MAX, true);
return min_t(int, len, PAGE_SIZE);
}</span><span style="font-size:18px;">
</span>
从上面可以看出,这主要是在sys目录下创建一些属性文件,这些属性文件在当我们adb进行系统后,在/sys/class/input/input1里面会有一些属性文件,就是在这里显示的,如下图所示:
而对于dev->dev.class = &input_class,则表示建立相对于的class文件属性,input_class是一个全局的结构体,
<span style="font-size:14px;">struct class input_class = {
.name = "input",
.devnode = input_devnode,
};</span>
它会在input_init初始化函数中进行注册,err = class_register(&input_class),这样就会在sys下建立一个class的目录,改class只在input_init函数中注册一次,所以,所以input事件都会在class目录下。
当自动申请了一个input_dev结构体后,模块驱动函数就会对input_dev结构体中的某些函数进行填充,在具体的TP的驱动中,会进行这样的调用来实现填充。
<span style="font-size:14px;">i2c.input_dev = input_allocate_device();//input_dev结构体申请
if(i2c.input_dev == NULL){
printk(ILITEK_ERROR_LEVEL "%s, allocate input device, error\n", __func__);
return -1;
}
ilitek_set_input_param(i2c.input_dev, i2c.max_tp, i2c.max_x, i2c.max_y);//input_dev的填充
static void
ilitek_set_input_param(
struct input_dev *input,
int max_tp,
int max_x,
int max_y)
{
int key;
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); // 表示支持按键事件和绝对坐标事件
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);//表明按键事件
#ifndef ROTATE_FLAG
input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x+2, 0, 0);//上报绝对事件的一些参数
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y+2, 0, 0);
#else
input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_y+2, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_x+2, 0, 0);
#endif
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, max_tp, 0, 0);
for(key=0; key<sizeof(touch_key_code); key++){
if(touch_key_code[key] <= 0){
continue;
}
set_bit(touch_key_code[key] & KEY_MAX, input->keybit);
}
input->name = ILITEK_I2C_DRIVER_NAME; //input_dev结构的一些参数赋值
input->id.bustype = BUS_I2C;
input->dev.parent = &(i2c.client)->dev;
}</span><span style="font-size:18px;">
</span>
在这个函数中主要就是对input_dev进行的填充,包括name、bustype等等,同时也有将i2c通讯中的dev赋值给input_dev中的私有化结构体,实现i2c与input设备的关联。对input_dev进行填充完之后,就要对input设置进行注册,这也是整个input系统最重要的一步,尽管linux只提供了input_register_device这样一个简单的api接口函数进行注册,但里面却做了不少事。
<span style="font-size:14px;">ret = input_register_device(i2c.input_dev);
if(ret){
printk(ILITEK_ERROR_LEVEL "%s, register input device, error\n", __func__);
return ret;
}
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
/* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); // 让所有设备都支持EV_SYN同步事件
/*******下面为一些对按键进行处理********/
/* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit);
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
if (!dev->hint_events_per_packet)
dev->hint_events_per_packet =
input_estimate_events_per_packet(dev);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer); //初始化定时器
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { //处理重复按键
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; //指定位置的键值
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; //设置键值
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1); //设置input_dev中设备名
error = device_add(&dev->dev); // // 往sys中添加设备
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); // 打印出文件系统的路径
pr_info("%s as %s\n",
dev->name ? dev->name : "Unspecified device",
path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex); //在input_allocate_device函数里面有对input_mutex锁进行初始化
if (error) {
device_del(&dev->dev);
return error;
}
//将input_dev加入input_dev_list链表中
list_add_tail(&dev->node, &input_dev_list); // 添加dev->node到input_dev_list的链表中
list_for_each_entry(handler, &input_handler_list, node) // 匹配handler
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}</span><span style="font-size:18px;">
</span>
input_register_device函数就是对input系统进行注册,函数开头部分是对按键事件进行处理,由于TP上报的是绝对事件,所以,就不对按键事件进行分析,函数中会有error =device_add(&dev->dev);这与input_allocate_device函数中的 device_initialize(&dev->dev);相对应,device_initialize对设备属性进行初始化,这里两个函数就共同让我们的input设备中的内嵌dev结构在系统中注册,并在sysfs文件系统中形成相应的文件层次结构。
error = mutex_lock_interruptible(&input_mutex);和mutex_unlock(&input_mutex);两行代码就是为了防止其中间的临界区代码被并发访问。input_mutex的初始化时在input_allocate_device函数里面进行设置的。
list_add_tail(&dev->node, &input_dev_list); 把该设备添加到input_dev_list中 list_for_each_entry(handler, &input_handler_list,node) // 遍历input_handler_list链表,匹配handler。
input_attach_handler(dev, handler); 遍历系统中所有已注册的handler链表即input_handler_list。
Input系统中主要有两个双向链表,input_dev_list和input_handler_list,这里面主要是对dev设备跟handler进行挂载,当注册input_dev设备时,会到input_handler_list链表中去搜索匹配相对应的handler结构体,同理在input_handler结构体注册的时候,也会进行同样的动作进行搜索匹配。
对于input_register_device中的input_attach_handler函数主要完成将input_dev与handler尝试匹配,只有匹配成功才能进行下一步操作,我们看看input_attach_handler。不过在这之前,我们有必要先看看list_add_tail(&dev->node, &input_dev_list); 和list_for_each_entry(handler, &input_handler_list, node)这是怎样进行添加跟遍历循环的。
下面先来看看list_add_tail的实现,list_add_tail的实现在include/linux/list.h中,函数原型为:<span style="font-size:14px;">static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{//list_head是一个双向链表,所以插入元素时要头结list_add_tail点跟尾结点都要操作
next->prev = new; //先插表尾,在插表头
new->next = next;
new->prev = prev;
prev->next = new;
}</span><span style="font-size:18px;">
</span>
list_add_tail(&dev->node, &input_dev_list)传入的是dev->node和input_dev_list这两个链表,按照list_add_tail就会将dev->node插到input_dev_list链表的表头。然后在看看遍历函数list_for_each_entry:
<span style="font-size: 14px;">#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))</span>
<span style="font-size: 14px;"></span><pre name="code" class="cpp">#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#defineoffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER),TYPE是某struct的类型 0是一个假想TYPE类型struct, MEMBER是该struct中的一个成员。由于该struct的基地址为0, member的地址就是该成员相对与struct头地址的偏移量。关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型。const typeof( ((type*)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr。(type*)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针。该指针就是member的入口地址了。
所以container_of的作用为根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针,所以pos = list_entry((head)->next, typeof(*pos), member)的作用大致为根据已知(head)->next指向链表中的类型,member变量的类型的地址,从而得到pos结构体的地址,。所以对于list_for_each_entry(handler,&input_handler_list, node),可以转换为:<span style="font-size:14px;">#define list_for_each_entry(handler, &input_handler_list, node) \
for (handler = list_entry((&input_handler_list)->next, typeof(*handler), node); \
& handler-> node != (&input_handler_list); \
handler = list_entry(handler -> node.next, typeof(*handle), node))</span><span style="font-size:18px;">
</span>
大致意思是得到input_handler_list里面的首handler的地址,然后比较handler-> node是不是在input_handler_list链表中,然后handler取下一个地址,循环不断的查找,直到查找成功为止。每对handler进行了一次查找就利用input_attach_handler函数进行匹配dev跟handler;
<span style="font-size:14px;">static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
// 在模块驱动中有这样一个赋值input->id.bustype = BUS_I2C
id = input_match_device(handler, dev); //handler->id_table存储handler支持的设备id。如果能够找到匹配的id,则建立dev和handler之间的连接
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: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}</span><span style="font-size:18px;">
</span>
先来看看函数刚开始引进的新的数据结构input_device_id :
<span style="font-size:14px;">struct input_device_id {
kernel_ulong_t flags; //定义需要匹配input_id的哪些域
__u16 bustype; //对应input_id的四个数据域
__u16 vendor;
__u16 product;
__u16 version;
//存储支持事件的位图,与input_dev中的同名数据成员功能一致
kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
kernel_ulong_t driver_info; //指示结构体中是否含有驱动信息
};</span><span style="font-size:18px;">
</span>
定义了一个id后,会有一个匹配id的过程id =input_match_device(handler, dev);
<span style="font-size:14px;">static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id;
int i;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype) //总线是否匹配
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor) //设备厂商是否匹配
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product) //设备号是否匹配
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version) //版本号是否匹配
continue;
//只有id->flags没定义或者定义的类型匹配成功才会进入MATCH_BIT的匹配项
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
if (!handler->match || handler->match(handler, dev))
return id;
}
#define MATCH_BIT(bit, max) \
for (i = 0; i < BITS_TO_LONGS(max); i++) \
if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
break; \
if (i != BITS_TO_LONGS(max)) \
continue;</span><span style="font-size:18px;">
</span>
handler的id中evbit、keybit等等中的某一位设置了,input设备也得具备这个条件(之前在驱动程序中会有一个set_bit(EV_ABS, akm->input_dev->evbit);为我们这个input 设备制造了一个条件)。由此看到。在id->flags 中定义了要匹配的项。定义INPUT_DEVICE_ID_MATCH_BUS。则是要比较input device 和input handler 的总线类型。INPUT_DEVICE_ID_MATCH_VENDOR, INPUT_DEVICE_ID_MATCH_PRODUCT , INPUT_DEVICE_ID_MATCH_VERSION 分别要求设备厂商、设备号和设备版本。如果id->flags 定义的类型匹配成功。或者是id->flags 没有定义,就会进入到MATCH_BIT 的匹配项了.从MATCH_BIT宏的定义可以看出。只有当iput device 和input handler 的id 成员在evbit, keybit,… swbit 项相同才会匹配成功。而且匹配的顺序是从evbit, keybit 到swbit.只要有一项不同,就会循环到id 中的下一项进行比较.简而言之,注册input device 的过程就是为input device 设置默认值,并将其挂以input_dev_list。与挂载在input_handler_list中的handler 相匹配。如果匹配成功,就会执行error =handler->connect(handler, dev, id);调用handler 的connect 函数。Handler里面的connect函数是在哪里定义的了?下面在看看evdev.c文件中的这个结构体:
<span style="font-size:14px;">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,
};
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};</span><span style="font-size:18px;">
</span>
然后在evdev.c文件中的init函数里面会有对handler的注册:
<span style="font-size:14px;">static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}</span><span style="font-size:18px;">
</span>
evdev.c文件时在编译时直接编入内核的,所以这就解决了之前在什么时候注册handler这个东西的疑问,handler的注册不像input_dev一样在模块驱动里面都进行了注册。
input_handler中的connect函数是指向evdev_connect函数的。<span style="font-size:14px;">static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
1 for (minor = 0; minor < EVDEV_MINORS; minor++) // EVDEV_MINORS定义为32
2 if (!evdev_table[minor]) // 遍历evdev_table数组,为空就跳出 遍历evdev_table下一个空的位置
3 break;
if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
// dev_set_name(&evdev->dev, "event%d", minor); // 这里event%d应该就是在sys/class/input下的input0、input1等结点
char * tmp_name = dev_name(&dev->dev);
if(!strncmp(dev_name(&dev->dev), "input", 5)){
tmp_name = tmp_name + 5;
printk("skip input ,get number : %s , =====\n", tmp_name);
}
printk("input number = %d\n", wh_atoi(tmp_name));
printk("event s+ : name = %s\n", dev_name(&dev->dev));
dev_set_name(&evdev->dev, "event%d", wh_atoi(tmp_name));
/
evdev->exist = true;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); // 填充handler这个数据结构的
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); // 在/dev目录下创建设备节点
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = input_register_handle(&evdev->handle); // 注册handle,不是注册handler
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}</span><span style="font-size:18px;">
</span>
这里定义了一个新的结构体struct evdev *evdev;
<span style="font-size:14px;"> struct evdev {
int open;
int minor;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
bool exist;
};</span><span style="font-size:18px;">
</span>
函数中表明的1、2、3行表示在evdev_table去寻找是否还有空位置,为空就记住此时的位置并跳出循环。evdev_table是一个全局的evdev结构体,定义为static struct evdev *evdev_table[EVDEV_MINORS];他是专门用来装evdev结构体的。而函数是调用error = evdev_install_chrdev(evdev);将封装填充好的数据加入到evdev_table结构体中的。
static int evdev_install_chrdev(struct evdev *evdev)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table[evdev->minor] = evdev;
return 0;
}
接下来看看
evdev_connect
函数中注册
input_handle
的函数
input_register_handle
的实现:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
/*
* Filters go to the head of the list, normal handlers
* to the tail.
*/
if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list); //将handle加入dev->h_list中
else
list_add_tail_rcu(&handle->d_node, &dev->h_list); //将handle加入handler->h_list中
mutex_unlock(&dev->mutex);
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
这样就将handle分别注册到了dev和handler中的链表去了,然后在各自的注册中去寻找匹配链表。
在注册完了input设备后,模块驱动程序中会在每次读完寄存器的值后对input系统进行一次数值的上报,input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x),先看看input_report_abs函数:
<span style="font-size:14px;">static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value); // 为内核产生随机因子
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}</span><span style="font-size:18px;">
</span>
其中if (is_event_supported(type, dev->evbit, EV_MAX))判断我们的input设备中的evbit中是否设置了EV_ABS这个标识,以表示对绝对坐标的支持。在之前会有一个注册设置函数ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY)| BIT_MASK(EV_ABS),是否对事件的支持就是在这里设置的。spin_lock_irqsave(&dev->event_lock,flags);和spin_unlock_irqrestore(&dev->event_lock,flags);表示为自旋锁加锁和解锁,这里的自旋锁会对所有中断也进行屏蔽。
<span style="font-size:14px;">static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) {
if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}</span><span style="font-size:18px;">
</span>
对于TP,上报的是绝对事件,所以type为EV_ABS。if (is_event_supported(code, dev->absbit, ABS_MAX))判断我们的input设备中的absbit中是否设置了ABS_MAX, disposition = input_handle_abs_event(dev, code,&value);调用了input_handle_abs_event函数,该函数将会返回一个INPUT_PASS_TO_HANDLERS值,而在代码的最后,会通过disposition 的值进行不同的调用,所以会调用到input_pass_event中去。
<span style="font-size:18px;"> </span><span style="font-size:14px;">static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;
rcu_read_lock(); // RCU形式的锁机制 读-拷贝-更新
handle = rcu_dereference(dev->grab); // 判断有没有对grab进行设置,之前模块驱动代码里面没有对这个进行设置
if (handle)
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}</span><span style="font-size:18px;">
</span>
该部分代码采用RCU(读-拷贝-更新)的加锁方式进行同步操作。对于RCU保护的共享数据结构,读执行单元不需要获得任何锁就可以访问它。handle =rcu_dereference(dev->grab)看看有没有对dev->grab进行设置,整改驱动代码中都没有对该项进行设置,所以不会执行这几行代码。随后就是遍历dev->h_list链表上的handle,看看有没有handle->open不为0的,当open为0的时候,表示没有dev设备跟handler进行匹配,这里在我们在用户空间通过open函数打开某个evdev的时候,相应的handle->open会++,这里显然我们要打开evdev设备来对input设备进行操作。handler->event(handle,type, code, value);通过handle这个信息表来查找相对应的handler以及里面的evdev_event函数。
<span style="font-size:14px;">static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
event.type = type; // 传递进来的参数赋值给event结构体
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event, time_mono, time_real);
rcu_read_unlock();
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);
}</span><span style="font-size:18px;">
</span>
函数第二行定义了一个新的结构体struct evdev_client:
<span style="font-size:14px;">struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct wake_lock wake_lock;
bool use_wake_lock;
char name[28];
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
int clkid;
unsigned int bufsize;
struct input_event buffer[];
};</span><span style="font-size:18px;">
</span>
evdev_client结构体里面内嵌了一个struct evdev *evdev,并且struct input_eventbuffer[EVDEV_BUFFER_SIZE];定义为一个内核区buffer。第三行定义了一个struct input_event的变量event,下面看看input_event结构体的定义:
<span style="font-size:14px;">struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};</span><span style="font-size:18px;">
</span>
它的作用很明显,就是保存作为参数传进来的type、code、value等值。
list_for_each_entry_rcu(client,&evdev->client_list, node)
evdev_pass_event(client, &event, time_mono,time_real);
遍历client链表,找到与我们对应的那个client,然后把程序第三行定义了并赋值了的event放到里面去。放到哪里面去呢?放到struct input_eventbuffer[EVDEV_BUFFER_SIZE]里面去,注意最多可放EVDEV_BUFFER_SIZE(32)个event。然后在函数最后调用wake_up_interruptible(&evdev->wait);唤醒一个等待队列,这个队列是在read函数进行等待的,所以说,只要这里有数据传入了,然后就唤醒队列,给read函数进行读写,下面会read函数里面的唤醒部分进行分析。
<span style="font-size:14px;">static void evdev_pass_event(struct evdev_client *client,
struct input_event *event,
ktime_t mono, ktime_t real)
{
event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
mono : real);
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
client->buffer[client->tail].time = event->time;
client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
if (client->use_wake_lock)
wake_unlock(&client->wake_lock);
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
if (client->use_wake_lock)
wake_lock(&client->wake_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
spin_unlock(&client->buffer_lock);
}</span><span style="font-size:18px;">
</span>
这里client->buffer[client->head++] = *event就将event事件加入到了buffer中,随后我们的用户程序就可以通过一定的方式(read()系统调用)来从这个buffer中拷贝数据了。这样一次从最下层驱动通过input子系统传到内核区buffer,然后再到用户空间的数据传输就这样完成了。
上面其实只是为输入子系统的核心层向上为用户层提供接口函数向下为驱动程序提供统一的接口函数。这样就能够使输入设备的事件通过输入子系统发送给用户层应用程序,用户层应用程序也可以通过输入子系统通知驱动程序完成某项功能。输入子系统的核心层作为一个模块存在,必然有一个初始化函数,该函数为在input.c中的input_init。<span style="font-size:14px;">static int __init input_init(void)
{
int err;
err = class_register(&input_class);
if (err) {
pr_err("unable to register input_dev class\n");
return err;
}
err = input_proc_init();//在proc下建立相关交互文件
if (err)
goto fail1;
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册cdev设备号为13
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}</span><span style="font-size:18px;">
</span>
然后在看看input_fops这个结构体,
<span style="font-size:14px;">static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};</span><span style="font-size:18px;">
</span>
file_operations中的open函数重指向input_open_file这个函数,该函数将控制转到input_handler中定义的fopss文件指针的open函数,该函数在input_handler中实现,这样就使不同的handler处理器对应了不同的文件打开方式,为完成不同功能提供了方便。我们来看看input_open_file:
<span style="font-size:14px;">static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
err = mutex_lock_interruptible(&input_mutex);
if (err)
return err;
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5]; //由次设备号获取handler
if (handler) //获取handler的fops
new_fops = fops_get(handler->fops);
mutex_unlock(&input_mutex);
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops || !new_fops->open) { //handler的fops没有实现open,表示该设备不存在
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op; //保存老的fops
file->f_op = new_fops; //设置新的fops
err = new_fops->open(inode, file); //打开handler中fops下的open
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
return err;
}</span><span style="font-size:18px;">
</span>
这样就调用到了handler里面的fops结构体中open指向的evdev_open函数,
<span style="font-size:14px;">static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE; //得到evdev_table[]中的序号给i
unsigned int bufsize;
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
evdev = evdev_table[i]; //取出evdev_table[]中对应的evdev
if (evdev)
get_device(&evdev->dev); //增加引用计数
mutex_unlock(&evdev_table_mutex);
if (!evdev)
return -ENODEV;
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
client = kzalloc(sizeof(struct evdev_client) +
bufsize * sizeof(struct input_event),
GFP_KERNEL); //分配evdev_client
if (!client) {
error = -ENOMEM;
goto err_put_evdev;
}
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
snprintf(client->name, sizeof(client->name), "%s-%d",
dev_name(&evdev->dev), task_tgid_vnr(current));
client->evdev = evdev; //将client->evdev指向它所表示的evdev
evdev_attach_client(evdev, client); //将client挂在evdev->client_list上
error = evdev_open_device(evdev); //打开输入设备
if (error)
goto err_free_client;
file->private_data = client;
nonseekable_open(inode, file);
return 0;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}</span><span style="font-size:18px;">
</span>
在evdev_open主要是调用了evdev_open_device去打开一个设备,
<span style="font-size:14px;">static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex); //获得锁
if (retval)
return retval;
if (!evdev->exist)//检查设备是否已经存在
retval = -ENODEV;
else if (!evdev->open++) {//检查设备是否已经打开
retval = input_open_device(&evdev->handle); //没打开则调用该函数打开设备
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}</span><span style="font-size:18px;">
</span>
在判断设备是否打开的时候,当设备没有打开,则执行input_open_device,并且,每执行一次则对open变量进行自增,表示打开了一个设备。
<span style="font-size:14px;">int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->going_away) {
retval = -ENODEV;
goto out;
}
handle->open++; //递增handle的打开计数
if (!dev->users++ && dev->open) //如果第一次打开
retval = dev->open(dev); //调用input_dev下的open函数
if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex);
return retval;
}</span><span style="font-size:18px;">
</span>
在input_open_device中,我们最终调用了input_dev下的open函数,而该函数就是设备驱动层的open即模块驱动程序中的open函数。
最后,我们来看看事件驱动层的Evdev.c,先看下该模块下的初始化函数evdev_init:
<span style="font-size:14px;">static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}</span>
<span style="font-size:14px;">int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list); //初始化链表
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) { //是否该次设备号对应的input_table已满
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler; //将handler插入input_table
}
list_add_tail(&handler->node, &input_handler_list); //把handler挂到input_handler_list上
list_for_each_entry(dev, &input_dev_list, node) //遍历input_dev_list上的input_dev
input_attach_handler(dev, handler); //查看input_dev与handler是否匹配
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}</span><span style="font-size:18px;">
</span>
evdev_init中利用
input_register_handler
注册一个跟踪evdev_handler ,我们知道通过input_register_handler注册了evdev_handler,我们看看evdev_handler是什么
<span style="font-size:14px;">static struct input_handler evdev_handler = {
.event = evdev_event, //向输入子系统报告事件
.connect = evdev_connect, //input_dev与input_handler相连
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};</span><span style="font-size:18px;">
</span>
evdev_handler结构体之前已经见过了,里面的向输入子系统报告事件的函数evdev_even和将input_dev与input_handler相连的函数evdev_connect已经在前面分析过了,下面我们继续看看evdev_handler下的evdev_fops成员
<span style="font-size:14px;">static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read, //读
.write = evdev_write, //写
.poll = evdev_poll,
.open = evdev_open, //打开
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};</span><span style="font-size:18px;">
</span>
上面这个file_operations结构体中的open我们已经分析过了,现在我们看看上面结构体中的read吧
<span style="font-size:14px;">static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
if (count < input_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); //进入等待队列,等待有数据报告给系统
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
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();
}
return retval;
}</span><span style="font-size:18px;">
</span>
在这个read函数中有个等待队列,什么时候这个等待队列的条件才满足呢?我们知道在有事件产生时,然后驱动层的中断处理函数会向输入子系统报告,最终调用了handler下的evdev_event函数,在那个函数中唤醒了这个等待队列,上面也提到过唤醒read函数中的等待队列,唤醒的就是这里的read函数中的等待队列。