Input Subsystem 系统主要由 drivers/input/input.c input_register_device() 和 input_register_handler() 两个函数实现。
Handler 组件通过 input_register_handler 函数将逻辑设备操作结构体注册到 input_handler_list 链表中;
device 组件通过 input_register_device 组件将直接对具体硬件的操作结构体注册到 input_dev_list 链表中
struct input_dev {
...
struct device dev;
...
struct list_head h_list; //与 input_dev关联的 input_headle 的链表头
struct list_head node; //用于将本设备连接到 input_dev_list
};
struct input_handler {
...
const struct file_operations *fops;
...
const struct input_device_id *id_table;//用户和设备匹配, 这个是事件处理器所支持的input设备
...
struct list_head h_list; //所支持的 input_headle 结构的链表的表头
struct list_head node; //链入全局链表input_handler_list, 它连接了所有注册到内核的handler
};
struct input_handle {
...
struct input_dev *dev; //关联的input_dev结构
struct input_handler *handler; //关联的input_headler结构
struct list_head d_node; //input_headle通过d_node连接到了input_dev的h_list上
struct list_head h_node; //input_headle通过h_node连接到了input_headler的h_list上
};
input_dev / input_headler / input_headle 关系:
input_dev 是硬件驱动层,代表一个input设备
input_headler 是事件处理层,代表一个事件处理器
input_headle 个人认为属于核心层, 代表一个配对的设备与事件处理器
那么input_dev 和input_handler是如何跟input子系统总线联系起来的呢?
(linux)/drivers/input/input.c
input_init()
class_register(&input_class); //创建类
register_chrdev(INPUT_MAJOR, "input", &input_fops); //创建类下的设备, 并由此可知 file_operations结构体变量为input_fops
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
//该结构体只填充了 .open 方法, read / write / poll 等方法没有填充, 那么肯定是由open做了些"工作"来间接实现了其他的功能, 那么就让我们来看看 input_open_file的内部
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
...
//以次设备号为下标,拿到一个 input_handler, 在input_handler内部有个file_operations成员
handler = input_table[iminor(inode) >> 5];
...
//将input_handler的file_operations成员拿出
new_fops = fops_get(handler->fops);
...
//将原来的fops保存起来, 将新的fops给 file->f_op
old_fops = file->f_op;
file->f_op = new_fops;
//从此file->f_op都是 新的fops
err = new_fops->open(inode, file);
...
return err;
}
以上程序的关键是 input_table[], 那么input_table是怎么来的呢?
insmod时候调用
//注册input_handler
input_register_handler()
input_table[handler->minor >> 5] = handler; //那么是谁调用了input_register_handler 了呢?
//将handler加入 input_handler_list链表
list_add_tail(&handler->node, &input_handler_list);
//每个 input_handler 都调用 input_attach_handler, 遍历headler 查看是否支持dev
list_for_each_entry(handler, &input_handler_list, node)
//根据input_handler 的id_table 判断能否支持该input_device
input_attach_handler(dev, handler);
插入设备调用
//注册输入设备
input_register_device()
//将每个 input_device 都放入链表
list_add_tail(&dev->node, &input_dev_list);
//每个 input_handler 都调用input_attach_handler, 遍历handler, 查看能否支持dev
list_for_each_entry(handler, &input_handler_list, node)
//根据input_handler 的id_table 判断能够支持该input_device
input_attach_handler(dev, handler);
以上两个函数非常对称, 不管先调用哪个函数都最后调用了 input_attach_handler, 那么input_attach_handler 做了什么工作呢?
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
id = input_match_device(handler->id_table, dev);//遍历id_table, 看找有没有与dev匹配的
if (!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
它先比较了handler层的 input_handler的id_table和device层的input_dev, 如果handler能够支持input_dev 则调用handler的connect将device和handler"连接"起来
//那么他们是怎么建立"连接"的呢? 主要看 handler 里的 connect 函数 (这里以evdev为例来说明)
evdev_connect
1. 申请一个 handle 结构体
2. 设置
handle.dev = input_dev;
handle.handler = input_handler;
3. input_dev->h_list = &handle;
input_hanlder->h_list = &handle;
这样一来
device层可以通过 input_dev的h_list先找到handle 然后再通过handle的handler找到 input_handler
handler层可以通过input_handler的h_list先找到handle然后再通过handle的dev找到 input_dev
两者就靠 handle连接了起来
从编写代码的过程再捋一遍思绪:
1. 申请input_dev结构体 input_allocate_device
2. 设置 input_dev
1) 可以产生哪类事件
2) 产生该类事件中的哪些事件
3. 将input_dev注册到内核
4. 实现业务逻辑代码
申请中断等...
问题:
- input_table 是什么?
- 应用层如何获取input_event上报的事件?