博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
input子系统分析之三:驱动模块
阅读量:6572 次
发布时间:2019-06-24

本文共 18854 字,大约阅读时间需要 62 分钟。

内核版本:3.9.5

本节将以even handler来分析设备的注册和打开的过程,分析之前不妨回顾一下上节介绍的数据结构.

结合前两节分析可知,input子系统分为3层,最上一层是event handler,中间层是input core,底层是input driver.input driver把event report到input core层,input core对event进行分发,传到 event handler,相应的event handler层把event 放到event buffer中,等待应用程序读取!这就是input的基本思想.

那么我我们来看,Linux模块机制告诉我们在设备注册之前必须先初始化INPUT子系统,这个工作由input_init()函数来完成.在drivers/input.c中:

1 static int __init input_init(void) 2 { 3     int err; 4  5     err = class_register(&input_class);//注册input类 6     if (err) { 7         pr_err("unable to register input_dev class\n"); 8         return err; 9     }10 11     err = input_proc_init();//创建/proc中的项12     if (err)13         goto fail1;14 15     err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),16                      INPUT_MAX_CHAR_DEVICES, "input");/*注册设备,主设备号为INPUT_MAJOR,就是13,后面注册的输入设备都使用该主设备号*/17     if (err) {18         pr_err("unable to register char major %d", INPUT_MAJOR);19         goto fail2;20     }21 22     return 0;23 24  fail2:    input_proc_exit();25  fail1:    class_unregister(&input_class);26     return err;27 }28 29 static void __exit input_exit(void)30 {31     input_proc_exit();32     unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),33                  INPUT_MAX_CHAR_DEVICES);34     class_unregister(&input_class);35 }36 37 subsys_initcall(input_init);38 module_exit(input_exit);

subsys_initcall和module_exit宏相信大家都很熟悉,这俩函数也很简单,没什么看的.

在入口函数里面创建了一个input_class类,其实就在/sys/class下创建了一个目录input.当然对于一个新设备,可以注册进一个class也可以不注册进去,如果存在对应class的话注册进去更好.另外在/proc创建了入口项,这样就可以/proc目录看到input的信息,然后就注册设备,可以看出输入子系统的主设备号是13,在这里并没有生成设备文件.只是在/dev/目录下创建了input目录,以后所有注册进系统的输入设备文件都放在这个目录下.

那么接下来看看怎么注册input设备的.我们需要在设备驱动层中完成输入设备的注册,通过调用input_register_device()函数来完成,该函数的一个重要任务就是完成设备与事件驱动的匹配.

1 int input_register_device(struct input_dev *dev) 2 { 3     static atomic_t input_no = ATOMIC_INIT(0); 4     struct input_devres *devres = NULL; 5     struct input_handler *handler; 6     unsigned int packet_size; 7     const char *path; 8     int error; 9 10     if (dev->devres_managed) {11         devres = devres_alloc(devm_input_device_unregister,12                       sizeof(struct input_devres), GFP_KERNEL);13         if (!devres)14             return -ENOMEM;15 16         devres->input = dev;17     }18 19     /* Every input device generates EV_SYN/SYN_REPORT events. */20     __set_bit(EV_SYN, dev->evbit);21 22     /* KEY_RESERVED is not supposed to be transmitted to userspace. */23     __clear_bit(KEY_RESERVED, dev->keybit);24 25     /* Make sure that bitmasks not mentioned in dev->evbit are clean. */26     input_cleanse_bitmasks(dev);27 28     packet_size = input_estimate_events_per_packet(dev);29     if (dev->hint_events_per_packet < packet_size)30         dev->hint_events_per_packet = packet_size;31 32     dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;33     dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);34     if (!dev->vals) {35         error = -ENOMEM;36         goto err_devres_free;37     }38 39     /*40      * If delay and period are pre-set by the driver, then autorepeating41      * is handled by the driver itself and we don't do it in input.c.42      */43     init_timer(&dev->timer);44     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {45         dev->timer.data = (long) dev;46         dev->timer.function = input_repeat_key;47         dev->rep[REP_DELAY] = 250;48         dev->rep[REP_PERIOD] = 33;49     }50 51     if (!dev->getkeycode)/*没有定义设备的getkeycode函数,则使用默认的获取键值函数*/52         dev->getkeycode = input_default_getkeycode;53 54     if (!dev->setkeycode)/*没有定义设备的setkeycode函数,则使用默认的设定键值函数*/55         dev->setkeycode = input_default_setkeycode;56 57     dev_set_name(&dev->dev, "input%ld",58              (unsigned long) atomic_inc_return(&input_no) - 1);/*设定dev的名字*/59 60     error = device_add(&dev->dev);/*添加设备*/61     if (error)62         goto err_free_vals;63 64     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);65     pr_info("%s as %s\n",66         dev->name ? dev->name : "Unspecified device",67         path ? path : "N/A");68     kfree(path);69 70     error = mutex_lock_interruptible(&input_mutex);71     if (error)72         goto err_device_del;73 74     list_add_tail(&dev->node, &input_dev_list);/*将设备添加到input_dev_list设备链表*/75 76     list_for_each_entry(handler, &input_handler_list, node)77         input_attach_handler(dev, handler);/*遍历input_handler_list,试图与每一个handler进行匹配*/78 79     input_wakeup_procfs_readers();80 81     mutex_unlock(&input_mutex);82 83     if (dev->devres_managed) {84         dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",85             __func__, dev_name(&dev->dev));86         devres_add(dev->dev.parent, devres);87     }88     return 0;89 90 err_device_del:91     device_del(&dev->dev);92 err_free_vals:93     kfree(dev->vals);94     dev->vals = NULL;95 err_devres_free:96     devres_free(devres);97     return error;98 }

第76,77行对于每个input_dev,遍历input_handler_list,调用input_attach_handler,根据input_handler的id_table判断能否支持这个input_dev.

1 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) 2 { 3     const struct input_device_id *id; 4     int error; 5  6     id = input_match_device(handler, dev); 7     if (!id) 8         return -ENODEV; 9 10     error = handler->connect(handler, dev, id);/*执行handler的connect,建立handler与设备之间的联系*/11     if (error && error != -ENODEV)12         pr_err("failed to attach handler %s to device %s, error: %d\n",13                handler->name, kobject_name(&dev->dev.kobj), error);14 15     return error;16 }

