Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43146
  • 博文数量: 9
  • 博客积分: 155
  • 博客等级: 入伍新兵
  • 技术积分: 661
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-09 22:40
个人简介

技术无止境--trouble is a friend

文章分类

全部博文(9)

文章存档

2013年(6)

2012年(3)

我的朋友

分类: LINUX

2013-08-15 18:20:12

     input子系统即linux提供的输入子系统,给类似于触摸屏这类设备提供交互通信之用。简单来说,对于像触摸屏这类设备想上报事件的话,通过input就可以实现driver到app的通信。同时,它也支持很多种不同的事件。
     一般我们在具体的driver中如何使用input?通常都是在probe中用input_allocate_device()函数分配一个struct input_dev *dev类型的设备结构体dev=input_allocate_device(),同时input_allocate_device()完成input_dev的初始化并返回一个指向input_dev类型的指针(input_dev结构代表一个输入设备,其包含了输入设备的一些相关信息,如设备支持的按键类型、设备的名称、设备支持的事件)。再下面就是input的注册了,我们用input_register_device(),此函数就是将我们上面的input_dev注册到input核心中。如果注册失败,用input_free_device()来free。如果注册成功,在卸载的时候用input_unregister_device()。那么input_register_device()究竟实现了什么,看名字我们知道是注册input,那它里面都干了些什么呢?我们来看看:

点击(此处)折叠或打开

  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.  
  8.          __set_bit(EV_SYN, dev->evbit);
  9.  
  10.          /*
  11.           * If delay and period are pre-set by the driver, then autorepeating
  12.           * is handled by the driver itself and we don't do it in input.c.
  13.           */
  14.  
  15.          init_timer(&dev->timer);
  16.          if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
  17.                    dev->timer.data = (long) dev;
  18.                    dev->timer.function = input_repeat_key;
  19.                    dev->rep[REP_DELAY] = 250;
  20.                    dev->rep[REP_PERIOD] = 33;
  21.          }
  22.  
  23.          if (!dev->getkeycode)
  24.                    dev->getkeycode = input_default_getkeycode;
  25.  
  26.          if (!dev->setkeycode)
  27.                    dev->setkeycode = input_default_setkeycode;
  28.  
  29.          snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
  30.                     "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
  31.  
  32.          error = device_add(&dev->dev);
  33.          if (error)
  34.                    return error;
  35.  
  36.          path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
  37.          printk(KERN_INFO "input: %s as %s\n",
  38.                    dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
  39.          kfree(path);
  40.  
  41.          error = mutex_lock_interruptible(&input_mutex);
  42.          if (error) {
  43.                    device_del(&dev->dev);
  44.                    return error;
  45.          }
  46.  
  47.          list_add_tail(&dev->node, &input_dev_list);
  48.  
  49.          list_for_each_entry(handler, &input_handler_list, node)
  50.                    input_attach_handler(dev, handler);
  51.  
  52.          input_wakeup_procfs_readers();
  53.  
  54.          mutex_unlock(&input_mutex);
  55.  
  56.          return 0;
  57. }
8行:__set_bit(EV_SYN, dev->evbit);设置input_dev支持的事件类型,这里由input_dev的evbit表示,其支持的事件类型如下所示:

点击(此处)折叠或打开

  1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/
  2. #define EV_KEY 0x01 /*键盘或者按键,表示一个键码*/
  3. #define EV_REL 0x02 /*鼠标设备,表示一个相对的光标位置结果*/
  4. #define EV_ABS 0x03 /*手写板产生的值,其是一个绝对整数值*/
  5. #define EV_MSC 0x04 /*其他类型*/
  6. #define EV_LED 0x11 /*LED灯设备*/
  7. #define EV_SND 0x12 /*蜂鸣器,输入声音*/
  8. #define EV_REP 0x14 /*允许重复按键类型*/
  9. #define EV_PWR 0x16 /*电源管理事件*/
15-21行:初始化一个timer,用来处理重复延迟的,rep[REP_DELAY]与rep[REP_PERIOD]一般使用默认值,为了自动处理重复按键设置的。
23-27行:判断getkeycode跟setkeycode是否定义,若没有则使用赋默处理函数。input_default_getkeycode是用来获得指定位置的键值,input_default_setkeycode是设置键值。
29-30行:填充input_dev的name,其name以input0-x形式出现在sysfs下面。
32-34行:device_add将input_dev包含的device结构添加到device核心(注册到linux的设备模型中),同时在sysfs文件系统中表现出来。
36-39行:print设备的具体的路径。
41-45、54行:获取互斥题即释放。
47-52行:list_add_tail(&dev->node, &input_dev_list)将input_dev加到input_dev_list链表中,在input_dev_list链表中包含了系统中所有的input_dev设备。list_for_each_entry(handler, &input_handler_list, node)遍历整个input_handler_list链表,找到一个handler的id_table跟input_dev的id一致的handler结构,将此handler跟input_dev结构联系起来。
     这里关于input_attach_handler()函数的代码不贴出来了,只简单说说,有兴趣的朋友可以跟踪源码查看,在这个函数里,主要就是handler跟input_dev的匹配。首先判断handler中的blacklist是否赋值,然后判断handler的blacklist跟input_dev的id是否匹配(input_match_device(handler->blacklist, dev)),这里关于blacklist有必要说下,它是一个input_device_id*的类型,它指向input_device_id的一个表,这个表中放的是driver忽略的一些设备。如果没有匹配下面则调用input_match_device(handler->id_table, dev)来匹配handler->id_table跟dev->id的数据,其中handler->id_table也是一个input_device_id类型的指针,保存着driver所支持的设备。最后一旦匹配成功,则调用handler->connect,此涵素将dev跟handler联系起来。
     到这里作个小总结,这里注册一个input device,其实就是为此input设备设置一些默认值,同时将这些device挂载到input_dev_list中,然后与挂载在input_handler_list中的handler进行匹配,若匹配成功,则调用handler->connect(),将二者相连。这里同时注意下,handler的connect函数其实调用的是evdev_handler的evdev_connect。evdev_handler跟evdev_connect在evdev.c中有描述。
     上面主要谈得是input_dev,其中已经多次出现handler,关于handler的注册是linux自己初始化好的,我们只需要了解它的角色用处。开机时在evdev_init里自动调用input_register_handle,那就看看。一般在子系统初始化后(input.c中加载具体设备驱动之前先input_init),经过注册input类、在proc下建立交互文件、注册cdev之后,会调用open,而这里的oen最后会关联到对应的handler的open,handler这时隆重登场。

