input子系统详解2
Posted 铅笔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了input子系统详解2相关的知识,希望对你有一定的参考价值。
上一节大概了解了输入子系统的流程
这一节认真追踪一下代码
input.c:
input_init(void)函数
1 static int __init input_init(void) 2 { 3 int err; 4 5 err = class_register(&input_class); 6 if (err) { 7 printk(KERN_ERR "input: unable to register input_dev class\\n"); 8 return err; 9 } 10 11 err = input_proc_init(); 12 if (err) 13 goto fail1; 14 15 err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 16 if (err) { 17 printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); 18 goto fail2; 19 } 20 21 return 0; 22 23 fail2: input_proc_exit(); 24 fail1: class_unregister(&input_class); 25 return err; 26 }
err = register_chrdev(INPUT_MAJOR, "input", &input_fops); /* 注册字符设备(主设备号INPUT_MAJOR = 13) */
input_fops-->
static const 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];
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV;
/*
* That\'s _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops;
err = new_fops->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}
知识点之一:struct input_handler *handler = input_table[iminor(inode) >> 5];
/* struct input_handler是用来定义一个handler的结构体,一个handler就对应一个struct input_handler结构体变量 */
----->input_table ----->
static struct input_handler *input_table[8];
既然是静态的就肯定在input.c中 ------>>>int input_register_handler(struct input_handler *handler)
int input_register_handler(struct input_handler *handler) { struct input_dev *dev; INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) { if (input_table[handler->minor >> 5]) return -EBUSY; input_table[handler->minor >> 5] = handler; } list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) input_attach_handler(dev, handler);//每个input_dev调用该函数并根据input_handler的id_table判断能不能支持这个inpt_dev了 input_wakeup_procfs_readers(); return 0; }
知识点之二:handler = input_table[iminor(inode) >> 5]; /* input_table是input子系统中用来管理handler的一个数组,里面存放的是handler的指针。通过次设备号找到本次应用层打开的输入设备对应的handler结构体 */
可知 input_table[]是由这个函数完成构造的 继续全局搜索该函数input_register_handler
可得到:
这些函数即分别对应上层的各个不同的handler的源代码。
evdev.c:
static int __init evdev_init(void) { return input_register_handler(&evdev_handler); }
input_register_handler(&evdev_handler);
在evdev_init函数中直接调用核心层提供的handler注册函数注册event这个handler,evdev_handler这个struct input_handler类型的变量就是对这个handler的一个描述,
evdev_handler:
static struct input_handler evdev_handler = { .event = evdev_event, /* 这个函数的作用就是实现将下层的事件包进行封装,然后存放在缓冲区中待read函数读取,并唤醒阻塞等待读取数据的进程 */ .connect = evdev_connect, /* 匹配成功之后,就会调用这个函数进行连接 */ .disconnect = evdev_disconnect, /* 断开连接 */ .fops = &evdev_fops, /* 这个就是应用open/read/write...时对应的接口函数封装 file_operations */ .minor = EVDEV_MINOR_BASE, /* 这个就是本handler下的设备的次设备号的起始设备号(基设备号) */ .name = "evdev", /* handler的名字 */ .id_table = evdev_ids, /* 一个handler描述自己支持的输入设备的特征的一个结构体,一个变量就描述了该halder支持的一类设备 */ };
evdev_handler变量就是本次分析的handler对应的结构体变量,变量中填充最重要的有3个:
evdev_event函数:
evdev_connect函数:
evdev_fops变量:这个变量是struct fileoperations类型的结构体变量,将来应用层通过调用open函数时,这个结构体封装的函数会被核心层中注册字符设备时封装的open函数调用
说明:
input_handler中id_table 会和input_register_device中的struct input_dev类型的结构体进行匹配
全局搜索input_register_device可以知道有很多鼠标键盘等设备都会调用这个函数进行一个注册
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; set_bit(EV_SYN, dev->evbit); /* * 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; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; list_add_tail(&dev->node, &input_dev_list);//置入到链表 snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); if (!dev->cdev.dev) dev->cdev.dev = dev->dev.parent; error = class_device_add(&dev->cdev); if (error) return error; path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler);//input_handler_list链表中的每个都调用这个函数
该函数根据input_handler的id_table判断能不能支持这个input_dev input_wakeup_procfs_readers(); return 0; }
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; if (handler->blacklist && input_match_device(handler->blacklist, dev)) return -ENODEV; id = input_match_device(handler->id_table, dev); if (!id) return -ENODEV; error = handler->connect(handler, dev, id); if (error && error != -ENODEV) printk(KERN_ERR "input: failed to attach handler %s to device %s, " "error: %d\\n", handler->name, kobject_name(&dev->cdev.kobj), error); return error; }
input_register_handler
// 放入数组
input_table[handler->minor >> 5] = handler;
// 放入链表
list_add_tail(&handler->node, &input_handler_list);
// 对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
注册输入设备:
input_register_device
// 放入链表
list_add_tail(&dev->node, &input_dev_list);
// 对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
input_attach_handler
id = input_match_device(handler->id_table, dev);
error = handler->connect(handler, dev, id);
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接
怎么建立连接?
在 evdec.c中找这个例子怎么建立连接
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; struct class_device *cdev; dev_t devt; int minor; int error; for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\\n"); return -ENFILE; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); if (!evdev) return -ENOMEM; INIT_LIST_HEAD(&evdev->client_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; sprintf(evdev->name, "event%d", minor); evdev_table[minor] = evdev; devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), cdev = class_device_create(&input_class, &dev->cdev, devt, dev->cdev.dev, evdev->name); if (IS_ERR(cdev)) { error = PTR_ERR(cdev); goto err_free_evdev; } /* temporary symlink to keep userspace happy */ error = sysfs_create_link(&input_class.subsys.kobj, &cdev->kobj, evdev->name); if (error) goto err_cdev_destroy; error = input_register_handle(&evdev->handle); if (error) goto err_remove_link; return 0; err_remove_link: sysfs_remove_link(&input_class.subsys.kobj, evdev->name); err_cdev_destroy: class_device_destroy(&input_class, devt); err_free_evdev: kfree(evdev); evdev_table[minor] = NULL; return error; }
1. 分配一个input_handle结构体
struct evdev *evdev;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
2.
input_handle.dev = input_dev; // 指向左边的输入设备input_dev
input_handle.handler = input_handler; // 指向右边的input_handler
3. 注册这个handle:
error = input_register_handle(&evdev->handle);
int input_register_handle(struct input_handle *handle) { struct input_handler *handler = handle->handler; list_add_tail(&handle->d_node, &handle->dev->h_list); list_add_tail(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); return 0; }
连接的时候构建一个input_handle结构体其中刚有个成员.dev 这个成员指向输入设备即input_device
还有一个.handler成员 他指向input_handler
input_handler有个h_list 指向了input_handle; 对于input_handler来讲就可以由h_list 找到input_handle然后找到dev然后找到相应的设备了
inpu_dev 有个 h_list 指向了input_handle; 对输入设备inpu_dev 来讲就可以由h_list 找到input_handle 然后input_handle 发现到inpu_dev 所对应的处理input_handler
evdev_connect
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle
// 设置
evdev->handle.dev = dev; // 指向左边的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; // 指向右边的input_handler
evdev->handle.private = evdev;
// 注册
error = input_register_handle(&evdev->handle);
怎么读按键?
app: read
--------------------------
.......
evdev_read
// 无数据并且是非阻塞方式打开,则立刻返回
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// 否则休眠
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
谁来唤醒?不好找,那就找在哪里休眠?在距代码中是在这evdev->wait里休眠 那就在这里唤醒 全局搜索evdev->wait
evdev_event时间处理函数函数:
wake_up_interruptible(&evdev->wait);
evdev_event来唤醒,那么evdev_event被谁调用?
猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr
// 上报事件
input_event(input, type, button->code, !!state);
input_sync(input);
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
struct input_handle *handle;
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value); evdev_event被调用
evdev_event函数分析:
当设备驱动层发生一个输入事件的时候,设备驱动层就会去读取设备硬件,获取事件信息,然后上报给核心层,核心层索引到与当前设备匹配成功的所有上层handler,然后再上报给
这些handler,上层就会调用event函数将事件包封装放入缓冲区,然后唤醒进程。 我对这个函数的内部细节并不了解,只能知道大概加上自己的一些猜想.
其实下层可以上报的事件都在我们的内核中是定义好的,我们都可以上报这些事,但是input子系统的上层输入事件驱动层的各个handler只能够处理某一些事件(event除外),
例如joy handler只能处理摇杆类型的事件,key handler只能处理键盘,内部实现的原理就是会在核心层做handler和device匹配的过程。如果我们的上报的事件与多个handler都能够匹配成功,那么绑定之后核心层会向这多个handler都上报事件,再由handler上报给应用层。
同一个input设备可以对应多个次设备号,因为对于一个输入设备来说,他在进行匹配的时候可能会与多个handler匹配成功,匹配成功就会在连接过程中创建设备文件,而不同的handler创建的设备文件的次设备号是不一样的,所以就是导致一个设备对应多个次设备号。
以上是关于input子系统详解2的主要内容,如果未能解决你的问题,请参考以下文章
14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段