匹配的具体过程如下:

1 static const struct input_device_id *input_match_device(struct input_handler *handler, 2                             struct input_dev *dev) 3 { 4     const struct input_device_id *id; 5  6     /*遍历handler的id_table与device进行匹配*/ 7     for (id = handler->id_table; id->flags || id->driver_info; id++) { 8  9         /*根据flags的标志位,按需要匹配相应的字段*/10         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)11             if (id->bustype != dev->id.bustype)//总线类型不匹配12                 continue;13 14         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)15             if (id->vendor != dev->id.vendor)//生产厂商不匹配16                 continue;17 18         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)19             if (id->product != dev->id.product)//产品不匹配20                 continue;21 22         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)23             if (id->version != dev->id.version)//版本不匹配24                 continue;25 26         if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))//匹配所有的事件类型27             continue;28 29         /*匹配所有事件的子事件*/30         if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))31             continue;32 33         if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))34             continue;35 36         if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))37             continue;38 39         if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))40             continue;41 42         if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))43             continue;44 45         if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))46             continue;47 48         if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))49             continue;50 51         if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))52             continue;53 54         if (!handler->match || handler->match(handler, dev))55             return id;//匹配成功,返回id56     }57 58     return NULL;59 }

这里接下来就到input_handler的connect了,看来注册input_handler是一道绕不过去的坎儿.那么我们来看具体的event handler的注册,在drivers/input/evdev.c中:

1 static const struct input_device_id evdev_ids[] = { 2     { .driver_info = 1 },    /* Matches all devices */ 3     { },            /* Terminating zero entry */ 4 }; 5  6 MODULE_DEVICE_TABLE(input, evdev_ids); 7  8 static struct input_handler evdev_handler = { 9     .event        = evdev_event,10     .events        = evdev_events,11     .connect    = evdev_connect,12     .disconnect    = evdev_disconnect,13     .legacy_minors    = true,14     .minor        = EVDEV_MINOR_BASE,15     .name        = "evdev",16     .id_table    = evdev_ids,17 };18 19 static int __init evdev_init(void)20 {21     return input_register_handler(&evdev_handler);22 }23 24 static void __exit evdev_exit(void)25 {26     input_unregister_handler(&evdev_handler);27 }28 29 module_init(evdev_init);30 module_exit(evdev_exit);

这里一些相关的代码我也顺便贴出来了,再来一个:

1 int input_register_handler(struct input_handler *handler) 2 { 3     struct input_dev *dev; 4     int error; 5  6     error = mutex_lock_interruptible(&input_mutex); 7     if (error) 8         return error; 9 10     INIT_LIST_HEAD(&handler->h_list);11 12     list_add_tail(&handler->node, &input_handler_list);13 14     list_for_each_entry(dev, &input_dev_list, node)15         input_attach_handler(dev, handler);16 17     input_wakeup_procfs_readers();18 19     mutex_unlock(&input_mutex);20     return 0;21 }

这个函数不用多说了,注册event handler这个input_haner的实例,并且在注册之后迅速遍历了一下input_dev_list链表,查找所有的input_dev设备,看这个input_handler是否支持它.那么回到我们上面的遗留问题看看input_handler的connect函数.对于event handler来说,这个函数就是evdev_connect.在drivers/input/evdev.c中:

1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, 2              const struct input_device_id *id) 3 { 4     struct evdev *evdev; 5     int minor; 6     int dev_no; 7     int error; 8  9     minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);10     if (minor < 0) {11         error = minor;12         pr_err("failed to reserve new minor: %d\n", error);13         return error;14     }15 16     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);17     if (!evdev) {18         error = -ENOMEM;19         goto err_free_minor;20     }21 22     INIT_LIST_HEAD(&evdev->client_list);23     spin_lock_init(&evdev->client_lock);24     mutex_init(&evdev->mutex);25     init_waitqueue_head(&evdev->wait);26     evdev->exist = true;27 28     dev_no = minor;29     /* Normalize device number if it falls into legacy range */30     if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)31         dev_no -= EVDEV_MINOR_BASE;32     dev_set_name(&evdev->dev, "event%d", dev_no);33 34     /*初始化handle,每个evdev中都有一个handle*/35     evdev->handle.dev = input_get_device(dev);/*这里就将handle的dev指针指向了input_dev*/36     evdev->handle.name = dev_name(&evdev->dev);37     evdev->handle.handler = handler;/*这里将handle的handler指向了当前的input_handler.注意本函数evdev_connect,可能是在在输入设备注册的时候38     在input_register_device函数中调用input_attach_handler的时候调用;也可能是在输入设备的处理方法input_handler时在input_register_handler39     函数中也会用到input_attach_handler函数,就会调用本函数.这里就很明显了,本函数就将input_handler和input_dev都放在input_handle中统一管理*/40     evdev->handle.private = evdev;41 42     /*初始化evdev中的内嵌device*/43     evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);44     evdev->dev.class = &input_class;45     evdev->dev.parent = &dev->dev;46     evdev->dev.release = evdev_free;47     device_initialize(&evdev->dev);48 49     /*注册handle,主要将handle链接到input_dev和handler的h_list链表中去*/50     error = input_register_handle(&evdev->handle);51     if (error)52         goto err_free_evdev;53 54     cdev_init(&evdev->cdev, &evdev_fops);55     evdev->cdev.kobj.parent = &evdev->dev.kobj;56     error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);57     if (error)58         goto err_unregister_handle;59 60     error = device_add(&evdev->dev);61     if (error)62         goto err_cleanup_evdev;63 64     return 0;65 66  err_cleanup_evdev:67     evdev_cleanup(evdev);68  err_unregister_handle:69     input_unregister_handle(&evdev->handle);70  err_free_evdev:71     put_device(&evdev->dev);72  err_free_minor:73     input_free_minor(minor);74     return error;75 }

