1、input子系统架构
下面对每层进行分析:
2、核心层input.c
input_init-> register_chrdev(INPUT_MAJOR, "input", &input_fops); static struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, }; static int input_open_file(struct inode *inode, struct file *file) { ... struct input_handler *handler = input_table[iminor(inode) >> 5]; struct file_operations *old_fops, *new_fops = NULL; if (!handler || !(new_fops = fops_get(handler->fops))) return -ENODEV; int err; old_fops = file->f_op; file->f_op = new_fops; err = new_fops->open(inode, file); … }
核心层主要创建了一个字符设备,并且只初始化了open函数。当打开设备时,input_open_file函数被调用,这个函数将从input_table数组中找到设备对应的handler,然后将设备的真正的fops结构赋值,最终掉用的就是对应设备的open函数。关于input_table数组在那里赋值,当然就是注册handler时。在handler层说明。
3、设备层
通过input_register_device注册设备。
void input_register_device(struct input_dev *dev) { … INIT_LIST_HEAD(&dev->h_list); //加入list list_add_tail(&dev->node, &input_dev_list); // 对于每一个input_handler,都调用input_attach_handler list_for_each_entry(handler, &input_handler_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev)))//根据id->flags进行对应匹配 //如果匹配成功则调用handler的connect函数 if ((handle = handler->connect(handler, dev, id))) input_link_handle(handle);//handle加入list }
4、handler层
通过input_register_handler注册handler
void input_register_handler(struct input_handler *handler) { ... INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) input_table[handler->minor >> 5] = handler; //赋值input_table list_add_tail(&handler->node, &input_handler_list); // 对于每个input_dev,调用input_attach_handler list_for_each_entry(dev, &input_dev_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) //根据id->flags进行对应匹配 //如果匹配成功则调用handler的connect函数 if ((handle = handler->connect(handler, dev, id))) input_link_handle(handle); //handle加入list ... }
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"
***特殊的handler:evdev_handler//用于匹配所有的devices
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 struct input_device_id evdev_ids[] = { { .driver_info = 1 },/* Matches all devices */ //匹配所有的设备0 { },/* Terminating zero entry */ };
5、怎么建立连接
当注册一个设备的时候,evdev_handler->evdev_connect被调用。
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) { ... if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) INIT_LIST_HEAD(&evdev->list); init_waitqueue_head(&evdev->wait); evdev->exist = 1; evdev->minor = minor; evdev->handle.dev = dev; evdev->handle.name = evdev->name; evdev->handle.handler = handler; evdev->handle.private = evdev; return &evdev->handle; ... }
创建handle,最主要的就是将handle.handler = handler,这样就建立了联系。通过handle将设备和hanler联系起来。
6、键盘,鼠标,怎么read
evdev_fops. evdev_read
static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) { .... // 无数据并且是非阻塞方式打开,则立刻返回 if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; // 否则休眠,等待被唤醒。 retval = wait_event_interruptible(list->evdev->wait,list->head != list->tail || (!list->evdev->exist)); //拷贝数据到用户空间 if (copy_to_user(buffer + retval, list->buffer + list->tail,sizeof(struct input_event))) ... }
何时可以读,当然是硬件将其唤醒。input_dev将其唤醒。
比如在gpio_keys.c中,通过中断来上报事件
中断到来:
input_event list_for_each_entry(handle, &dev->h_list, d_node) if(handle->open) handle->handler->event(handle, type, code, value);//调用的就是evdev_event, input_sync //表示发送完毕
7、事件类型
struct input_dev { unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件 unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键 unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮 unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y unsigned long mscbit[NBITS(MSC_MAX)];// 表示其他功能事件 unsigned long ledbit[NBITS(LED_MAX)]; //表示指示灯事件 unsigned long sndbit[NBITS(SND_MAX)];// 表示声音或警报事件 unsigned long ffbit[NBITS(FF_MAX)];// 表示作用力事件 unsigned long swbit[NBITS(SW_MAX)];// 表示开关事件
8、2.6和4.9内核input核心层的区别
主要的流程都是一样的,4.9的内核去除了在初始化创建register_chrdev(INPUT_MAJOR, "input", &input_fops);而是在connet函数中创建对应的字符设备。也就去除了input_table数组。对2.6进行了优化,去除了查找fops的过程,直接通过handle找到对应的fops。