Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1742937
  • 博文数量: 1493
  • 博客积分: 38
  • 博客等级: 民兵
  • 技术积分: 5834
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-19 17:28
文章分类

全部博文(1493)

文章存档

2016年(11)

2015年(38)

2014年(137)

2013年(253)

2012年(1054)

2011年(1)

分类:

2012-08-30 08:53:32

原文地址:输入子系统一 作者:_ChinaUnix

    Input子系统是分层结构,由上图可以分为三层,核心层,硬件驱动层,事件处理层,各个层之间的通信的是通过事件,其传送方向为硬件驱动层-à子系统核心层à事件处理层-à用户空间,下面来分析下每层具体做的工作。
Input子系统结构与功能实现
1)其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
    (2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
1.核心层
输入子系统核心对应于/drivers/input/input.c文件,也是作为一个模块注册到内核的,先分析模块初始化函数。

点击(此处)折叠或打开

  1. static int __init input_init(void)
  2. {
  3.     int err;

  4.     err = class_register(&input_class);//向内核注册一个类,用于linux设备模型,注册后会在/sys/class下面出现input目录。
  5.     if (err) {
  6.         printk(KERN_ERR "input: unable to register input_dev class\n");
  7.         return err;
  8.     }

  9.     err = input_proc_init();//proc文件系统相关初始化

  10.     if (err)
  11.         goto fail1;

  12.     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
  13.     if (err) {
  14.         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
  15.         goto fail2;
  16.     }

  17.     return 0;

  18.  fail2:    input_proc_exit();
  19.  fail1:    class_unregister(&input_class);
  20.     return err;
  21. }

    从上面可以看出其实input设备也是一类字符设备,只不过操作方法交给了输入子系统。所有的主设备号为13的字符设备的操作最终都会转入到input_fops,Input_fops定义如下:

点击(此处)折叠或打开

  1. static const struct file_operations input_fops = {
  2.     .owner = THIS_MODULE,
  3.     .open = input_open_file,
  4. };

    仔细看看,这个和以前的字符设备不一样,该fops只有一个open函数,那么怎么样实现从输入设备获取到值呢?我们来看看input_open_file做了些什么。


点击(此处)折叠或打开

  1. static int input_open_file(struct inode *inode, struct file *file)
  2. {
  3.     struct input_handler *handler = input_table[iminor(inode) >> 5];
  4. //根据次设备号在input_table数组中查询相应的处理函数(Input_table[]input_register_handler中初始化)

  5.     const struct file_operations *old_fops, *new_fops = NULL;
  6.     int err;

  7.     /* No load-on-demand here? */
  8.     if (!handler || !(new_fops = fops_get(handler->fops)))//获取handler结构体的fops
  9.         return -ENODEV;

  10.     /*
  11.      * That's _really_ odd. Usually NULL ->open means "nothing special",
  12.      * not "no device". Oh, well...
  13.      */
  14.     if (!new_fops->open) {
  15.         fops_put(new_fops);
  16.         return -ENODEV;
  17.     }
  18.     old_fops = file->f_op;
  19.     file->f_op = new_fops;// handler中的fops换掉当前的fops
  20.     err = new_fops->open(inode, file);//调用新的fops中的open函数
  21.     if (err) {
  22.         fops_put(file->f_op);
  23.         file->f_op = fops_get(old_fops);
  24.     }
  25.     fops_put(old_fops);
  26.     return err;
  27. }

       通input_tablehandlerfops重新获取,那么来分析下input_table是怎么实现的,这个主要是通过iinput_register_handler来注册一个事件处理器。该函数主要是对于每个注册的handler进行保存 input_table[handler->minor >> 5] = handler;

点击(此处)折叠或打开

  1. int input_register_handler(struct input_handler *handler)
  2. {
  3.     struct input_dev *dev;

  4.     INIT_LIST_HEAD(&handler->h_list);

  5.     if (handler->fops != NULL) {
  6.         if (input_table[handler->minor >> 5])
  7.             return -EBUSY;
  8.   input_table[handler->minor >> 5] = handler;//连接到input_handler_list链表
  9. // 因为每个handler都会处理最大32个input_dev,所以要以minor的32为倍数对齐,这个minor是传进来的handler的MINOR_BASE        
  10. }
  11.     list_add_tail(&handler->node, &input_handler_list);//连接到input_handler_list链表
  12.     list_for_each_entry(dev, &input_dev_list, node)
  13.         input_attach_handler(dev, handler);//     handlerdev进行配对,不过这次主要是遍历input_dev


  14.     input_wakeup_procfs_readers();
  15.     return 0;
  16. }

其上主要是完成加handler加入到链表,同时完成handler与dev的匹配。下面看看input_attach_handler的处理过程。

点击(此处)折叠或打开

  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.     if (handler->blacklist && input_match_device(handler->blacklist, dev))
  6.         return -ENODEV;
  7.       //blacklist是handler因该忽略的input设备类型,如果应该忽略的input设备也配对上了,那就出错了   
  8.     id = input_match_device(handler->id_table, dev);//其主要是配对handler->id_tabledev
  9.     if (!id)
  10.         return -ENODEV;

  11.     error = handler->connect(handler, dev, id);
  12.     if (error && error != -ENODEV)
  13.         printk(KERN_ERR
  14.             "input: failed to attach handler %s to device %s, "
  15.             "error: %d\n",
  16.             handler->name, kobject_name(&dev->cdev.kobj), error);

  17.     return error;
  18. }
      从上可以看出,其主要是配对handler->id_tabledev是否有mattch,如果配对成功,则调用handlerconnect函数。

