input子系统本身的注册:
input_init(void)
class_register(&input_class); /* sysfs的class下出现input文件夹 */
input_proc_init(); /* 初始化input的proc文件系统 */
register_chrdev(INPUT_MAJOR, "input", &input_fops); /* 注册字符设备 */
input的dev注册的流程:
input = input_allocate_device(); /* 申请一个设备空间 */
...... /* 初始化input里面的数据 */
input_register_device(input); /* 注册该设备 */
...... /* 各种填充input里面的变量 */
device_add(&dev->dev); /* 经过此步骤后sysfs的class里面的input下面会多出来一个inputx,里面展示的name等就是前面填充和初始化的 */
list_add_tail(&dev->node, &input_dev_list); /* 增加该dev到核心层维护的dev链表中 */
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); /* 尝试匹配handler */
input_match_device(handler, dev) /* 匹配过程(算法) */
handler->connect(handler, dev, id); /* 匹配上了则连接两者到handle,并创建设备(假设匹配到了evdev) */
....... /* 分配次设备号,申请一个struct evdev,并初始化里面的链表,等待队列等等 */
input_register_handle(&evdev->handle); /* 绑定dev到handle,handler在初始化这里以及绑定了 */
evdev_install_chrdev(evdev); /* 把该evdev添加到evdev维护的数组中 */
device_add(&evdev->dev); /* 在sysfs的 class里的input文件夹下创建eventx,为应用层提供接口 */
input的handler注册流程:
input_register_handler(&evdev_handler);
input_table[handler->minor >> 5] = handler; /* 注册该handler类型 */
list_add_tail(&handler->node, &input_handler_list); /* 把该handler加入到核心层维护的handler链表 */
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); /* 尝试匹配dev */
input_match_device(handler, dev) /* 匹配过程(算法) */
handler->connect(handler, dev, id); /* 匹配上了则连接两者到handle */
....... /* 分配次设备号,申请一个struct evdev,并初始化里面的链表,等待队列等等 */
input_register_handle(&evdev->handle); /* 绑定dev到handle,handler在初始化这里以及绑定了 */
evdev_install_chrdev(evdev); /* 把该evdev添加到evdev维护的数组中 */
device_add(&evdev->dev); /* 在sysfs的 class里的input文件夹下创建eventx,为应用层提供接口 */
这位兄弟的这张图太经典了,继续拿来用一下,大家分析的时候参照,具体的分析在我前面的驱动分析里面有分析在这张图。
这张图来自这篇博客,也写的很经典,大家而已参考学习哈。https://www.cnblogs.com/deng-tao/p/6094049.html
下面的流程都可以用这张图分析的到。
事件如何从设备驱动层传递到应用层(以按键事件为例):
input_report_key(button_dev, KEY_LEFT, !gpio_get_value(S5PV210_GPH0(2)));
input_event(dev, EV_KEY, code, !!value);
input_handle_event(dev, type, code, value);
..... /* 各种分析按键类型等 */
input_pass_event(dev, type, code, value);
/* 通过dev->hlist找到handle */
handle->handler->event(handle, type, code, value); /* 进而通过handle找到handler以及里面的event函数 */
....... /* event里面打包数据成input_event格式 */
evdev_pass_event(client, &event); /* 把数据放到环形数组,并通知上层 */
client->buffer[client->head++] = *event; /* 数据放到缓冲区 */
kill_fasync(&client->fasync, SIGIO, POLL_IN) /* 异步通知应用层 */
wake_up_interruptible(&evdev->wait); /* 唤醒等待队列 */
应用层如何让读取一个事件:
evdev_read
...... /* 错误分析 */
retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist); /* 睡眠,自己会进入等待队列,等待被唤醒 */
/* 执行到这里说明发生了事件,是被上面的等待队列唤醒的 */
evdev_fetch_next_event(client, &event));
*event = client->buffer[client->tail++]; /* 将buffer中数据取走*/
input_event_to_user(buffer + retval, &event);
copy_to_user(buffer, event, sizeof(struct input_event)); /* 将数据拷贝到用户空间 */
应用层如何打开一个设备:
evdev_open
evdev = evdev_table[i]; /* 通过次设备号得到该event,event是在connect中放的 */
...... /* 申请一个client并初始化,即使是同一个event,打开多次也要申请多个event */
evdev_attach_client(evdev, client); /* 把该client加入到该evdev的打开链表中 */
evdev_open_device(evdev);
...... /* 判断是不是已经打开了,已经打开的就单纯的把evdev里的open打开次数+1,如果打开次数是0,测需要条用核心层的open */
input_open_device(&evdev->handle);
...... /* 核心层则通过handlr里面的open计数来打开次数 ,同时该设备也有自己的user打开次数统计*/
dev->open(dev); /* 核心层检查发现确实是第一次打开,并且设备驱动层有定义open函数,则继续调用设备驱动层的open函数 */
nonseekable_open(inode, file); /* 打开完后,把该打开模式设置为不能使用seek读取(输入只能一包一包读,不能跳着读) */