点击(此处)折叠或打开

  1. int input_register_handler(struct input_handler *handler)
  2. {
  3.          struct input_dev *dev;
  4.          int retval;
  5.  
  6.          retval = mutex_lock_interruptible(&input_mutex);
  7.          if (retval)
  8.                    return retval;
  9.  
  10.          INIT_LIST_HEAD(&handler->h_list);
  11.  
  12.          if (handler->fops != NULL) {
  13.                    if (input_table[handler->minor >> 5]) {
  14.                             retval = -EBUSY;
  15.                             goto out;
  16.                    }
  17.                    input_table[handler->minor >> 5] = handler;
  18.          }
  19.  
  20.          list_add_tail(&handler->node, &input_handler_list);
  21.  
  22.          list_for_each_entry(dev, &input_dev_list, node)
  23.                    input_attach_handler(dev, handler);
  24.  
  25.          input_wakeup_procfs_readers();
  26.  
  27.  out:
  28.          mutex_unlock(&input_mutex);
  29.          return retval;
  30. }
6-8行:对input_mutex加锁,若失败则返回。
10行:初始化h_list链表,此链表连接与本input_handler相联系的下一个下一个handler。
12-18行:判断handler的fops是否可用,再判断input_table是否可用,最后将handler加到input_table中。
20-24行:将handler加到input_handler_list(包含系统所有的input_handler)中去,再遍历input_dev_list(与上面类似,即先加入到各自的链表中,再与另一条链表的对象匹配),将具体的dev与handler进行匹配,若成功则将二者相连。
25行:相关procfs文件系统内容。
    到了这里我们必须面对另外一个结构体了,struct input_handle,前面说那么多,若没有这个桥梁,恐怕都是无用功。input_handle主要用途是用来连接input_dev与input_handler的。我们通过input_register_handle来注册一个新的handle到input子系统中。下面我们看看input_register_handle:

点击(此处)折叠或打开

  1. int input_register_handle(struct input_handle *handle)
  2. {
  3.          struct input_handler *handler = handle->handler;
  4.          struct input_dev *dev = handle->dev;
  5.          int error;
  6.  
  7.          /*
  8.           * We take dev->mutex here to prevent race with
  9.           * input_release_device().
  10.           */
  11.          error = mutex_lock_interruptible(&dev->mutex);
  12.          if (error)
  13.                    return error;
  14.          list_add_tail_rcu(&handle->d_node, &dev->h_list);
  15.          mutex_unlock(&dev->mutex);
  16.          synchronize_rcu();
  17.  
  18.          /*
  19.           * Since we are supposed to be called from ->connect()
  20.           * which is mutually exclusive with ->disconnect()
  21.           * we can't be racing with input_unregister_handle()
  22.           * and so separate lock is not needed here.
  23.           */
  24.          list_add_tail(&handle->h_node, &handler->h_list);
  25.  
  26.          if (handler->start)
  27.                    handler->start(handle);
  28.  
  29.          return 0;
  30. }
3-4行:定义input_dev与input_handler的结构体,同时指向handle。
14、24行:list_add_tail_rcu(&handle->d_node, &dev->h_list)将handle加入input_dev的dev->h_list,list_add_tail(&handle->h_node, &handler->h_list)将handle加入到input_handler的handler->h_list.
26-27行:若start存在,则调用之。
    可以看出input_register_handle就是将handle挂载到input_dev跟input_handler的h_list链表上,在handler的connect中调用之,下面再说。上面的input_dev、input_handle、input_handler三者之间就这样被绑定联系起来。(可能有点迷糊,仔细推敲上面代码,我们发现input_handle的dev指向input_dev,input_handle的的handler指向input_handler,下面的handle的d_node跟handle的h_node分别指向dev的h_list跟handler的h_list,这样就将三者很好的衔接起来)。






阅读(1038) | 评论(3) | 转发(0) |
0

上一篇:cross-2.95交叉编译

下一篇:input子系统分析2

给主人留下些什么吧!~~

sinc_mark2013-08-27 19:36:04

想请教关于输入子系统的应用,怎么从应用连接到驱动啊?
如果有资料,请给出些,谢谢!

csuman2013-08-17 13:53:51

mark.