input子系统的搭建要点:
核心层为事件驱动层和设备驱动层的注册提供API的实现、核心层为设备驱动层上报事件提供API的实现 、事件驱动层为应用层提供API的实现 。int input_register_handler(struct input_handler *handler);②为设备驱动层提供的:
void input_unregister_handler(struct input_handler *handler);
struct input_dev *input_allocate_device(void);设备驱动要用到的上报输入事件函数API:
void input_free_device(struct input_dev *dev);
int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);//下面是这个函数的特殊定义##上面这些接口函数的实现过程几乎就是input子系统的框架搭建过程:##
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
input_register_handler(struct input_handler *handler)
{
......
// ①放入数组:根据次设备号就可以查找到,方便应用层快速调用
input_table[handler->minor >> 5] = handler;//把传进来的handler结构体根据他自身的minor次设备号放到input_table[]数组对应的位置
// ②链入链表:方便遍历
list_add_tail(&handler->node, &input_handler_list);
// ③在input_dev_list链表中主动找匹配的input_dev:
list_for_each_entry(dev, &input_dev_list, node) //for循环的宏定义
input_attach_handler(dev, handler);
id = input_match_device(handler->id_table, dev); // 根据input_handler的id_table判断能否支持这个input_dev
error = handler->connect(handler, dev, id); //match成功的话调用
......
}
完全可以想象只要有调用input_register_handler()就会根据minor来填充input_table[]对应的项,这点必须值得肯定!
input_register_device(struct input_dev *dev)
{
......
// 链入链表
list_add_tail(&dev->node, &input_dev_list);
// 在input_handler_list链表中主动找匹配的input_handler:
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
id = input_match_device(handler->id_table, dev);// 根据input_handler的id_table判断能否支持这个input_dev
error = handler->connect(handler, dev, id); //如果能支持,则调用input_handler的connect函数建立"连接"
......
}
也在内核源码里边来搜一搜input_register_device函数在哪些地方被调用:
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
......
//1. 分配一个input_handle结构体
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
//2.设置这个结构体
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); // 指向input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; //指向input_handler
evdev->handle.private = evdev;
//3.把这个结构体注册到input_handler和input_dev结构体
error = input_register_handle(&evdev->handle);//下面进一步分析这个函数就知道究竟是怎样让handler和dev建立联系的!
......
}
input_register_handle函数实现:
input_register_handle();
{
......
list_add_tail_rcu(&handle->d_node, &dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
......
}
小结一下这一点的内容:
input_handle.dev = input_dev; // 指向input_dev3. 注册:
input_handle.handler = input_handler; // 指向input_handler
input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
input_handle_event(dev, type, code, value);
switch (type) {
case EV_SYN:
...
case EV_KEY:
...
case EV_SW:
...
case EV_ABS:
...
case EV_REL:
...
}
...
//分析到这里要小心了,一开始以为是这个if条件成立,因为第一感觉就是input_device结构体的这个成员会在哪里被初始化,
//然后就去直奔input_register_device函数里找啊找,半毛钱都没找到,原来本来就没有进行定义,所以dev->event为空,条件不成立!
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS)//擦!从sourceInsi也可以发现INPUT_PASS_TO_HANDLERS这个宏是有定义的
input_pass_event(dev, type, code, value);//这个就是设备驱动层上报事件的关键工作!
}
input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
...
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,type, code, value);//最终调用的是匹配的input_handler结构体的event成员
//这就好办了,handler是事件驱动层,我们就拿event.c文件里的例子来继续分析
}
void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
{
......
wake_up_interruptible(&evdev->wait);//唤醒工作
}
小结:
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 file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read, //以这个接口函数的实现进行分析
......
};
evdev_read()
{
.......
// 无数据并且是非阻塞方式打开,则立刻返回
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// 否则进入休眠,上面刚刚提到:这里的休眠就是设备驱动层通过调用input_event()函数最终重定向到事件驱动层的evdev_event()函数来唤醒的
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
.......
}
小结:
事件驱动层和核心层都是通用的,我们需要实现的是设备驱动层。输入设备驱动会把硬件产生的事件信息用统一的格式(struct input_event)上报给核心层,然后核心层进行分类后,再上报给相应的事件处理驱动程序,最后通过事件层传递到用户空间,应用程序可以通过设备节点来获取事件信息。比如底层报上来的是一个按键事件,核心层会报给evdev来处理;如果报的是一个鼠标事件,核心层会报给mousedev来处理。最后附上一张经典的输入子系统的框架图。