linux input子系统学习(一)之 input-core
2015-10-19 10:42
691 查看
linux中有众多的子系统,input子系统是其中的一种,用来处理一类型的输入设备,例如keypad,touchpanel,mice等等,我目前接触到就只有前2种也只写过前2种的驱动,所以后面的就只介绍到key的驱动以及TP的驱动了。
input的核心是一个文件,即input.c,(drivers\input),说实在的其实input子系统也是一种字符设备,只不过进行了一下封装。整个input子系统用一个系统框图就可以很好的概括了,当然这个都是大牛的书上写的,抄来的。
![](http://img.blog.csdn.net/20151015193107387?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在input.c中还有几个比较重要接口用来注册input的设备及驱动的。input_register_handler 是用来注册input的驱动的,这个驱动管理着同一类的设备,一个驱动最多能管理32个设备。
好,看注册设备的input_register_device(struct input_dev *dev)。
申请一个input设备input_allocate_device
总结:在input.c中,初始化了input的子系统,并且提供了一些有关将设备以及驱动如何添加到input子系统中的函数,利用这些函数,就可以往input子系统中的添加设备,读取设备的值等等。
input的核心是一个文件,即input.c,(drivers\input),说实在的其实input子系统也是一种字符设备,只不过进行了一下封装。整个input子系统用一个系统框图就可以很好的概括了,当然这个都是大牛的书上写的,抄来的。
static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, .llseek = noop_llseek, }; static int __init input_init(void) { int err; err = class_register(&input_class); //会在sys/class/下产生input的目录 if (err) { pr_err("unable to register input_dev class\n"); return err; } err = input_proc_init(); //会在proc/bus/产生input的目录,以及生成devices以及handlers的文件 if (err) goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //注册一个字符设备,主设备号INPUT_MAJOR = 13,名字是input,相关的操作函数初始化为 if (err) { //input_fops pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; } return 0; fail2: input_proc_exit(); fail1: class_unregister(&input_class); return err; } static void __exit input_exit(void) { input_proc_exit(); unregister_chrdev(INPUT_MAJOR, "input"); class_unregister(&input_class); } subsys_initcall(input_init); //subsys_initcall 保证较高优先级初始化 module_exit(input_exit);由此说明,input子系统也是字符设备。我们知道,当上层应用需要访问input的设备时,必然要调用input的open函数:
static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler; const struct file_operations *old_fops, *new_fops = NULL; int err; err = mutex_lock_interruptible(&input_mutex); if (err) return err; /* No load-on-demand here? */ handler = input_table[iminor(inode) >> 5]; //input_table存放的是handler,相当于我们平常所用的驱动指针 if (handler) new_fops = fops_get(handler->fops); mutex_unlock(&input_mutex); /* * That's _really_ odd. Usually NULL ->open means "nothing special", * not "no device". Oh, well... */ if (!new_fops || !new_fops->open) { fops_put(new_fops); err = -ENODEV; goto out; } old_fops = file->f_op; file->f_op = new_fops; err = new_fops->open(inode, file); //实际调用的是handler的指针,以evdev handler为例,实际调用的是 if (err) { //evdev_open的函数 fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); out: return err; }
在input.c中还有几个比较重要接口用来注册input的设备及驱动的。input_register_handler 是用来注册input的驱动的,这个驱动管理着同一类的设备,一个驱动最多能管理32个设备。
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); // 初始化handler->h_list 的链表 if (handler->fops != NULL) { //判断handler是否为空 if (input_table[handler->minor >> 5]) { retval = -EBUSY; goto out; } input_table[handler->minor >> 5] = handler; //根据minor将全局的input_table 中初始化为handler } list_add_tail(&handler->node, &input_handler_list); //input_handler_list是一个全局的存放handler的链表 list_for_each_entry(dev, &input_dev_list, node) //遍历input_dev_list的链表,这个链表是存放input dev的链表 input_attach_handler(dev, handler); //对dev和handler的进行匹配 input_wakeup_procfs_readers(); //唤醒input_devices_poll_wait的等待队列 out: mutex_unlock(&input_mutex); return retval; } EXPORT_SYMBOL(input_register_handler);那么比较重要的是他们是如何匹配的,看input_attach_handler
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; //id中包括input_device的设备的总线类型,version,支持的输入类型,vendor等等 int error; id = input_match_device(handler, dev); //实际进行match的函数,具体分析 if (!id) return -ENODEV; error = handler->connect(handler, dev, id); //将dev及handler进行Connect具体分析 if (error && error != -ENODEV) pr_err("failed to attach handler %s to device %s, error: %d\n", handler->name, kobject_name(&dev->dev.kobj), error); return error; }看input_match_device的函数
static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev) { const struct input_device_id *id; int i; for (id = handler->id_table; id->flags || id->driver_info; id++) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) if (id->bustype != dev->id.bustype) //判断bustype 是否相等 continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) if (id->vendor != dev->id.vendor) //判断vendor 是否相等 continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) if (id->product != dev->id.product) //判断product 是否相等 continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) if (id->version != dev->id.version) //判断version 是否相等 continue; MATCH_BIT(evbit, EV_MAX); //判断handler的是否支持dev的事件类型,不支持的话直接进行下一轮的比较 MATCH_BIT(keybit, KEY_MAX); MATCH_BIT(relbit, REL_MAX); MATCH_BIT(absbit, ABS_MAX); MATCH_BIT(mscbit, MSC_MAX); MATCH_BIT(ledbit, LED_MAX); MATCH_BIT(sndbit, SND_MAX); MATCH_BIT(ffbit, FF_MAX); MATCH_BIT(swbit, SW_MAX); if (!handler->match || handler->match(handler, dev)) //判断match是否为空,后调用handler的match函数 return id; //返回id } return NULL; }当成功匹配到时,会调用handler的connect的函数即handler->connect(handler, dev, id)。
好,看注册设备的input_register_device(struct input_dev *dev)。
int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); struct input_handler *handler; const char *path; int error; /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); if (!dev->hint_events_per_packet) dev->hint_events_per_packet = input_estimate_events_per_packet(dev); /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250; dev->rep[REP_PERIOD] = 33; } if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; //查看是否dev的getkeycode是否被赋值,否则采用input_default_getkeycode if (!dev->setkeycode) //查看是否dev的setkeycode是否被赋值,否则采用input_default_setkeycode dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); error = device_add(&dev->dev); if (error) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); //<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(248, 248, 248);">设置input_dev中device的名字,会在/class/input中出现input0,input1...</span> pr_info("%s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) { device_del(&dev->dev); return error; } list_add_tail(&dev->node, &input_dev_list); //将dev添加到input_dev_list中 list_for_each_entry(handler, &input_handler_list, node) //遍历input_handler_list 链表 input_attach_handler(dev, handler); //匹配dev和handler,过程和注册handler是一样的 input_wakeup_procfs_readers(); //唤醒读取的线程 mutex_unlock(&input_mutex); return 0; }
申请一个input设备input_allocate_device
struct input_dev *input_allocate_device(void) { struct input_dev *dev; dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); //申请input_dev大空间 if (dev) { //对申请的空间进行初始化赋值 dev->dev.type = &input_dev_type; dev->dev.class = &input_class; device_initialize(&dev->dev); mutex_init(&dev->mutex); spin_lock_init(&dev->event_lock); INIT_LIST_HEAD(&dev->h_list); //初始化dev的h_list INIT_LIST_HEAD(&dev->node); //初始化dev的node __module_get(THIS_MODULE); } return dev; }
总结:在input.c中,初始化了input的子系统,并且提供了一些有关将设备以及驱动如何添加到input子系统中的函数,利用这些函数,就可以往input子系统中的添加设备,读取设备的值等等。
相关文章推荐
- Linux 常用命令 对应的 英文 [便于记忆]
- linux应用编程笔记(11)信号通信
- linux内核之文件系统
- linux中 include文件,放置目录路径
- centos 防火墙关闭
- 【linux】ubuntu安装问题,go back to the menu and correct this problem
- Linux下防止进程使用swap及防止OOM机制导致进程被kill掉
- Linux页表机制初始化
- Linux常用命令大全
- Linux高端内存映射(上)
- Linux高端内存映射(中)
- Linux高端内存映射(下)
- Linux伙伴系统(五)--通过迁移类型分组来实现反碎片
- Linux伙伴系统(四)--释放页
- Linux防火墙:iptables禁IP与解封IP常用命令
- Linux防火墙:iptables禁IP与解封IP常用命令
- Linux伙伴系统(三)--分配页
- Linux伙伴系统(二)--伙伴系统的初始化
- Linux伙伴系统(一)--伙伴系统的概述
- Linux<九>程序与资源管理