第2章输入子系统的事件驱动
上一章已经说过输入子系统分为三层,最上面的一层是事件处理层,我们暂时称它为事件驱动,这是相对于上一章的设备驱动来讲的。
上一章介绍了设备驱动注册时要与匹配的 handler 连接,报告的事件也会分发给连接的 handler ,这一章介绍 handler 的相关操作。
2.1 重要的数据结构
首先介绍 input_handle ,这个结构体用来连接input_dev 和 input_handler。它的代码如 程序清单2 .1 所示。
程序清单2 .1 input_handle
/* include/linux/input.h */
struct input_handle {
void *private; ⑴
int open; ⑵
const char *name; ⑶
struct input_dev *dev; ⑷
struct input_handler *handler; ⑸
struct list_head d_node; ⑹
struct list_head h_node; ⑺
};
各个成员的含义如下:
⑴ 私有数据指针。
⑵ 记录本设备被打开的次数。
⑶ 创建此 handle 的 handler 所赋予的名字。
⑷ 指向附着的 input_dev。
⑸ 指向创建此 handle 的 handler 。
⑹ 链表节点,用来加入附着的 input_dev。
⑺ 链表节点,用来加入附着的 input_handler。
在 程序清单1.11 中,我们看到input_dev 和 input_handler匹配过程中用到了 input_device_id 。它的代码如程序清单 2.2 所示。
程序清单2 .2 input_device_id
/* include/linux/mod_devicetable.h */
struct input_device_id {
kernel_ulong_t flags; ⑴
__u16 bustype; ⑵
__u16 vendor;
__u16 product;
__u16 version;
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; ⑷
};
各个成员的含义如下:
⑴ 定义需要匹配 input_id的哪些域(使用方法参考 程序清单1.11 )。
⑵ 对应 input_id 的四个数据域。
⑶ 存储支持事件的位图,与 input_dev中的同名数据成员功能一致。
⑷ 指示结构体中是否含有驱动信息。
input_handler 这个结构体是事件驱动的主体,每一种处理方式对应一个handler 结构体。它的定义如 程序清单2.3 所示。
程序清单2 .3 input_handler
/* include/linux/input.h */
struct input_handler {
void *private; ⑴
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); ⑵
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); ⑶
void (*disconnect)(struct input_handle *handle); ⑷
void (*start)(struct input_handle *handle); ⑸
const struct file_operations *fops; ⑹
int minor; ⑺
const char *name; ⑻
const struct input_device_id *id_table; ⑼
const struct input_device_id *blacklist; ⑽
struct list_head h_list; ⑾
struct list_head node; ⑿
};
每个成员的具体解释如下:
⑴ 私有数据指针。
⑵ 事件处理函数指针。设备驱动报告的事件最终由这个函数来处理 ( 参考 程序清单1.14 ) 。
⑶ 连接 handler 和 input_dev 的函数指针。
⑷ 断开连接函数指针。
⑸ 为给定的 handle 启动 handler 函数指针。
⑹ 文件操作结构体。
⑺ 这个 handler 可以使用的 32 个次设备号的最小值。
⑻ 此 handler 的名字。
⑼ 可以处理的 input_device_ids列表(用法参考 程序清单1.11 )。
⑽ 需要被忽略的 input_device_ids列表。
⑾ 用来连接 handle 的链表链表节点。每个与此 handler 相关的 handle 都放入此链表。
⑿ 用来放入全局 handler链表的节点。
2.2 input_handler 的注册
首先介绍存放注册的 input_handler 所用的数据结构。如所 程序清单2.4 示。
程序清单2 .4 input core 全局数据
/* driver/input/input.c */
static LIST_HEAD(input_dev_list); /* 设备链表头 */
static LIST_HEAD(input_handler_list); /* handler 链表头 */
static DEFINE_MUTEX(input_mutex); /* 保护以上两个链表 */
static struct input_handler *input_table[8]; /* 存放注册的 input_handler 的指针数组*/
程序清单1.9 中⑷ 处注册的 input_dev 结构体加入到上面的input_dev_list 当中,下面将要介绍的注册 input_handler ,其实就是将 input_hangler 加入到 input_handler_list 当中。 input_table 中存放进行文件操作的 handler ,使用它们次设备号的最高三比特在 input_table 中寻址,因此每个handler 最多支持 32 个设备节点。由上面的代码可以看出输入子系统最多允许 8 个进行文件操作的input_handler 同时存在。
input_register_handler 的代码如 程序清单 2.5所示。
程序清单2 .5 input_register_handler
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]) { ⑶
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler; ⑷
}
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();
out:
mutex_unlock(&input_mutex);
return retval;
}
EXPORT_SYMBOL(input_register_handler);
程序含义如下:
⑴ 初始化 handler 中的链表节点,为加入 input_handler_list 做准备。
⑵ 如果此 handler 需要进行文件操作。
⑶ 如果相应的次设备号段被占用。
⑷ 将 handler 注册进 input_table 。
⑸ 将 handler 加入 input_handler_list 链表。
⑹ 遍历 input_dev_list链表中的每一个 input_dev 结构体。
⑺ 将 handler 与每一个 input_dev 进行匹配。
input_attach_handler 的代码如 程序清单 1 .10所示,这里不再赘述。
总结一下 input_handler 的注册过程:见自己加入input_handler_list 和 input_table,然后与 input_dev_list 中的 input_dev 比较并与匹配的建立连接。
参考 1.4 input_dev的注册 这一节,我们发现 input_dev 和 input_handler 都是将自己加入各自的链表,然后再和对方链表中匹配的进行连接。