Linux driver之input framework

摘要:

  • input framework
  • input_dev与handler匹配
  • input_dev注册
  • handler之evdev
  • 总结

input framework

  • framework一般有两个目的:

    • 一方面向开发者提供统一的接口(API);
    • 另一方面是向实现功能的模块提供接口,将功能挂接框架里。最终实现通过API来使用系统功能。
  • linux input驱动框架原理:

    • input以input_dev_list和input_handler_list为核心的两个链表:
      驱动开发者通过input_register_device()将struct input_dev(一般每个设备会以这个类型的结构体变量注册到input框架里)核心结构体类型的变量挂入input_dev_list链表中。
      通过input_register_handler()将struct input_handler结构体变量挂入到input_handler_list链表中。
      input的input_dev_list和input_handler_list两个链表定义在drivers/input/input.c里面:

      static LIST_HEAD(input_dev_list);
      static LIST_HEAD(input_handler_list);
    • 我们将input_dev结构体类型的变量叫着input device,将input_handler叫着input handler。可以理解成device是面向设备,将输入设备抽象成input_dev结构体注册到input框架里面;而handler是面向上层应用提供接口,每个handler提供的是一种类型接口,不同handler提供不同的接口(linux一般默认的都是evdev(一个handler),在drivers/input/evdev.c)。

    • 调用input_register_device()注册device到input框架时,在input_register_device()函数里面按照匹配规则(下面会单独讲述匹配规则)将本次要注册的device和链表input_handler_list的handler一一匹配,如果匹配成功,会调用handler里面的connect指向的函数,在connect调用中会向上层应用创建接口(一般都是字符设备接口,evdev创建的是字符设备,有了字符设备上层就可以通过文件节点的形式访问了)
      input_register_device()函数里面匹配的部分的代码片段:

      ist_add_tail(&dev->node, &input_dev_list);//device加入到链表
      
      //匹配handler
      list_for_each_entry(handler, &input_handler_list, node)
          input_attach_handler(dev, handler);

      input_attach_handler()调用匹配函数input_match_device(),如匹配成功就调用connect。input_attach_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: %d\n",
             handler->name, kobject_name(&dev->dev.kobj), error);
      
          return error;
      }
    • handler也是可以自定义,通过input_register_handler()将自己定义的handler注册到input框架里面(evdev就是很好的一个例子,evdev是linux默认的handler,一般我们都不自己定义handler,都是用evdev,默认匹配规则,基本上都能匹配上evdev),handler里面是带匹配函数的:

      bool (*match)(struct input_handler *handler, struct input_dev *dev);
      

      在调用input_register_handler()注册时,同样也会与input_dev_list链表里面的每一个device匹配,优先使用handler自带的匹配函数(match),如果handler的match为NULL,则默认使用input里面自带的匹配规则匹配。input_register_handler()函数里面的代码片段:

      INIT_LIST_HEAD(&handler->h_list);
      
      list_add_tail(&handler->node, &input_handler_list);
      
      list_for_each_entry(dev, &input_dev_list, node)
          input_attach_handler(dev, handler);
    • device和handler如果匹配成功,会调用handler里面的connect,connect负责向上创建接口,同时connect还需要向input框架里面(调用input_register_handle()函数)注册handle(注意与handler是不同的),evdev的connect代码片段:

      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;
      
      error = input_register_handle(&evdev->handle);
      if (error)
          goto err_free_evdev;
      
      cdev_init(&evdev->cdev, &evdev_fops);
      evdev->cdev.kobj.parent = &evdev->dev.kobj;
      error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
      if (error)
          goto err_unregister_handle;
      
      error = device_add(&evdev->dev);
      if (error)
          goto err_cleanup_evdev;

      从上面的代码,evdev的connect注册了handle(input device和handler匹配成功后,需要建立一个handle来将device和handler连接起来,当调用input_event上报时,就可以从device找到handler(调用过程input_event()->input_handle_event()->input_pass_values(),在input_pass_values里面会通过handle找到handler),再通过handler的event处理上报的输入值到上层),也创建了字符设备(字符设备,就是以文件节点的形式向上层提供接口)

    • 当input_register_dev()注册的输入设备(input_dev)有输入值时,需要调用input框架提供的input_event()向上层上报输入值,输入值上报是通过handler的event上报,输入值类型:

      struct input_value {
          __u16 type;
          __u16 code;
          __s32 value;
      };

      input_event()函数定义,如下:

      void input_event(struct input_dev *dev,
       unsigned int type, unsigned int code, int value)
      {
          unsigned long flags;
      
          if (is_event_supported(type, dev->evbit, EV_MAX)) {
      
              spin_lock_irqsave(&dev->event_lock, flags);
              input_handle_event(dev, type, code, value);
              spin_unlock_irqrestore(&dev->event_lock, flags);
          }
      }

      其中函数调用关系:
      input_event()->input_handle_event()->input_pass_values()->input_to_handler()
      input_pass_values()函数定义,如下:

      static void input_pass_values(struct input_dev *dev,
                struct input_value *vals, unsigned int count)
      {
          struct input_handle *handle;
          struct input_value *v;
      
          if (!count)
              return;
      
          rcu_read_lock();
      
          handle = rcu_dereference(dev->grab);
          if (handle) {
              count = input_to_handler(handle, vals, count);
          } else {
              list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                  if (handle->open)
                      count = input_to_handler(handle, vals, count);
          }
      
          rcu_read_unlock();
      
          add_input_randomness(vals->type, vals->code, vals->value);
      
          /* trigger auto repeat for key events */
          for (v = vals; v != vals + count; v++) {
              if (v->type == EV_KEY && v->value != 2) {
                  if (v->value)
                      input_start_autorepeat(dev, v->code);
                  else
                      input_stop_autorepeat(dev);
              }
          }
      }
      

      从这可以到从input_dev找到handle(连接input device 和handler的结构)。一般情况下,是执行的以下部分code:

              list_for_each_entry_rcu(handle, &dev->h_list, d_node)
                  if (handle->open)
                      count = input_to_handler(handle, vals, count);

      在函数input_to_handler()里面,通过handle找到handler,函数定义:

      static unsigned int input_to_handler(struct input_handle *handle,
              struct input_value *vals, unsigned int count)   
      {
          struct input_handler *handler = handle->handler;
          struct input_value *end = vals;
          struct input_value *v;
      
          for (v = vals; v != vals + count; v++) {
              if (handler->filter &&
                  handler->filter(handle, v->type, v->code, v->value))
                  continue;
              if (end != v)
                  *end = *v;
              end++;
          }
      
          count = end - vals;
          if (!count)
              return 0;
      
          if (handler->events)
              handler->events(handle, vals, count);
          else if (handler->event)
              for (v = vals; v != end; v++)
                  handler->event(handle, v->type, v->code, v->value);
      
          return count;
      }

      调用过程看得出,handle是input device和handler的联合剂作用。也可以看到调用了handler的event,最终input device输入值到上层是通过handler的event处理。

  • 框架原理总结:
    input框架,总的来说有三个大接口:

    • 第一个,向设备提供device(struct input_dev),以及注册函数input_register_device(),同时还有些对input_dev结构体变量操作的函数(后面会单独描述)。
    • 第二个,将应用层接口挂入到input框架里面的handler,提供了注册handler的input_register_handler()函数,同时也提供了input device和handler的联合剂handle,以及handle的注册函数input_register_handle(),这个注册函数一般在connect里面调用,当然也可以在其他地方调用来注册,只要时机选择正确。handler的connect(一个开发者自己实现的函数)创建应用层接口,event(开发者自己实现的接口)负责处理上报。
    • 第三个,提供了input_event()上报函数,在注册设备的device有数据时,需要此函数来上报。当然,input将input_event封装出了其他接口函数,使用更方便,下面会单独描述。