至此设备的注册完成!对应event handler,在/dev/input中将多出一个event(x)设备文件,对应一个evdev实例,应用程序打开它的话也就意味着通过event handler来和设备驱动层传递事件.

第54~60行这几行字符设备初始化,这里之后再将字符设备注册,然后我们打开event(x)设备文件的时候实际上就调用evdev_fops里定义的open回调函数.相应的操作函数也在evdev_fops中定义了.我们来看看evdev_fops.同样在drivers/input/evdev.c中:

1 static const struct file_operations evdev_fops = { 2     .owner        = THIS_MODULE, 3     .read        = evdev_read, 4     .write        = evdev_write, 5     .poll        = evdev_poll, 6     .open        = evdev_open, 7     .release    = evdev_release, 8     .unlocked_ioctl    = evdev_ioctl, 9 #ifdef CONFIG_COMPAT10     .compat_ioctl    = evdev_ioctl_compat,11 #endif12     .fasync        = evdev_fasync,13     .flush        = evdev_flush,14     .llseek        = no_llseek,15 };

首先来看打开event(x)设备文件,evdev_open函数.

1 static int evdev_open(struct inode *inode, struct file *file) 2 { 3     struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); 4     unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); 5     struct evdev_client *client; 6     int error; 7  8     /*每当一个应用程序打开该文件都会生成一个client*/ 9     client = kzalloc(sizeof(struct evdev_client) +10                 bufsize * sizeof(struct input_event),11              GFP_KERNEL);12     if (!client)13         return -ENOMEM;14 15     client->bufsize = bufsize;16     spin_lock_init(&client->buffer_lock);17     client->evdev = evdev;18     evdev_attach_client(evdev, client);/*将client链入evdev的client_list中*/19 20     error = evdev_open_device(evdev);21     if (error)22         goto err_free_client;23 24     file->private_data = client;25     nonseekable_open(inode, file);26 27     return 0;28 29  err_free_client:30     evdev_detach_client(evdev, client);31     kfree(client);32     return error;33 }

