zoukankan      html  css  js  c++  java
  • input子系统

    linux的input子系统在网上的资料很多,讲应用的很多,但是input子系统如何去工作的较少,想通过源码去理解input子系统如何工作的。

    input子系统应用
    一般input子系统应用流程如下
    input_allocate_device -> input_register_device->input_event->input_unregister_device
    关于其具体的使用会在讲述函数时说明。
    2.1 input_dev 结构体
    struct input_dev {
     const char *name;   
     const char *phys;   --物理路径
     const char *uniq;   --设备的唯一识别码
     struct input_id id; --设备id unsigned long evbit[BITS_TO_LONGS(EV_CNT)];   --BITS_TO_LONGS其实将多个bit组合成多个long类型的数据,evbit用于表明设备对事件类型的支持
     unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; --按键事件使能标志位
     unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; --相对坐标事件使能标志
     unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; --绝对坐标事件使能标志
     unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
     unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
     unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
     unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
     unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; unsigned int keycodemax;  --键盘码表项的个数
     unsigned int keycodesize; --键盘码表项的大小
     void *keycode;            --键盘码表的首地址
     int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
     int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode); struct ff_device *ff; unsigned int repeat_key;
     struct timer_list timer; int sync; int abs[ABS_MAX + 1];
     int rep[REP_MAX + 1]; unsigned long key[BITS_TO_LONGS(KEY_CNT)];
     unsigned long led[BITS_TO_LONGS(LED_CNT)];
     unsigned long snd[BITS_TO_LONGS(SND_CNT)];
     unsigned long sw[BITS_TO_LONGS(SW_CNT)]; int absmax[ABS_MAX + 1];
     int absmin[ABS_MAX + 1];
     int absfuzz[ABS_MAX + 1];
     int absflat[ABS_MAX + 1]; int (*open)(struct input_dev *dev);
     void (*close)(struct input_dev *dev);
     int (*flush)(struct input_dev *dev, struct file *file);
     int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); struct input_handle *grab; spinlock_t event_lock;
     struct mutex mutex; unsigned int users;
     int going_away; struct device dev; struct list_head h_list;
     struct list_head node;
    };


    2.2申请设备
    申请设备比较简单。
    struct input_dev *input_allocate_device(void)
    {
     struct input_dev *dev;
     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
     if (dev) {
      dev->dev.type = &input_dev_type;   --设置设备类型
      dev->dev.class = &input_class;     --设置设备类
      device_initialize(&dev->dev);
      mutex_init(&dev->mutex);
      spin_lock_init(&dev->event_lock);
      INIT_LIST_HEAD(&dev->h_list);
      INIT_LIST_HEAD(&dev->node);  __module_get(THIS_MODULE);
     }
     return dev;
    }


    2.3设备初始化
    2.3.1设定设备所支持的事件
    有两种手段可以去设定:

           1.__set_bit(EV_KEY,xx->evbit),设定evbit支持EV_KEY

           2.xx_dev->evbit[0] = BIT(EV_KEY);


    2.3.2设定设备事件使能位
    其设置方法同上。
    __set_bit(xx_keycodes[i], xx_dev->keybit);
    对于上面两个性质的设置还可以通过调用input_set_capability函数来实现。
    其源码如下:
    下面的源码很简单易懂,不说了。

    /* 记录本设备对于哪些事件感兴趣(对其进行处理)*/
    void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
    {
     switch (type) {


     case EV_KEY:
      __set_bit(code, dev->keybit); //比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)
      break;

    case EV_REL:
      __set_bit(code, dev->relbit);
      break;

    case EV_ABS:
      __set_bit(code, dev->absbit);
      break;

    case EV_MSC:
      __set_bit(code, dev->mscbit);
      break;

    case EV_SW:
      __set_bit(code, dev->swbit);
      break;

    case EV_LED:
      __set_bit(code, dev->ledbit);
      break;

    case EV_SND:
      __set_bit(code, dev->sndbit);
      break;

    case EV_FF:
      __set_bit(code, dev->ffbit);
      break;

    case EV_PWR:
      /* do nothing */
      break;

    default:
      printk(KERN_ERR
       "input_set_capability: unknown type %u (code %u) ",
       type, code);
      dump_stack();
      return;
     } __set_bit(type, dev->evbit);
    }

    2.3.3设置设备的信息
    设置设备的name,bus,以及open,close等等成员。关于open和close还有点事可以说的。open必须要返回0,否则出错,close无返回值。

    2.4 input设备注册
    int input_register_device(struct input_dev *dev)
    {
     static atomic_t input_no = ATOMIC_INIT(0);   --这个记住每调用一次该函数input_no增加一次
     struct input_handler *handler;               --设置handler初始
     const char *path;
     int error; __set_bit(EV_SYN, dev->evbit);     --每个设备都要支持该事件
     init_timer(&dev->timer);           --初始化定时器,在linux中采用定时器定时去查询设备事件
     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {   --如果rep没有设定
      dev->timer.data = (long) dev;             
      dev->timer.function = input_repeat_key;  --定时器处理函数
      dev->rep[REP_DELAY] = 250;
      dev->rep[REP_PERIOD] = 33;
     }
     if (!dev->getkeycode)
      dev->getkeycode = input_default_getkeycode;
     if (!dev->setkeycode)
      dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld",(unsigned long) atomic_inc_return(&input_no) - 1); --设置设备名称 error = device_add(&dev->dev);    --设备添加
     if (error)
      return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
     printk(KERN_INFO "input: %s as %s ",dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
     kfree(path); error = mutex_lock_interruptible(&input_mutex);    --锁定互斥
     if (error) {
      device_del(&dev->dev);
      return error;
     } list_add_tail(&dev->node, &input_dev_list);    --将设备节点添加到input设备链表中。 list_for_each_entry(handler, &input_handler_list, node)   --从input处理链表中查询匹配
      input_attach_handler(dev, handler);             --匹配处理函数 input_wakeup_procfs_readers();   --这个不管 mutex_unlock(&input_mutex); return 0;
    }
    根据上面的函数,来一一分析其调用的函数。
    No.1 input_repeat_key该函数在没有设定repeat参数情况下调用。下面的函数在看event层的时候在回过头来看。
    static void input_repeat_key(unsigned long data)   
    {
     struct input_dev *dev = (void *) data;
     unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags);  --自旋锁锁定,记住临界区的代码不能够阻塞。 if (test_bit(dev->repeat_key, dev->key) &&is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {  input_pass_event(dev, EV_KEY, dev->repeat_key, 2);  if (dev->sync) {
       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
      }  if (dev->rep[REP_PERIOD])
       mod_timer(&dev->timer, jiffies +
         msecs_to_jiffies(dev->rep[REP_PERIOD]));
     } spin_unlock_irqrestore(&dev->event_lock, flags);
    }
    No.2 input_default_getkeycode
    这个函数是默认的获取keycode,下面的函数又是很好理解的,偷懒一下。
    static int input_default_getkeycode(struct input_dev *dev, int scancode, int *keycode)
    {
     if (!dev->keycodesize)
      return -EINVAL;
     if (scancode >= dev->keycodemax)
      return -EINVAL;
     *keycode = input_fetch_keycode(dev, scancode);
     return 0;
    }static int input_fetch_keycode(struct input_dev *dev, int scancode)
    {
     switch (dev->keycodesize) {
      case 1:
       return ((u8 *)dev->keycode)[scancode];
      case 2:
       return ((u16 *)dev->keycode)[scancode];
      default:
       return ((u32 *)dev->keycode)[scancode];
     }
    }
    No.3 input_default_setkeycode
    默认的设置keycode函数,来好好看一下。
    static int input_default_setkeycode(struct input_dev *dev,int scancode, int keycode)
    {
     int old_keycode;
     int i; if (scancode >= dev->keycodemax)    --scancode是设置的"位",检查设置位是否溢出
      return -EINVAL;
     if (!dev->keycodesize)   
      return -EINVAL;
     if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))  --检测大小是否匹配。
      return -EINVAL; switch (dev->keycodesize) {  --下面是查询替换事件查询表
      case 1: {
       u8 *k = (u8 *)dev->keycode;
       old_keycode = k[scancode];
       k[scancode] = keycode;
       break;
      }
      case 2: {
       u16 *k = (u16 *)dev->keycode;
       old_keycode = k[scancode];
       k[scancode] = keycode;
       break;
      }
      default: {
       u32 *k = (u32 *)dev->keycode;
       old_keycode = k[scancode];
       k[scancode] = keycode;
       break;
      }
     } clear_bit(old_keycode, dev->keybit);  --更改位
     set_bit(keycode, dev->keybit); for (i = 0; i < dev->keycodemax; i++) {   --防止出错
      if (input_fetch_keycode(dev, i) == old_keycode) {
       set_bit(old_keycode, dev->keybit);
       break; /* Setting the bit twice is useless, so break */
      }
     }
     return 0;
    }No.4 input_attach_handler
    这个函数很重要,这个其实类似于platform中probe函数,将设备与设备事件处理相连接。
    static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
    {
     const struct input_device_id *id;
     int error;
     if (handler->blacklist && input_match_device(handler->blacklist, dev))  --先检测dev是否在黑名单中,强人!!!下面会看这handler在哪儿设置了。
      return -ENODEV;
     id = input_match_device(handler->id_table, dev);  --查询到设备id表
     if (!id)
      return -ENODEV;
     error = handler->connect(handler, dev, id);   --将两者连接起来。
     if (error && error != -ENODEV)
      printk(KERN_ERR"input: failed to attach handler %s to device %s, ""error: %d ",handler->name, kobject_name(&dev->dev.kobj), error);
     return error;

    }


    http://lexandros.blog.163.com/blog/static/185566588201291875623729/



  • 相关阅读:
    同台电脑 多Git账号同时使用
    netty对http协议解析原理解析(转载)
    Netty 线程模型与Reactor 模式
    增量/存量数据按时间维度分组
    网易技术分享:Nginx缓存引发的跨域惨案
    全面剖析Redis Cluster原理和应用
    聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路
    美团点评基于 Flink 的实时数仓建设实践
    美团技术分享:大众点评App的短视频耗电量优化实战
    美团技术分享:美团深度学习系统的工程实践
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744597.html
Copyright © 2011-2022 走看看