我是靠谱客的博主 眼睛大铅笔,最近开发中收集的这篇文章主要介绍input子系统分析一(input设备注册)1 input设备初始化,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1 input设备初始化

我们以一个遥控器的设备为例,分析input设备的注册流程:

1.1 申请input设备

remote_dev = input_allocate_device();

1.2 初始化设备结构体

  remote_dev->name = XXX_NAME;
  remote_dev->phys = XXX_PHYS;
  remote_dev->id.vendor = XXX_VENDOR_ID;
  remote_dev->id.product = XXX_PRODUCT_ID;
  remote_dev->id.version = XXX_CTL_VERSION;

1.3 配置设备支持的事件类型

  set_bit(EV_KEY, remote_dev->evbit);
  set_bit(EV_REP, remote_dev->evbit);

1.4 配置设备支持的按键值

  remote_dev->keycode = xxx_key_tab;
  remote_dev->keycodesize = sizeof(int);
  remote_dev->keycodemax = ARRAY_SIZE(xxx_key_tab);

1.5 注册input设备

input_register_device(remote_dev);

2 input设备的注册流程

2.1 input_register_device函数

根据代码中注释来分析注册流程:

 int input_register_device(struct input_dev *dev)
  {
      struct input_devres *devres = NULL;
      struct input_handler *handler;
      unsigned int packet_size;
      const char *path;
      int error;
  ​
      if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) { // 绝对设备,却没有坐标信息,拒绝注册
          return -EINVAL;
      }
      __set_bit(EV_SYN, dev->evbit);  // 每个输入设备都会生成EV_SYN/SYN_REPORT事件 
      error = device_add(&dev->dev);
      if (error)
          goto err_free_vals;
  ​
      path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
      pr_info("%s as %sn",
          dev->name ? dev->name : "Unspecified device",
          path ? path : "N/A");
      kfree(path);
  ​
      error = mutex_lock_interruptible(&input_mutex);
      if (error)
          goto err_device_del;
  ​
      list_add_tail(&dev->node, &input_dev_list);  // 将设备插入input_dev_list链尾
      list_for_each_entry(handler, &input_handler_list, node) // 遍历所有input事件驱动,匹配设备
          input_attach_handler(dev, handler);  // 下面细说
      input_wakeup_procfs_readers();  // poll事件,这里不细说
      mutex_unlock(&input_mutex);
  }

2.2 input设备匹配input handler的过程

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
  {
      const struct input_device_id *id;
      int error;
  ​
      id = input_match_device(handler, dev);
      if (!id)
          return -ENODEV;
  ​
      error = handler->connect(handler, dev, id);
      if (error && error != -ENODEV)
          pr_err("failed to attach handler %s to device %s, error: %dn",
                 handler->name, kobject_name(&dev->dev.kobj), error);
  ​
      return error;
  }

好,划重点,我们来好好分析下输入子系统的匹配过程!

  1. 事实上,当每一个 dev 和 handler 注册到内核进来,都会调用到 input_attach_handler 函数进行对比匹配。

    当Input dev注册时,遍历所有handler,调用input_attach_handler匹配。

    当handler注册时,遍历所有Input dev,调用input_attach_handler匹配。

  2. 在遍历的过程中,即使有一个匹配成功了,也会继续匹配下去。

    所以Input dev和handler是多对多的关系。

    举例:学习时用的按键输入子系统驱动实验,会发现按键上报的数据可以从tty1 中 cat 到,也可以从 eventx 中 cat 到。

  3. 真正进行匹配的是handler->id_table 和 dev。

下面我们分析下内核中常见的几种handler和我们的remote_dev匹配的过程:

2.2.1 remote_dev与evdev handler匹配

evdev.c -> handler -> id_table

static const struct input_device_id evbug_ids[] = {
      { .driver_info = 1 },   /* Matches all devices */
      { },                   /* Terminating zero entry */
  };

remote_dev->evbit

set_bit(EV_KEY, remote_dev->evbit); // 按键事件
set_bit(EV_REP, remote_dev->evbit); // 重复事件

匹配函数input_match_device_id