第20行调用evdev_open_device来打开evdev(如果将inpit_dev抽象为一个父对象,这其实就是一个子对象,或者说这个结构封装了一个具体的实例化的input_dev).

1 static int evdev_open_device(struct evdev *evdev) 2 { 3     int retval; 4  5     retval = mutex_lock_interruptible(&evdev->mutex); 6     if (retval) 7         return retval; 8  9     if (!evdev->exist)10         retval = -ENODEV;11     else if (!evdev->open++) {
/*如果是第一次打开该设备,则要执行输入设备的open*/12 retval = input_open_device(&evdev->handle);13 if (retval)14 evdev->open--;15 }16 17 mutex_unlock(&evdev->mutex);18 return retval;19 }

这里终于看到了输入子系统核心层的接口input_open_device,此函数就是内核为我们做好的初始化input_dev的函数接口.

1 int input_open_device(struct input_handle *handle) 2 { 3     struct input_dev *dev = handle->dev; 4     int retval; 5  6     retval = mutex_lock_interruptible(&dev->mutex); 7     if (retval) 8         return retval; 9 10     if (dev->going_away) {11         retval = -ENODEV;12         goto out;13     }14 15     handle->open++;16 17     if (!dev->users++ && dev->open)/*如果是第一次打开此设备(当前input_dev的使用者数为0),并且input_dev中定义的open函数不为空*/18         retval = dev->open(dev);//执行input_dev中定义的open,完成设备的初始化19 20     if (retval) {21         dev->users--;22         if (!--handle->open) {23             /*24              * Make sure we are not delivering any more events25              * through this handle26              */27             synchronize_rcu();28         }29     }30 31  out:32     mutex_unlock(&dev->mutex);33     return retval;34 }

至于具体的如何初始化input_dev,这个是具体的输入设备去实现的.我们这里不分析了.现在来看看,对于一个event(x)设备文件的.

1 static ssize_t evdev_read(struct file *file, char __user *buffer, 2               size_t count, loff_t *ppos) 3 { 4     struct evdev_client *client = file->private_data; 5     struct evdev *evdev = client->evdev; 6     struct input_event event; 7     size_t read = 0; 8     int error; 9 10     if (count != 0 && count < input_event_size())11         return -EINVAL;12 13     for (;;) {14         if (!evdev->exist)/*evdev没有定义返回函数,直接返回吧,因为我们下面必须要用到这个函数*/15             return -ENODEV;16 17         if (client->packet_head == client->tail &&18             (file->f_flags & O_NONBLOCK))/*无数据并且是非阻塞状态,则直接返回,说明没什么要处理的*/19             return -EAGAIN;20 21         /*22          * count == 0 is special - no IO is done but we check23          * for error conditions (see above).24          */25         if (count == 0)26             break;27 28         while (read + input_event_size() <= count &&29                evdev_fetch_next_event(client, &event)) {30 31             if (input_event_to_user(buffer + read, &event))32                 return -EFAULT;33 34             read += input_event_size();35         }36 37         if (read)38             break;39 40         if (!(file->f_flags & O_NONBLOCK)) {
/*如果是可阻塞状态的话,则等待在wait队列上.直到有数据要被处理,当前进程才被唤醒.这很好理解,既然是41 输入设备,读的话比如读按键,那么必须要有硬件设备有按键按下才会返回按键值,这里还是处于事件处理层,应用程序在这里休眠,那么谁来唤醒?42 当然是有按键按下才去唤醒,因此这个工作就交给了设备驱动层,那么找到这个唤醒呢,直接去找不好找,那么可以直接搜索evdev->wait,搜索结果43 可知evdev->wait在evdev_event()函数中被唤醒*/44 error = wait_event_interruptible(evdev->wait,45 client->packet_head != client->tail ||46 !evdev->exist);47 if (error)48 return error;49 }50 }51 52 return read;53 }

注释中说的很清楚,evdev_event()会唤醒此处的读按键进程.那么evdev_event()又是被谁调用?显然是设备驱动层,现在看一个设备层例子,内核中有个按键的例子,gpiokey.c,这只是个例子不针对任何设备,在gpiokey.c终端处理函数里面.

1 static void evdev_pass_values(struct evdev_client *client, 2             const struct input_value *vals, unsigned int count, 3             ktime_t mono, ktime_t real) 4 { 5     struct evdev *evdev = client->evdev; 6     const struct input_value *v; 7     struct input_event event; 8     bool wakeup = false; 9 10     event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?11                       mono : real);12 13     /* Interrupts are disabled, just acquire the lock. */14     spin_lock(&client->buffer_lock);15 16     for (v = vals; v != vals + count; v++) {17         event.type = v->type;18         event.code = v->code;19         event.value = v->value;20         __pass_event(client, &event);21         if (v->type == EV_SYN && v->code == SYN_REPORT)22             wakeup = true;23     }24 25     spin_unlock(&client->buffer_lock);26 27     if (wakeup)28         wake_up_interruptible(&evdev->wait);29 }30 31 /*32  * Pass incoming events to all connected clients.33  */34 static void evdev_events(struct input_handle *handle,35              const struct input_value *vals, unsigned int count)36 {37     struct evdev *evdev = handle->private;38     struct evdev_client *client;39     ktime_t time_mono, time_real;40 41     time_mono = ktime_get();42     time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());43 44     rcu_read_lock();45 46     client = rcu_dereference(evdev->grab);47 48     if (client)49         evdev_pass_values(client, vals, count, time_mono, time_real);50     else51         list_for_each_entry_rcu(client, &evdev->client_list, node)52             evdev_pass_values(client, vals, count,53                       time_mono, time_real);54 55     rcu_read_unlock();56 }57 58 /*59  * Pass incoming event to all connected clients.60  */61 static void evdev_event(struct input_handle *handle,62             unsigned int type, unsigned int code, int value)63 {64     struct input_value vals[] = { { type, code, value } };65 66     evdev_events(handle, vals, 1);67 }