点击(此处)折叠或打开

  1. static const struct input_device_id *input_match_device(const struct input_device_id *id,
  2.                             struct input_dev *dev)
  3. {
  4.     int i;

  5.     for (; id->flags || id->driver_info; id++) {

  6.         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
  7.             if (id->bustype != dev->id.bustype)
  8.                 continue;

  9.         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
  10.             if (id->vendor != dev->id.vendor)
  11.                 continue;

  12.         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
  13.             if (id->product != dev->id.product)
  14.                 continue;

  15.         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
  16.             if (id->version != dev->id.version)
  17.                 continue;

  18.         MATCH_BIT(evbit, EV_MAX);
  19.         MATCH_BIT(keybit, KEY_MAX);
  20.         MATCH_BIT(relbit, REL_MAX);
  21.         MATCH_BIT(absbit, ABS_MAX);
  22.         MATCH_BIT(mscbit, MSC_MAX);
  23.         MATCH_BIT(ledbit, LED_MAX);
  24.         MATCH_BIT(sndbit, SND_MAX);
  25.         MATCH_BIT(ffbit, FF_MAX);
  26.         MATCH_BIT(swbit, SW_MAX);

  27.         return id;
  28.     }

  29.     return NULL;
  30. }
       比较input_dev中的idhandler支持的id,这个存放在handlerid_table中,首先看id->driver_info是否有设置,如果设置了,表明他匹配所有的id,例如evdev。然后在根据id->flag来比较,如果成功进入MATCH_BIT,这个宏用来进行按位比较,比较所支持的事件类型。

点击(此处)折叠或打开

  1. #define MATCH_BIT(bit, max) \
  2.         for (i = 0; i < NBITS(max); i++) \
  3.             if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
  4.                 break; \
  5.         if (i != NBITS(max)) \
  6.             continue;

点击(此处)折叠或打开

  1. struct input_device_id {

  2.     kernel_ulong_t flags;

  3.     __u16 bustype;
  4.     __u16 vendor;
  5.     __u16 product;
  6.     __u16 version;

  7.     kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
  8.     kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
  9.     kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
  10.     kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
  11.     kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
  12.     kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
  13.     kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
  14.     kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
  15.     kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

  16.     kernel_ulong_t driver_info;
  17. };
      由此看出,在id->flag中第一了要匹配的项,定义INPUT_DEVICE_ID_MATCH_BUS,则要比较input_device和input_handler的总线类型。其他分别为设备厂商,设备号和设备版本。如果匹配成功,就会调用handlerconnect函数。对于不同的connect函数处理的方式不同,下面以evdev为例

点击(此处)折叠或打开

  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.     struct class_device *cdev;
  6.     dev_t devt;
  7.     int minor;
  8.     int error;
  9. //说明evdev这个handler可以同时有32个输入设备与他配对,evdev_table中以minor存放evdev结构体。
  10.     for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
  11.     if (minor == EVDEV_MINORS) {
  12.         printk(KERN_ERR "evdev: no more free evdev devices\n");
  13.         return -ENFILE;
  14.     }

  15.  //分配一个evdev结构体,这个结构体是evdev事件处理器特有
  16.    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  17.     if (!evdev)
  18.         return -ENOMEM;

  19.     INIT_LIST_HEAD(&evdev->client_list);
  20.     init_waitqueue_head(&evdev->wait);
  21. //以上主要是注册一个evdev结构体的handle,然后初始化,连接到input_handlerinput_dev      
  22.     evdev->exist = 1;
  23.     evdev->minor = minor;
  24.     evdev->handle.dev = dev;
  25.     evdev->handle.name = evdev->name;
  26.     evdev->handle.handler = handler;
  27.     evdev->handle.private = evdev;
  28.     sprintf(evdev->name, "event%d", minor);

  29.     evdev_table[minor] = evdev;

  30.     devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),

  31.     cdev = class_device_create(&input_class, &dev->cdev, devt,
  32.                  dev->cdev.dev, evdev->name);   //注册handle结构体
  33.     if (IS_ERR(cdev)) {
  34.         error = PTR_ERR(cdev);
  35.         goto err_free_evdev;
  36.     }

  37.     /* temporary symlink to keep userspace happy */
  38.     error = sysfs_create_link(&input_class.subsys.kobj,
  39.                  &cdev->kobj, evdev->name);
  40.     if (error)
  41.         goto err_cdev_destroy;

  42.     error = input_register_handle(&evdev->handle);
  43.     if (error)
  44.         goto err_remove_link;

  45.     return 0;

  46.  err_remove_link:
  47.     sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
  48.  err_cdev_destroy:
  49.     class_device_destroy(&input_class, devt);
  50.  err_free_evdev:
  51.     kfree(evdev);
  52.     evdev_table[minor] = NULL;
  53.     return error;
  54. }

       函数主要做的都是一些善后的工作,分配一个evdev结构体,并初始化相关成员,evdev结构体中也有input_handle结构,并初始化并注册。那么看看这个handle的实现。