bool input_match_device_id(const struct input_dev *dev,
                 const struct input_device_id *id)
  {
      if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
          if (id->bustype != dev->id.bustype)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
          if (id->vendor != dev->id.vendor)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
          if (id->product != dev->id.product)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
          if (id->version != dev->id.version)
              return false;
      /* __bitmap_subset(A, B): 判断A位图是否是B位图的子集*/
      if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
          !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
          !bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
          !bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
          !bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
          !bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
          !bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
          !bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
          !bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
          !bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
          return false;
      }
      return true;
  }

evdev.c -> handler -> id_table除了driver_info都为空,所以以上if判断中都进不去,程序直接跳到28行返回真。

这也说明了"evdev事件驱动可以适用于任何的 input_dev."

2.2.2 remote_dev与keyboard handler匹配

我们继续来看keyboard handler

keyboard -> handler -> id_table

bool input_match_device_id(const struct input_dev *dev,
                 const struct input_device_id *id)
  {
      if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
          if (id->bustype != dev->id.bustype)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
          if (id->vendor != dev->id.vendor)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
          if (id->product != dev->id.product)
              return false;
      if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
          if (id->version != dev->id.version)
              return false;
      /* __bitmap_subset(A, B): 判断A位图是否是B位图的子集*/
      if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) ||
          !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) ||
          !bitmap_subset(id->relbit, dev->relbit, REL_MAX) ||
          !bitmap_subset(id->absbit, dev->absbit, ABS_MAX) ||
          !bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) ||
          !bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) ||
          !bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) ||
          !bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) ||
          !bitmap_subset(id->swbit, dev->swbit, SW_MAX) ||
          !bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) {
          return false;
      }
      return true;
  }

remote_dev->evbit

  set_bit(EV_KEY, remote_dev->evbit); // 按键事件
  set_bit(EV_REP, remote_dev->evbit); // 重复事件

keyboard handler的flag=INPUT_DEVICE_ID_MATCH_EVBIT,所以input_match_device_id中前4条if条件也不成立。然后evbit位图中置位了EV_KEY和EV_SND. 而remote_dev的evbit位图置位了EV_KEY和EV_REP.

显然,keyboard handler->evbit是remote_dev->evbit的子集,说明keyboard驱动支持我们的remote_dev

2.2.3 remote_dev与mousedev_handler handler匹配

mousedev -> handler -> id_table

static const struct input_device_id mousedev_ids[] = {
      {
          .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
                  INPUT_DEVICE_ID_MATCH_KEYBIT |
                  INPUT_DEVICE_ID_MATCH_RELBIT,
          .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
          .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
          .relbit = { BIT_MASK(REL_X) | BIT_MASK(REL_Y) },
      },  /* A mouse like device, at least one button,
             two relative axes */
      {
          .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
                  INPUT_DEVICE_ID_MATCH_RELBIT,
          .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) },
          .relbit = { BIT_MASK(REL_WHEEL) },
      },  /* A separate scrollwheel */
      {
          .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
                  INPUT_DEVICE_ID_MATCH_KEYBIT |
                  INPUT_DEVICE_ID_MATCH_ABSBIT,
          .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
          .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
          .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
      },  /* A tablet like device, at least touch detection,
             two absolute axes */
      {
          .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
                  INPUT_DEVICE_ID_MATCH_KEYBIT |
                  INPUT_DEVICE_ID_MATCH_ABSBIT,
          .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
          .keybit = { [BIT_WORD(BTN_TOOL_FINGER)] =
                  BIT_MASK(BTN_TOOL_FINGER) },
          .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
                  BIT_MASK(ABS_PRESSURE) |
                  BIT_MASK(ABS_TOOL_WIDTH) },
      },  /* A touchpad */
      {
          .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
              INPUT_DEVICE_ID_MATCH_KEYBIT |
              INPUT_DEVICE_ID_MATCH_ABSBIT,
          .evbit = { BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) },
          .keybit = { [BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) },
          .absbit = { BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
      },  /* Mouse-like device with absolute X and Y but ordinary
             clicks, like hp ILO2 High Performance mouse */
  ​
      { },    /* Terminating entry */
  };

支持以上事件的就会和 mousedev事件驱动匹配,这里不再细说。

2.3 Input设备与handler建立连接