好了,就贴到这里,input子系统的大体脉络以及比较清楚了.

转载于:https://www.cnblogs.com/jason-lu/p/3156411.html

你可能感兴趣的文章
linux安装go环境并编写第一个go程序
查看>>
解决:laravel出现Please provide a valid cache path.
查看>>
[JAVA] String常用方法
查看>>
oracle
查看>>
兼容IE浏览器样式的html上传文件控件
查看>>
直接插入排序
查看>>
ssh建立安全跳板机,方便外网登录内网机器
查看>>
fstab中mount错误导致不能启动
查看>>
OSPF转发地址深入解析
查看>>
SQLServer的Top功能
查看>>
CentOS之crontab
查看>>
Nginx-Access日志格式
查看>>
【在线研讨-现场文字】《敏捷开发用户故事分类与组织结构(二期-3)》2012-07-03...
查看>>
F5扩展Synthesis架构实现可靠的应用与互联网访问
查看>>
Hyper-V 2012 R2 配置存储QoS
查看>>
易语言 --什么情况下 用许可证
查看>>
项目总结:凡事预则立,不预则废!
查看>>
VNC怎么和宿主机共享粘贴板
查看>>
ORA-32004: obsolete and/or deprecated parameter(s)
查看>>
建属于自己的网站
查看>>