input_dev和handler匹配

input提供的匹配函数input_match_device()定义如下:

static const struct input_device_id *input_match_device(struct input_handler *handler,
                            struct input_dev *dev)
{
    const struct input_device_id *id;

    for (id = handler->id_table; id->flags || id->driver_info; id++) {

        /*输入设备所属总线类型检测*/
        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
            if (id->bustype != dev->id.bustype)
                continue;
        /*输入设备供应商检测*/
        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
            if (id->vendor != dev->id.vendor)
                continue;
        /*输入设备厂商检测*/
        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
            if (id->product != dev->id.product)
                continue;
        /*输入设备版本检测*/
        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
            if (id->version != dev->id.version)
                continue;
        /*输入设备支持的事件类型检测*/
        if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
            continue;
        /*输入设备支持按键类型具体按键的支持检测*/
        if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
            continue;
        /*输入设备支持相对坐标事件类型检测*/
        if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
            continue;
        /*输入设备支持绝对坐标事件类型检测*/
        if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
            continue;

        if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
            continue;

        if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
            continue;

        if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
            continue;

        if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
            continue;

        if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
            continue;

        if (!handler->match || handler->match(handler, dev))
            return id;
    }

    return NULL;
}

可以看到handler->id_table提供了一个handler支持的设备表struct input_device_id:

struct input_device_id {

    kernel_ulong_t flags;