dev和handler匹配成功后,会调用handler->connect(handler, dev, id);

2.3.1 evdev事件驱动

如果是evdev事件驱动,会调用evdev_connect接口:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
               const struct input_device_id *id)
  {
      struct evdev *evdev;
      int minor;
      int dev_no;
      int error;
  ​
      minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
      if (minor < 0) {
          error = minor;
          pr_err("failed to reserve new minor: %dn", error);
          return error;
      }
  ​
      evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
      if (!evdev) {
          error = -ENOMEM;
          goto err_free_minor;
      }
  ​
      INIT_LIST_HEAD(&evdev->client_list);
      spin_lock_init(&evdev->client_lock);
      mutex_init(&evdev->mutex);
      evdev->exist = true;
  ​
      dev_no = minor;
      /* Normalize device number if it falls into legacy range */
      if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
          dev_no -= EVDEV_MINOR_BASE;
      dev_set_name(&evdev->dev, "event%d", dev_no);
  ​
      evdev->handle.dev = input_get_device(dev);
      evdev->handle.name = dev_name(&evdev->dev);
      evdev->handle.handler = handler;
      evdev->handle.private = evdev;
  ​
      evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
      evdev->dev.class = &input_class;
      evdev->dev.parent = &dev->dev;
      evdev->dev.release = evdev_free;
      device_initialize(&evdev->dev);
  ​
      error = input_register_handle(&evdev->handle);
      if (error)
          goto err_free_evdev;
  ​
      cdev_init(&evdev->cdev, &evdev_fops);
  ​
      error = cdev_device_add(&evdev->cdev, &evdev->dev);
      if (error)
          goto err_cleanup_evdev;
  ​
      return 0;
  ​
   err_cleanup_evdev:
      evdev_cleanup(evdev);
      input_unregister_handle(&evdev->handle);
   err_free_evdev:
      put_device(&evdev->dev);
   err_free_minor:
      input_free_minor(minor);
      return error;
  }

我们来重点分析下evdev_connect做了哪些事情:

step1:创建一个evdev设备,分配一个次设备号,该设备号决定了/dev/input/eventX中的X的值。

step2:初始化evdev->handle,这个handle不是evdev->handler,它们两有完全不同的用途。

evdev->handle用来绑定匹配成功的Input_dev和evdev->handler

step3:将evdev->handle注册进内核. 见44行。

事实上就是将evdev->handle挂在了input_dev_list和input_handler_list的h_list上。从此,建立好了三者 的铁三角关系,通过input_handler_list和input_dev_list以及input_hande中任何一方,都可以找到彼此。

借用大神的一张图,直观的感受下这个三角关系:

step4:将step1创建的evdev设备注册到内核,见48-50行。app访问我们的input_dev设备就是通过这个新建的evdev设备节点来访问的。

3 总结

        回到我们的remote_dev,至此remote_dev的注册,匹配,连接过程就完成了。设备/dev/input/eventX被创建,app在访问remote_dev时,我们最终就能够通过基于evdev->fops的open、read、write等方式在应用层获取数据.

  1. evdev事件驱动可以支持任何类型的dev,并且会在/dev/input/eventX下创建eventX设备节点。

  2. 如果dev支持按键类事件,会匹配keyboard事件驱动,会将数据上送到tty层。

  3. 如果 dev 支持鼠标的一些事件,会匹配 mousedev的事件驱动,具体还会要分析id_table,并且会创建杂项设备节点。

4 遗留问题

        至此,我们已经分析完了遥控器设备的注册流程,回过头想想,其实还有很多问题没有解决,比如:

  1. 遥控器数据是如何上报的?app怎样高效得到遥控器上报的数据?app怎么辨别用户按下的是什么键?
  2. app open一个input节点,最终怎么操作到我们remote设备?read和write又是如何访问设备的?
  3. evdev匹配这很多input_dev,它是如何区分这些设备的?如何管理这个设备的数据?

至于app如何访问input_dev的这些细节,这个也比较复杂,下回剖析~

最后

以上就是眼睛大铅笔为你收集整理的input子系统分析一(input设备注册)1 input设备初始化的全部内容,希望文章能够帮你解决input子系统分析一(input设备注册)1 input设备初始化所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部