Chinaunix首页 | 论坛 | 博客
  • 博客访问: 201581
  • 博文数量: 71
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-11 22:28
个人简介

一个懒惰的文艺工程师!

文章分类

全部博文(71)

文章存档

2015年(1)

2014年(7)

2013年(63)

我的朋友

分类: LINUX

2013-11-02 16:27:44

原文地址:linux内核input子系统解析 作者:FBI888XH

Android、X windows、qt等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越倾向于标准的input输入子系统。

因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大。但如果你想更灵活的应用它,就需要好好的分析下input子系统了。

一、input输入子系统框架

下图是input输入子系统框架,输入子系统由输入子系统核心层( Input Core ),驱动层和事件处理层(Event Handler)三部份组成。一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

注意:keyboard.c不会在/dev/input下产生节点,而是作为ttyn终端(不包括串口终端)的输入。

二、Input driver编写要点

1、分配、注册、注销input设备

点击(此处)折叠或打开

  1. struct input_dev *input_allocate_device(void)
  2. int input_register_device(struct input_dev *dev)
  3. void input_unregister_device(struct input_dev *dev)

2、设置input设备支持的事件类型、事件码、事件值的范围、input_id等信息

参见usb键盘驱动:usbkbd.c

点击(此处)折叠或打开

  1. usb_to_input_id(dev, &input_dev->id);//设置bustype、vendo、product等
  2. input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);//支持的事件类型
  3. input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);// EV_LED事件支持的事件码
  4. for (i = 0; i < 255; i++)
  5. set_bit(usb_kbd_keycode[i], input_dev->keybit); //EV_KEY事件支持的事件码

include/linux/input.h中定义了支持的类型(下面列出的是2.6.22内核的情况)

点击(此处)折叠或打开

  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_SW 0x05
  7. #define EV_LED 0x11
  8. #define EV_SND 0x12
  9. #define EV_REP 0x14
  10. #define EV_FF 0x15
  11. #define EV_PWR 0x16
  12. #define EV_FF_STATUS 0x17
  13. #define EV_MAX 0x1f
一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。比如:EV_KEY事件,需要定义其支持哪些按键事件码。

3、如果需要,设置input设备的打开、关闭、写入数据时的处理方法

参见usb键盘驱动:usbkbd.c

点击(此处)折叠或打开

  1. input_dev->open = usb_kbd_open;
  2. input_dev->close = usb_kbd_close;
  3. input_dev->event = usb_kbd_event;

4、在发生输入事件时,向子系统报告事件

用于报告EV_KEY、EV_REL、EV_ABS等事件的函数有:

点击(此处)折叠或打开

  1. void input_report_key(struct input_dev *dev, unsigned int code, int value)
  2. void input_report_rel(struct input_dev *dev, unsigned int code, int value)
  3. void input_report_abs(struct input_dev *dev, unsigned int code, int value)
如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的)

点击(此处)折叠或打开

  1. void input_event(struct input_dev *dev, unsigned int type,
  2.                              unsigned int code, int value)
5、应用程序使用

应用程序可以通过读/dev/event文件来获得input设备的信息(与上一步提交的信息对应),例如:

点击(此处)折叠或打开

  1. int main(void)
  2. {
  3.     int buttons_fd;
  4.     int key_value,i=0,count;

  5.     struct input_event ev_key;
  6.     buttons_fd = open("/dev/event0", O_RDWR);
  7.     if (buttons_fd < 0) {
  8.         perror("open device buttons");
  9.         exit(1);
  10.     }

  11.     for (;;) {
  12.         count = read(buttons_fd,&ev_key,sizeof(struct input_event));
  13.     //    printf("count=%d\n",count);
  14.         for(i=0; i<(int)count/sizeof(struct input_event); i++)
  15.             if(EV_KEY==ev_key.type)
  16.                 printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code-1,ev_key.value);
  17.             if(EV_SYN==ev_key.type)
  18.                 printf("syn event\n\n");

  19.     }

  20.     close(buttons_fd);
  21.     return 0;



三、Event Handler层解析

1、Input输入子系统数据结构关系图

2、input_handler结构体

以evdev.c中的evdev_handler为例:

点击(此处)折叠或打开

  1. static struct input_handler evdev_handler = {
  2.        .event = evdev_event, //向系统报告input事件,系统通过read方法读取
  3.        .connect = evdev_connect, //和input_dev匹配后调用connect构建
  4.        .disconnect = evdev_disconnect,
  5.        .fops = &evdev_fops, //event设备文件的操作方法
  6.        .minor = EVDEV_MINOR_BASE, //次设备号基准值
  7.        .name = "evdev",
  8.        .id_table = evdev_ids, //匹配规则
  9. };
3、input字符设备注册过程
drivers/input/input.c中:

点击(此处)折叠或打开

  1. static int __init input_init(void)
  2. {
  3.       int err;
  4.       err = class_register(&input_class);
  5.       ……
  6.       err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  7.       ……
  8. }
input_fops定义:

点击(此处)折叠或打开

  1. static const struct file_operations input_fops = {
  2.     .owner = THIS_MODULE,
  3.     .open = input_open_file,
  4. };
Input_dev和input_handler匹配后调用input_handler的connect。以evdev_handler为例:

点击(此处)折叠或打开

  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
  2. {
  3.      struct evdev *evdev;
  4.      struct class_device *cdev;
  5.      dev_t devt;
  6.      int minor;
  7.      int error;

  8.      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor];
  9.          minor++);
  10.          if (minor == EVDEV_MINORS) {
  11.              printk(KERN_ERR "evdev: no more free evdev devices\n");
  12.              return -ENFILE;
  13.          }

  14.      evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  15.                  //为每个匹配evdev_handler的设备创建一个evdev。
  16.      if (!evdev)
  17.          return -ENOMEM;

  18.      INIT_LIST_HEAD(&evdev->client_list);
  19.      init_waitqueue_head(&evdev->wait);

  20.      evdev->exist = 1;
  21.      evdev->minor = minor;
  22.      evdev->handle.dev = dev;
  23.      evdev->handle.name = evdev->name;
  24.      evdev->handle.handler = handler;
  25.      evdev->handle.private = evdev;
  26.      sprintf(evdev->name, "event%d", minor);

  27.      evdev_table[minor] = evdev;
  28. //记录evdev的位置,字符设备/dev/input/evnetx访问时根据次设备号及EVDEV_MINOR_BASE最终在evdev_open中找到对应的evdev
  29.               
  30.      devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
  31.      cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);//创建了event字符设备节点
  32.                 ……
  33. }


4、input字符设备的打开过程

点击(此处)折叠或打开

  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_handler
  5.      const struct file_operations *old_fops, *new_fops = NULL;
  6.      int err;
  7.      if (!handler || !(new_fops = fops_get(handler->fops)))
  8.                 //取出对应input_handler的file_operations
  9.           return -ENODEV;
  10.       if (!new_fops->open) {
  11.           fops_put(new_fops);
  12.           return -ENODEV;
  13.       }
  14.       old_fops = file->f_op;
  15.       file->f_op = new_fops;//重定位打开的设备文件的操作方法
  16.       err = new_fops->open(inode, file);
  17.       if (err) {
  18.           fops_put(file->f_op);
  19.           file->f_op = fops_get(old_fops);
  20.       }
  21.       fops_put(old_fops);
  22.       return err;
  23. }

5、input字符设备的其它操作

由于在open阶段已经把设备文件的操作操作方法重定位了到了具体的input_handler,所以其它接口操作(read、write、ioctl等),由各个input_handler的fops方法决定。如evdev.c中的:evdev_fops。

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