    __u16 bustype;
    __u16 vendor;
    __u16 product;
    __u16 version;

    kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

    kernel_ulong_t driver_info;
};
  • 第一部份,flags代表是否需要检测设备四个属性:bustype(输入设备所属总线类型),vendor(输入设备供应商),product(输入设备厂商),version(设备的版本)。如果提供的handler需要检测这些属性,需要将flags的相应位置位,如果需要检测的属性与handler的值不匹配,则不能连接此handler来处理输入的值。位定义如下(include/linux/mod_devicetable.h):

        #define INPUT_DEVICE_ID_MATCH_BUS   1
        #define INPUT_DEVICE_ID_MATCH_VENDOR    2
        #define INPUT_DEVICE_ID_MATCH_PRODUCT   4
        #define INPUT_DEVICE_ID_MATCH_VERSION   8

    另外mod_devicetable.h在里面还有些其他标志位,但目前作用不是很清楚,估计修改input_match_device()函数,可以用,是猜的。

  • 第二部分,关于输入值事件类型匹配:

    if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
            continue;

    bitmap_subset(id->evbit, dev->evbit, EV_MAX)其中id->evbit是handler支持的事件(支持的事件相应位是1),dev->evbit输入设备支持的事件,同样,支持的位是1,EV_MAX表示支持最多事件,bitmap_subset()表示,如果id->evbit为1的位,是dev->evbit为1的位的子集(换句话说dev->evbit为1的位包含id->evbit为1的位),则返回1,否则返回0。以上代码表示的就是,handler支持的事件,input device必须全部支持,否则无法和handler匹配,而input device支持的事件,handler不一定支持(handler的id支持的事件id->evbit,id->absbit,id->keybit等全部位,为0,表示全部支持,就看device了)。另外,需要明白的是,evbit是事件类型,而像keybit,relbit,absbit等是具体某一类事件,而且一类事件又包含了很多具体这类事件下的具体事件编号,如下结构体理解:

    struct input_value {
        __u16 type;//事件类型
        __u16 code;//具体事件类型,某个动作编码
        __s32 value;//该编号动作下的值
    };

    type在linux下为下列宏,或者多个宏的或(include/uapi/linux/input.h):

           /*
            * Event types
            */  
            #define EV_SYN          0x00
            #define EV_KEY          0x01
            #define EV_REL          0x02
            #define EV_ABS          0x03
            #define EV_MSC          0x04
            #define EV_SW           0x05
            #define EV_LED          0x11
            #define EV_SND          0x12
            #define EV_REP          0x14
            #define EV_FF           0x15
            #define EV_PWR          0x16
            #define EV_FF_STATUS        0x17
            #define EV_MAX          0x1f
            #define EV_CNT          (EV_MAX+1)

    code就要看type支持的事件,当然也可以支持一类或者多类,如EV_KEY按键事件,只列出部分,按键比较多:

    /*
     * Keys and buttons
     *
     * Most of the keys/buttons are modeled after USB HUT 1.12
     * (see http://www.usb.org/developers/hidpage).
     * Abbreviations in the comments:
     * AC - Application Control
     * AL - Application Launch Button
     * SC - System Control
     */
         #define KEY_RESERVED       0
        #define KEY_ESC         1
        #define KEY_1           2
        #define KEY_2           3
        #define KEY_3           4
        #define KEY_4           5
        #define KEY_5           6
        #define KEY_6           7
        #define KEY_7           8
        #define KEY_8           9
        #define KEY_9           10
        #define KEY_0           11
        #define KEY_MINUS       12
        #define KEY_EQUAL       13
        #define KEY_BACKSPACE       14
        #define KEY_TAB         15
        #define KEY_Q           16
        #define KEY_W           17
        #define KEY_E           18
        #define KEY_R           19
        #define KEY_T           20
        #define KEY_Y           21
        #define KEY_U           22

    value值就看具体设备了,对于按键设备来说是1或者0,表示按键按下或抬起,触摸屏value就是坐标值,code就是代表某个坐标轴。
    其它具体事件,具体看,都定义在include/uapi/linux/input.h。
    其实struct input_value驱动里面并不定义,而是送到上层应用软件去解析值的意义,上面描述的按键,触摸屏都已经公认的,驱动跟着上层软件解析的意义去描述,比较清晰,直观。应用软件完全可以把一个触摸屏的坐标值解析成一个按键,从而产生一个按键的动作。

  • 第三部分,如下代码:

    if (!handler->match || handler->match(handler, dev))
            return id;

    当input框架定义的,以上flags和事件类型,具体支持的事件,都匹配通过后。handler没有另外增加匹配规则,就直接匹配通过返回支持的id table,否则,再按照handler的匹配规则匹配。

  • 第四部分,id->driver_info,其实个人感觉不需要对flags检测(id->flags为0)时,要保证id->driver_info为非零,才会启动匹配过程,否则将返回NULL。evdev的handler就是不检查flags,如下(drivers/input/evdev.c):

    static const struct input_device_id evdev_ids[] = {
    { 
        .driver_info = 1 }, /* Matches all devices */
        { },            /* Terminating zero entry */
    };
  • 第五部分,input_match_device()返回值,匹配成功返回id table,不成功返回NULL。