点击(此处)折叠或打开

  1. int input_register_handle(struct input_handle *handle)
  2. {
  3.     struct input_handler *handler = handle->handler;

  4.     list_add_tail(&handle->d_node, &handle->dev->h_list);
  5.     list_add_tail(&handle->h_node, &handler->h_list);

  6.     if (handler->start)
  7.         handler->start(handle);

  8.     return 0;
  9. }

        这个函数只做了两个工作,把一个handle结构体通过d_node链表项,分别连接到input_devh_listinput_handlerh_list上,以后就可以通过h_list就可以遍历相关的input_handle了。input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev input_handler h_list上了。通过input_dev input_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。通过input_handle也可以找到input_devinput_handler。那么input_dev怎么注册到链表中呢?

点击(此处)折叠或打开

  1. int input_register_device(struct input_dev *dev)
  2. {
  3.     static atomic_t input_no = ATOMIC_INIT(0);
  4.     struct input_handler *handler;
  5.     const char *path;
  6.     int error;

  7.     set_bit(EV_SYN, dev->evbit);  //EN_SYN 这个是设备都要支持的事件类型 


  8.     /*
  9.      * If delay and period are pre-set by the driver, then autorepeating
  10.      * is handled by the driver itself and we don't do it in input.c.
  11.      */

  12.     init_timer(&dev->timer);// 这个内核定时器是为了重复按键而设置的 
  13.     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
  14.         dev->timer.data = (long) dev;
  15.         dev->timer.function = input_repeat_key;
  16.         dev->rep[REP_DELAY] = 250;
  17.         dev->rep[REP_PERIOD] = 33;//如果没有定义有关重复按键的相关值,就用内核默认的  
  18.     }

  19.     if (!dev->getkeycode)
  20.         dev->getkeycode = input_default_getkeycode;

  21.     if (!dev->setkeycode)
  22.         dev->setkeycode = input_default_setkeycode;
  23. //以上设置的默认函数由input心提供 
  24.     list_add_tail(&dev->node, &input_dev_list); // 将新分配的input设备连接到input_dev_list   
  25.     snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
  26.          "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

  27.     if (!dev->cdev.dev)
  28.         dev->cdev.dev = dev->dev.parent;

  29.     error = class_device_add(&dev->cdev);//添加设备节点
  30.     if (error)
  31.         return error;

  32.     path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
  33.     printk(KERN_INFO "input: %s as %s\n",
  34.         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
  35.     kfree(path);
、、 / /遍历input_handler_list链表,配对 input_dev  input_handler   
  1.     list_for_each_entry(handler, &input_handler_list, node)
  2.         input_attach_handler(dev, handler);

  3.     input_wakeup_procfs_readers();

  4.     return 0;
  5. }

   input_register_device完成的主要功能就是:初始化一些默认的值,将自己的device结构添加到linux设备模型当中,将input_dev添加到input_dev_list链表中,然后寻找合适的handlerinput_handler配对,配对的核心函数是input_attach_handler,而该函数在input_register_handler中已经分析过了。

   下面总结下上面的分析过程,如下图:

in    input_dev 是硬件驱动层,代表一个input设备, 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。input_handler 是事件处理层,代表一个事件处理器, nput_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作。input_handle 个人认为属于核心层,代表一个配对的input设备与input事件处理器    

下面看看其中比较重要的数据结构input_handle

点击(此处)折叠或打开

  1. struct input_handle {
  2. //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。   
  3.     void *private;

  4.     int open;
  5.     const char *name;

  6.     struct input_dev *dev;
  7.     struct input_handler *handler;

  8.     struct list_head    d_node;   //input_handle通过d_node连接到了input_dev上的h_list链表上
  9.     struct list_head    h_node; //input_handle通过h_node连接到了input_handlerh_list链表上
  10. };
     那么写输入子系统的驱动程序方法:
 1.分配一个input_dev结构体(input_allocate_device)
 2.设置 包括设置是哪类事件等
 3.注册  input_register_device
 4.硬件相关的初始化

阅读(297) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~