input_dev注册

准确说,input_dev注册,除去input框架公共部分,是需要按照handler来注册,但是,在我们用input框架时,很多情况都是默认handler是evdev,因此这里,也按照handler是evdev,来描述。

  • 第一步,分配input_dev,可以定义input_dev类型结构体全局变量(这不单独描述),也可以用input框架提供的分配函数input_allocate_device(),声明在include/linux/input.h:

    struct input_dev *da;
    da = input_allocate_device();
  • 第二部,设置input支持的事件类型,handler是evdev,都必须支持EV_SYN同步事件(原因在单独描述evdev时说明),其他事件,根据输入设备来设置,这里假定输入设备是同时支持按键和触摸屏事件:

    da->evbit = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

    也可以写成

    da->evbit = EV_SYN | EV_KEY | BEV_ABS;

    还可以写成

        __set_bit(EV_SYN,da->evbit);
        __set_bit(EV_KEY,da->evbit);
        __set_bit(BEV_ABS,da->evbit);

    或者

        set_bit(EV_SYN,da->evbit);
        set_bit(EV_KEY,da->evbit);
        set_bit(BEV_ABS,da->evbit);

    都是将da->evbit相应位置1,BIT_MASK,__set_bit(非原子性),set_bit(原子性的)可以在include/linux/bitops.h,include/asm-generic/bitops/non-bitops.h,include/linux/asm-generic/bitops/atomic.h。一般用set_bit比较好,有原子性。

  • 第三部分,具体事件设置,这里用按键和触摸屏举例

    • 按键, 支持那个按键,一般用:

      input_set_capability(da->input_dev, EV_KEY, KEY_1);  

      表示支持1按键。

    • 触摸屏,对触摸屏属性的设置,一般用:

      
      void input_set_abs_params(struct input_dev *dev, unsigned int axis,
            int min, int max, int fuzz, int flat)

      axis:表示那个轴,X轴,还是Y轴,Z轴
      min:表示轴上输入的坐标,最小值
      max:表示轴上输入的坐标,最大值
      fuzz,flat:暂时还不清楚,一般都设置成0

      //表示触摸屏X轴,最小值是0,最大是1000
      input_set_abs_params(da->input_dev, ABS_MT_POSITION_X, 0, 1000, 0, 0);
      //表示触摸屏Y轴,最小值是0,最大是3000
      input_set_abs_params(da->input_dev, ABS_MT_POSITION_Y, 0, 3000, 0, 0);
      //表示触摸屏,触点面积(一般假定位椭圆),短轴最小值0,最大255
      input_set_abs_params(da->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
      //表示触摸屏,触点面积(一般假定位椭圆),长轴最小值0,最大255
      input_set_abs_params(da->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
      //表示触摸屏,触摸滑动时轨迹ID,最下值0,最大255
      input_set_abs_params(da->input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);

    其实,在关于input框架,我们不用关注具体事件表示意思,我们只关注struct input_value结构三个值,具体意思,由应用程序根据具体设备解析意义。

  • 第四部分,设定input_dev的名子name,以及flags

    da->input_dev->name = "daname";

    关于如下,如果handler是evdev,是不需要的:

        da->input_dev->id.bustype = BUS_I2C;//
        da->input_dev->id.vendor = 0xDEAD;//
        da->input_dev->id.product = 0xBEEF;//
        da->input_dev->id.version = 10427;//

    要检查这些属性还需设置flags标志位。

  • 第五部,也是最后一部,注册:

    int ret = 0;
     ret = input_register_device(da->input_dev);
    if (ret)
    {
       //注册失败
       input_free_device(da);
    }

handler之evdev:

  • 了解evdev先看如下代码(drivers/input/evdev.c):

    static const struct input_device_id evdev_ids[] = {
        { .driver_info = 1 },   /* Matches all devices */
        { },            /* Terminating zero entry */
    };
    
    static struct input_handler evdev_handler = {
        .event      = evdev_event,
        .events     = evdev_events,
        .connect    = evdev_connect,
        .disconnect = evdev_disconnect,
        .legacy_minors  = true,
        .minor      = EVDEV_MINOR_BASE,
        .name       = "evdev",
        .id_table   = evdev_ids,
    };
    
    static int __init evdev_init(void)
    {
        return input_register_handler(&evdev_handler);
    }
    
    static void __exit evdev_exit(void)
    {
        input_unregister_handler(&evdev_handler);
    }

    evdev处理过程:

    • 1、以驱动模块加载的形式,调用input_register_handler()注册handler到input框架里面。
    • 2、当有input_dev注册到input框架时,与evdev_ids匹配(基本上都能匹配成功)。
    • 3、匹配成功后evdev_connect()函数被调用,这个函数创建了/dev/input/event*文件节点(上层用用程序接口)。创建的字符文件绑定的file_operations如下:
    static const struct file_operations evdev_fops = {
        .owner      = THIS_MODULE,
        .read       = evdev_read,
        .write      = evdev_write,
        .poll       = evdev_poll,
        .open       = evdev_open,
        .release    = evdev_release,
        .unlocked_ioctl = evdev_ioctl,
    
    #ifdef CONFIG_COMPAT
    
    .compat_ioctl   = evdev_ioctl_compat,
     #endif
        .fasync     = evdev_fasync,
        .flush      = evdev_flush,
        .llseek     = no_llseek,
    };
    • 4、当上层应用软件需要这个input_dev提供的数据时,首先需要open下/dev/input/event*,获取文件句柄,只有这样open下后,evdev才会为其分配struct evdev_client类型的结构体变量内存空间(client将成为file->private_data = client)。
    • 5、应用程序,可以直接调用evdev_read读取,如果此时input_dev没有数据将会阻塞在这里,等有数据时将解除阻塞返回;也可以用evdev_poll先探测有无数据(参考linux的poll),有数据再调用evdev_read来读取;也可以用evdev_fasync异步方法获取(参考linux的异步机制fasync)。
    • 6、当input_dev有数据,调用input_event()上报时,evdev_events()或者evdev_event()将被调用,具体是那个暂时还每弄清楚,功能都一样,此时会解除evdev_read的阻塞,以及evdev_poll,采用fasync的,应用程序将接收到kill_fasync()发出的通知。
    • 7、一些特殊信息,控制或者获取,通过evdev_ioctl来设置或获取,如触摸屏一些信息就是通过ioctl方式,在evdev_ioctl里面会调用到的部分代码如下:
    if (_IOC_DIR(cmd) == _IOC_READ) {
    
        if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
            return handle_eviocgbit(dev,
                        _IOC_NR(cmd) & EV_MAX, size,
                        p, compat_mode);
    
        if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
    
            if (!dev->absinfo)
                return -EINVAL;
    
            t = _IOC_NR(cmd) & ABS_MAX;
            abs = dev->absinfo[t];
    
            if (copy_to_user(p, &abs, min_t(size_t,
                    size, sizeof(struct input_absinfo))))
                return -EFAULT;
    
            return 0;
        }
    }
    
    if (_IOC_DIR(cmd) == _IOC_WRITE) {
    
        if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
    
            if (!dev->absinfo)
                return -EINVAL;
    
            t = _IOC_NR(cmd) & ABS_MAX;
    
            if (copy_from_user(&abs, p, min_t(size_t,
                    size, sizeof(struct input_absinfo))))
                return -EFAULT;
    
            if (size < sizeof(struct input_absinfo))
                abs.resolution = 0;
    
            /* We can't change number of reserved MT slots */
            if (t == ABS_MT_SLOT)
                return -EINVAL;
    
            /*
             * Take event lock to ensure that we are not
             * changing device parameters in the middle
             * of event.
             */
            spin_lock_irq(&dev->event_lock);
            dev->absinfo[t] = abs;
            spin_unlock_irq(&dev->event_lock);
    
            return 0;
        }
    }

    absinfo像这些,就是触摸屏信息:

        struct input_absinfo {
            __s32 value;//那个坐标轴,X,Y等
            __s32 minimum;//对应坐标轴的最小值
            __s32 maximum;//对应坐标轴的最大值
            __s32 fuzz;
            __s32 flat;
            __s32 resolution;
    };

总结:

input device和input handler的注册到input框架时,进行匹配,如匹配成功,由handler的connect创建应用成学接口,同时用handle将input device和input handler连接起来。

设备驱动,有数据时,通过input_event()上报数据到匹配的handler。

应用程序,首先要open创建的文件节点(应用程序接口),然后,才能通过read,poll,fasync三种方式的其中一种方式获取数据。

你可能感兴趣的:(linux驱动)