深层次分析linux2.6input子系统

在linux下,按键、触摸屏、鼠标等都可以利用input接口函数来实现设备驱动。

 

 

从上图可知:

输入子系统由三部分构成:

1 驱动

2 输入子系统

3 处理函数

其中2,3都是内核已经完成,我们要完成的就是1驱动

设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口。驱动报告的事件经过InputCoreEventhandler最终到达用户空间。

Input_dev 结构中比较重要的域有:

Name:名字

Evbit:设备所支持的事件类型

Keybit:按键类型

注册输入设备的函数为:

int input_register_device(struct input_dev *dev)

注销输入设备的函数为:

void input_unregister_device(struct input_dev *dev)

设备所支持的事件类型有:

EV_RST Reset EV_KEY 按键

EV_REL 相对坐标EV_ABS绝对坐标

EV_MSC 其它EV_LED LED

EV_SND 声音EV_REP Repeat

EV_FF 力反馈

使用set_bit(EV_KEY, button_dev.evbit)告知结构体支持哪种设备类型

用于报告EV_KEYEV_RELEV_ABS事件的函数分别为:

void input_report_key(struct input_dev *dev,unsigned int code, int value)

void input_report_rel(struct input_dev *dev,unsigned int code, int value)

void input_report_abs(struct input_dev *dev,unsigned int code, int value)

code

事件的代码。如果事件的类型是EV_KEY,该代码code为设备键盘代码。代码值0~127为键盘上的按键代码,0x110~0x116为鼠标上按键代码,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键。其它代码含义请参看include/linux/input.h文件

value

事件的值。如果事件的类型是EV_KEY,当按键按下时值为1,松开时值为0

input_sync()用于事件同步,它告知事件的接收者:驱动已经发出了一个完整的报告。

在中断中,使用input_report_key等函数想用户空间报告,在应用程序中直接读取状态。

 

输入子系统的设备文件名默认为event0 – event31主设备号默认为13,类似于misc混杂设备。

下面基于mini2440的input输入按键驱动代码:

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <linux/poll.h>

#include <linux/irq.h>

#include <asm/irq.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <linux/platform_device.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

#include <linux/input.h>

 

 

 

struct input_dev *button_dev;

struct button_irq_desc {

   int irq;

   int pin;

   int pin_setting;

   int number;

   char *name;   

};

 

 

static struct button_irq_desc button_irqs [] = {

   {IRQ_EINT8 , S3C2410_GPG0 ,  S3C2410_GPG0_EINT8  , 0, "KEY0"}, 

   {IRQ_EINT11, S3C2410_GPG3 ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},

   {IRQ_EINT13, S3C2410_GPG5 ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},

   {IRQ_EINT14, S3C2410_GPG6 ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},

   {IRQ_EINT15, S3C2410_GPG7 ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},

   {IRQ_EINT19, S3C2410_GPG11,  S3C2410_GPG11_EINT19, 5, "KEY5"},

};

 

static int key_values = 0;

 

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

   struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

   int down;

    udelay(0);

   down = !s3c2410_gpio_getpin(button_irqs->pin);   //down: 1(按下)0(弹起)

  if (!down) {

      

      /*报告事件*/

      key_values = button_irqs->number;

      //printk("====>rising key_values=%d\n",key_values);

 if(key_values==0)

     input_report_key(button_dev, KEY_1, 0);

 if(key_values==1)

     input_report_key(button_dev, KEY_2, 0);

 if(key_values==2)

     input_report_key(button_dev, KEY_3, 0);

 if(key_values==3)

     input_report_key(button_dev, KEY_4, 0);

 if(key_values==4)

     input_report_key(button_dev, KEY_5, 0);

 if(key_values==5)

     input_report_key(button_dev, KEY_6, 0);

 

 input_sync(button_dev);     

     }

  

    else {

       

       key_values = button_irqs->number;

       //printk("====>falling key_values=%d\n",key_values);

 if(key_values==0)

       input_report_key(button_dev, KEY_1, 1);

 if(key_values==1)

       input_report_key(button_dev, KEY_2, 1);

 if(key_values==2)

       input_report_key(button_dev, KEY_3, 1);

 if(key_values==3)

       input_report_key(button_dev, KEY_4, 1);

 if(key_values==4)

       input_report_key(button_dev, KEY_5, 1);

 if(key_values==5)

       input_report_key(button_dev, KEY_6, 1);

 input_sync(button_dev);     

 

    }

 

 

   return IRQ_RETVAL(IRQ_HANDLED);

}

 

 

static int s3c24xx_request_irq(void)

{

   int i;

   int err = 0;

   

   for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

      

 

                           if (button_irqs[i].irq < 0) {

                                                       continue;

                                  }

      /* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */

       err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,

                         button_irqs[i].name, (void *)&button_irqs[i]);

       if (err)

           break;

   }

 

   if (err) {

       i--;

       for (; i >= 0; i--) {

          if (button_irqs[i].irq < 0) {

             continue;

          }

          disable_irq(button_irqs[i].irq);

           free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

       }

       return -EBUSY;

   }

 

   

   return 0;

}

 

 

static int __init dev_init(void)

{

 

 

 /*request irq*/

 s3c24xx_request_irq();

      /* Initialise input stuff */

      button_dev = input_allocate_device();

      if (!button_dev) {

             printk(KERN_ERR "Unable to allocate the input device !!\n");

             return -ENOMEM;

      }

      button_dev->name = "s3c2440_button";

      button_dev->id.bustype = BUS_RS232;

 button_dev->id.vendor = 0xDEAD;

 button_dev->id.product = 0xBEEF;

 button_dev->id.version = 0x0100;

 

      button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SYN);

      //set_bit(EV_KEY, button_dev->evbit)//支持EV_KEY事件

      set_bit(KEY_1,   button_dev->keybit);

      set_bit(KEY_2,   button_dev->keybit);

      set_bit(KEY_3,   button_dev->keybit);

      set_bit(KEY_4,   button_dev->keybit);

      set_bit(KEY_5,   button_dev->keybit);

      set_bit(KEY_6,   button_dev->keybit);

      //printk("KEY_RESERVED=%d ,KEY_1=%d",KEY_RESERVED,KEY_1);

      input_register_device(button_dev);   //注册input设备

 

      printk ("initialized\n");

 

      return 0;

}

 

static void __exit dev_exit(void)

{

          int i;

   

 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

             if (button_irqs[i].irq < 0) {

              continue;

                    }

             free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);

  }

 

      input_unregister_device(button_dev);

}

 

module_init(dev_init);

module_exit(dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("David Xie");

 

应用程序:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/select.h>

#include <sys/time.h>

#include <errno.h>

#include <linux/input.h>

int main(void)

{

      int buttons_fd;

      int key_value,i=0,count;

 

      struct input_event ev_key;

      buttons_fd = open("/dev/event0", O_RDWR);

      if (buttons_fd < 0) {

             perror("open device buttons");

             exit(1);

      }

 

      for (;;) {

             count = read(buttons_fd,&ev_key,sizeof(struct input_event));

      //     printf("count=%d\n",count);

             for(i=0; i<(int)count/sizeof(struct input_event); i++)

                    if(EV_KEY==ev_key.type)

                           printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code-1,ev_key.value);

                    if(EV_SYN==ev_key.type)

                           printf("syn event\n\n");

 

      }

 

 

 

      close(buttons_fd);

      return 0;

}

加载内核模块:

多了event0设备文件

运行应用程序:

 

下面我们分析input子系统源码:

在分析源代码之前,我们要先了解几个主要的结构:

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)]; /*设备所支持的事件类型*/
 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 absres[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;   /*用于连接input_handler和input_dev的结构*/

 spinlock_t event_lock;
 struct mutex mutex;

 unsigned int users;
 bool going_away;

 struct device dev;

 struct list_head h_list;   /*链表连接input_handle*/
 struct list_head node;/*链表连接下一个input_dev*/
};

 input_handler表示一个Input设备对应的handler

struct input_handler {

 void *private;
 /*事件处理函数*/
 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
 /*用来连接handler和dev的函数*/
 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
 void (*disconnect)(struct input_handle *handle);
 void (*start)(struct input_handle *handle);
 /*操作函数集*/
 const struct file_operations *fops;
 /*次设备号*/
 int minor;
 const char *name;
 /*用来识别驱动是否能够处理的表*/
 const struct input_device_id *id_table;
 /*用来识别驱动不能处理的表(黑名单)*/
 const struct input_device_id *blacklist;
 /*用来连接handle*/
 struct list_head h_list;
 /*用来连接下一个hanlder*/
 struct list_head node;
};

input_handle是用来连接input_handler和input_dev的结构

 /*用于连接input_dev和input_hander*/
struct input_handle {

 void *private; /*私有指针*/
 /*打开计数*/
 int open;   
 const char *name;

 struct input_dev *dev;  /*依附的input_dev结构*/
 struct input_handler *handler; /*依附的handler*/

 struct list_head d_node; /*连接dev*/
 struct list_head h_node;/*连接handler*/
};

 

在内核中,因为Input是作为子系统存在,跟其子系统一样,首先会执行input.c的input_init,(如rtc子系统在加载具体设备驱动程序之前会加载rtc_init,iic子系统会加载iic_init)。

其中input_init的标志是subsys_initcall(input_init);“优先级”比module_init更高。

static int __init input_init(void)
{
 int err;
 /*填充input_abs_bypass数组*/
 input_init_abs_bypass();
 /*注册input类,在/sys/class/input*/
 err = class_register(&input_class);
 if (err) {
  printk(KERN_ERR "input: unable to register input_dev class\n");
  return err;
 }
 /*在Proc建立相关文件*/
 err = input_proc_init();
 if (err)
  goto fail1;
 /*注册cdev结构 主设备号是13*/
 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
 if (err) {
  printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
  goto fail2;
 }

 return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
 return err;
}

有上可知 input_init主要功能(1)填充input_abs_bypass数组并注册input类(2)在proc目录下建立交互文件(3)注册cdev,主设备号为13

我们在查看input_fops结构:

static const struct file_operations input_fops = {
 .owner = THIS_MODULE,
 .open = input_open_file,
};
只有open函数,为什么呢,我们查看input_open_file的源码:

static int input_open_file(struct inode *inode, struct file *file)
{
 struct input_handler *handler;
 const struct file_operations *old_fops, *new_fops = NULL;
 int err;

 lock_kernel();
 /* No load-on-demand here? */
 /*通过次设备号左移5位找到handler结构,也就是次设备号0-31对应的handler结构都一样
 32-63一样,依次类推*/
 handler = input_table[iminor(inode) >> 5];
  /*获取handler中的fops*/
 if (!handler || !(new_fops = fops_get(handler->fops))) {
  err = -ENODEV;
  goto out;
 }

 /*
  * That's _really_ odd. Usually NULL ->open means "nothing special",
  * not "no device". Oh, well...
  */
  /*如果open函数指针为空*/
 if (!new_fops->open) {
  fops_put(new_fops);
  err = -ENODEV;
  goto out;
 }
 old_fops = file->f_op;
 file->f_op = new_fops;
 /*执行handler的open函数*/
 err = new_fops->open(inode, file);
 
 if (err) {
  fops_put(file->f_op);
  file->f_op = fops_get(old_fops);
 }
 fops_put(old_fops);
out:
 unlock_kernel();
 return err;
}

由上可知:open函数最终会调用对应handler的open函数。

子系统初始化之后,下面就是事件的驱动了,在这里我们支持的是按键,对应的驱动是evdev.c

static int __init evdev_init(void)
{
 return input_register_handler(&evdev_handler);
}

static struct input_handler evdev_handler = {
 .event  = evdev_event,
 .connect = evdev_connect,
 .disconnect = evdev_disconnect,
 .fops  = &evdev_fops,
 .minor  = EVDEV_MINOR_BASE,
 .name  = "evdev",
 .id_table = evdev_ids,
};

int input_register_handler(struct input_handler *handler)
{
 struct input_dev *dev;
 int retval;

 retval = mutex_lock_interruptible(&input_mutex);
 if (retval)
  return retval;

 INIT_LIST_HEAD(&handler->h_list);
 /*将handler存于input_handler数组*/
 if (handler->fops != NULL) {
  if (input_table[handler->minor >> 5]) {
   retval = -EBUSY;
   goto out;
  }
  input_table[handler->minor >> 5] = handler;
 }
 /*将hander添加到input_handler_list*/
 list_add_tail(&handler->node, &input_handler_list);
 /*遍历整个input_dev_list*/
 list_for_each_entry(dev, &input_dev_list, node)
  input_attach_handler(dev, handler);

 input_wakeup_procfs_readers();

 out:
 mutex_unlock(&input_mutex);
 return retval;
}

事件层注册完之后

下面就是具体按键驱动的注册了:

首先:

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;
  /*设备初始化,在/sys/device目录下建立目录*/
  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;
}

input_allocate_device分配一个input_dev,并在/sys/device下建立目录。

下面注册dev结构

int input_register_device(struct input_dev *dev)
{
 static atomic_t input_no = ATOMIC_INIT(0);
 struct input_handler *handler;
 const char *path;
 int error;
 /* 对每个输入设备都设置EV_SYN位 */
 __set_bit(EV_SYN, dev->evbit);
 
 /*
  * If delay and period are pre-set by the driver, then autorepeating
  * is handled by the driver itself and we don't do it in input.c.
  */
 /*初始化内核定时器*/
 init_timer(&dev->timer);
 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
  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);
 /*添加设备到device*/
 error = device_add(&dev->dev);
 if (error)
  return error;
  /*获取kobj路径*/
 path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
 printk(KERN_INFO "input: %s as %s\n",
  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;
 }
 /*将dev结构加入到input_dev_list链表头*/
 list_add_tail(&dev->node, &input_dev_list);
 /*遍历整个input_handler_list链表,通过判断handler的id域和dev的id域是否匹配,如果匹配将handler和dev联系起来*/
 list_for_each_entry(handler, &input_handler_list, node)
  input_attach_handler(dev, handler);
 /*唤醒等待队列*/
 input_wakeup_procfs_readers();
 /*互斥锁解锁*/
 mutex_unlock(&input_mutex);

 return 0;
}

input_register_device的主要作用是

(1)      对形参Input_dev结构的一些默认参数做填充,包括支持的事件类型,timer函数,getkeycode函数指针,setkeycode函数指针,并且对dev域的name填充。

(2)      device_add 添加设备到device核心

(3)      将该Input_dev结构加入到input_dev_list链表

(4)      遍历整个input_handler_list链表,找到一个handlerid_tableinput_devid一直的handler结构,并将此hanlder和此input_dev结构联系起来

对(4)要重点分析:

list_for_each_entry(handler, &input_handler_list, node)

             input_attach_handler(dev, handler);

list_for_each_entry之前已经讲过,就是对input_handler_list整个链表进行遍历。

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
 const struct input_device_id *id;
 int error;
 /*如果该dev的id在handler的blacklist已经登记,则跳过*/
 if (handler->blacklist && input_match_device(handler->blacklist, dev))
  return -ENODEV;
 /*找到handler的id_table和dev的id一致的id_table结构*/
 id = input_match_device(handler->id_table, dev);
 if (!id)
  return -ENODEV;
 /*通过ID将input和handler联系起来了*/
 error = handler->connect(handler, dev, id);
 if (error && error != -ENODEV)
  printk(KERN_ERR
   "input: failed to attach handler %s to device %s, "
   "error: %d\n",
   handler->name, kobject_name(&dev->dev.kobj), error);

 return error;
}

当handler的id_table和dev的id一致就执行handler的connect函数将dev和handler联系起来。执行handler的connect函数。那么在这里对应的就是evdev_handler的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 error;
 /*找到evdev_table中第一个为NULL的元素*/
 for (minor = 0; minor < EVDEV_MINORS; minor++)
  if (!evdev_table[minor])
   break;
 /*如果evdev_table中每个元素存在则退出*/
 if (minor == EVDEV_MINORS) {
  printk(KERN_ERR "evdev: no more free evdev devices\n");
  return -ENFILE;
 }
 /*分配内存*/
 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
 if (!evdev)
  return -ENOMEM;

 INIT_LIST_HEAD(&evdev->client_list);
 spin_lock_init(&evdev->client_lock);
 mutex_init(&evdev->mutex);
 init_waitqueue_head(&evdev->wait);

 dev_set_name(&evdev->dev, "event%d", minor);
 evdev->exist = 1;
 evdev->minor = minor;

 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, EVDEV_MINOR_BASE + minor);
 evdev->dev.class = &input_class;
 evdev->dev.parent = &dev->dev;
 evdev->dev.release = evdev_free;
 device_initialize(&evdev->dev);
 /*将hanlde handler input_dev三者联系起来*/
 error = input_register_handle(&evdev->handle);
 if (error)
  goto err_free_evdev;
 /*将evdev填充到evdev_table*/
 error = evdev_install_chrdev(evdev);
 if (error)
  goto err_unregister_handle;
 /*添加设备*/
 error = device_add(&evdev->dev);
 if (error)
  goto err_cleanup_evdev;

 return 0;

 err_cleanup_evdev:
 evdev_cleanup(evdev);
 err_unregister_handle:
 input_unregister_handle(&evdev->handle);
 err_free_evdev:
 put_device(&evdev->dev);
 return error;
}

由上:evdev_connect的函数主要功能:(1)分配一个evdev,并将它填充到evdev_table中(方便以后调用),(2)调用input_register_handlehander handle input_dev连接起来,其中handle分别和input_devhandler相连。(3)调用device_add添加设备。

调用完evdev_connect之后handle handler input_dev之间就通过链表关联起来了。

对于(2)要分析一下:

int input_register_handle(struct input_handle *handle)
{
 struct input_handler *handler = handle->handler;
 struct input_dev *dev = handle->dev;
 int error;

 /*
  * We take dev->mutex here to prevent race with
  * input_release_device().
  */
 error = mutex_lock_interruptible(&dev->mutex);
 if (error)
  return error;
 /*将handle连接到input的头部*/
 list_add_tail_rcu(&handle->d_node, &dev->h_list);
 mutex_unlock(&dev->mutex);

 /*
  * Since we are supposed to be called from ->connect()
  * which is mutually exclusive with ->disconnect()
  * we can't be racing with input_unregister_handle()
  * and so separate lock is not needed here.
  */
  /*将handle连接到handler的尾部*/
 list_add_tail(&handle->h_node, &handler->h_list);
 /*如果存在start域,则执行start*/
 if (handler->start)
  handler->start(handle);

 return 0;
}

通过input_register_handle就将handler handle input_dev3个结构通过链表联系起来了。

当上面这些步骤完成之前,驱动就在内核注册好了,驱动程序加载完后,在/dev/input目录下就生成了even0设备,它的主设备号是13,次设备号为64.

当应用程序使用open函数打开/dev/input/even0设备时,在内核中Input子系统,open对应的驱动时input.cinput_open_file函数:

 

static int input_open_file(struct inode *inode, struct file *file)
{
 struct input_handler *handler;
 const struct file_operations *old_fops, *new_fops = NULL;
 int err;

 lock_kernel();
 /* No load-on-demand here? */
 /*通过次设备号左移5位找到handler结构,也就是次设备号0-31对应的handler结构都一样
 32-63一样,依次类推*/
 handler = input_table[iminor(inode) >> 5];
  /*获取handler中的fops*/
 if (!handler || !(new_fops = fops_get(handler->fops))) {
  err = -ENODEV;
  goto out;
 }

 /*
  * That's _really_ odd. Usually NULL ->open means "nothing special",
  * not "no device". Oh, well...
  */
  /*如果open函数指针为空*/
 if (!new_fops->open) {
  fops_put(new_fops);
  err = -ENODEV;
  goto out;
 }
 old_fops = file->f_op;
 file->f_op = new_fops;
 /*执行handler的open函数*/
 err = new_fops->open(inode, file);
 
 if (err) {
  fops_put(file->f_op);
  file->f_op = fops_get(old_fops);
 }
 fops_put(old_fops);
out:
 unlock_kernel();
 return err;
}

input_open_file主要做了以下事情:(1)通过此设备号右移5位(因为Input_register_handler中次设备号左移5位存储于Input_table中)获取handler结构,在此处就是evdev_handler2)如果evdev_handleropen函数存在就执行evdev_handleropen函数。

上面已经提到:evdev_handlerfops:下面就执行evdev_handlerfopsopen了。

 

static int evdev_open(struct inode *inode, struct file *file)
{
 struct evdev *evdev;
 /*evdev_client结构对应一个具体的设备*/
 struct evdev_client *client;
 /*次设备号减去64=0*/
 int i = iminor(inode) - EVDEV_MINOR_BASE;
 int error;

 if (i >= EVDEV_MINORS)
  return -ENODEV;

 error = mutex_lock_interruptible(&evdev_table_mutex);
 if (error)
  return error;
 /*获取储存在evdev_table的evdev结构*/
 evdev = evdev_table[i];
 if (evdev)
  get_device(&evdev->dev);
 mutex_unlock(&evdev_table_mutex);

 if (!evdev)
  return -ENODEV;

 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
 if (!client) {
  error = -ENOMEM;
  goto err_put_evdev;
 }

 spin_lock_init(&client->buffer_lock);
 client->evdev = evdev;
 /*通过链表将client置于evdev的链表头*/
 evdev_attach_client(evdev, client);

 error = evdev_open_device(evdev);
 if (error)
  goto err_free_client;
 /*将client置于file的私有指针域*/
 file->private_data = client;
 return 0;

 err_free_client:
 evdev_detach_client(evdev, client);
 kfree(client);
 err_put_evdev:
 put_device(&evdev->dev);
 return error;
}

evdev_open函数主要做了以下事情:(1)通过次设备号获取evdev结构(2)分配一个endev_client结构并将client->evdev指向获取的evdev结构(3)调用evdev_attach_client函数将client置于evdev的链表头(4)调用evdev_open_device函数

evdev_open_device函数:

{

      int retval;

 

      retval = mutex_lock_interruptible(&evdev->mutex);

      if (retval)

             return retval;

 

      if (!evdev->exist)

             retval = -ENODEV;

      else if (!evdev->open++) {

             retval = input_open_device(&evdev->handle);

             if (retval)

                    evdev->open--;

      }

 

      mutex_unlock(&evdev->mutex);

      return retval;

}

函数调用input_open_device函数

int input_open_device(struct input_handle *handle)

{

      struct input_dev *dev = handle->dev;

      int retval;

 

      retval = mutex_lock_interruptible(&dev->mutex);

      if (retval)

             return retval;

 

      if (dev->going_away) {

             retval = -ENODEV;

             goto out;

      }

      /*open计数加一*/

      handle->open++;

      /*当用户第一次调用时会调用devopen函数*/

      if (!dev->users++ && dev->open)

             retval = dev->open(dev);

 

      if (retval) {

             dev->users--;

             if (!--handle->open) {

                    /*

                     * Make sure we are not delivering any more events

                     * through this handle

                     */

                    synchronize_rcu();

             }

      }

 

 out:

      mutex_unlock(&dev->mutex);

      return retval;

}

因为在本按键驱动中,没有设置button_devopen域,所以这里input_open_device无意义。

下面就是read函数监听键值了:

Read函数对应的驱动程序是 evdev.cevdev_read

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
 /*获得client结构*/
 struct evdev_client *client = file->private_data;
 struct evdev *evdev = client->evdev;
 struct input_event event;
 int retval;
 /*如果读取的字符数小于input_event结构的大小*/
 if (count < input_event_size())
  return -EINVAL;
 /*如果头结点等于尾节点而且打开标志是不阻塞的,那么直接返回*/
 if (client->head == client->tail && evdev->exist &&
     (file->f_flags & O_NONBLOCK))
  return -EAGAIN;
 /*条件为假阻塞,直到为真为止*/
 retval = wait_event_interruptible(evdev->wait,
  client->head != client->tail || !evdev->exist);
 if (retval)
  return retval;

 if (!evdev->exist)
  return -ENODEV;
 
 while (retval + input_event_size() <= count &&
        evdev_fetch_next_event(client, &event)) {

  if (input_event_to_user(buffer + retval, &event))
   return -EFAULT;

  retval += input_event_size();
 }

 return retval;
}

这里有必要提一下evdev_read函数中的client结构:

 

这里有必要提一下evdev_read函数中的client结构:

struct evdev_client {
 /*存放事件的buffer*/
 struct input_event buffer[EVDEV_BUFFER_SIZE];
 /*事件头结点*/
 int head;
 /*事件尾节点*/
 int tail;
 spinlock_t buffer_lock; /* protects access to buffer, head and tail */
 struct fasync_struct *fasync;
 /*evdev结构指针*/
 struct evdev *evdev;
 /*指向handler的链表*/
 struct list_head node;
};

这个结构中的head tail相当于一个循环队列,只有当head不等于tail的时候才表示buffer里面不为空。那么当调用evdev_read时运行到retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);evdev_read会阻塞。这样就会等待按键。

当按下键值,进入中断函数,调用input_report_key报告event

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
 input_event(dev, EV_KEY, code, !!value);
}

void input_event(struct input_dev *dev,
   unsigned int type, unsigned int code, int value)
{
 unsigned long flags;
 /*判断evbit是否是事件所支持*/
 if (is_event_supported(type, dev->evbit, EV_MAX)) {

  spin_lock_irqsave(&dev->event_lock, flags);
  add_input_randomness(type, code, value);
  /*事件处理函数*/
  input_handle_event(dev, type, code, value);
  spin_unlock_irqrestore(&dev->event_lock, flags);
 }
}

函数最终调用input_handle_event

input_handle_event函数再根据type类型做相应处理,该驱动去typeEV_KEY
static void input_handle_event(struct input_dev *dev,
          unsigned int type, unsigned int code, int value)
{
 int disposition = INPUT_IGNORE_EVENT;

 switch (type) {

 case EV_SYN:
  switch (code) {
  case SYN_CONFIG:
   disposition = INPUT_PASS_TO_ALL;
   break;

  case SYN_REPORT:
   if (!dev->sync) {
    dev->sync = 1;
    disposition = INPUT_PASS_TO_HANDLERS;
   }
   break;
  case SYN_MT_REPORT:
   dev->sync = 0;
   disposition = INPUT_PASS_TO_HANDLERS;
   break;
  }
  break;

 case EV_KEY:
  /*再次判断keybit是否是小于最大键值*/
  if (is_event_supported(code, dev->keybit, KEY_MAX) &&
      !!test_bit(code, dev->key) != value) {

   if (value != 2) {
    __change_bit(code, dev->key);
    if (value)
     input_start_autorepeat(dev, code);
    else
     input_stop_autorepeat(dev);
   }

   disposition = INPUT_PASS_TO_HANDLERS;
  }
  break;

 case EV_SW:
  if (is_event_supported(code, dev->swbit, SW_MAX) &&
      !!test_bit(code, dev->sw) != value) {

   __change_bit(code, dev->sw);
   disposition = INPUT_PASS_TO_HANDLERS;
  }
  break;

 case EV_ABS:
  if (is_event_supported(code, dev->absbit, ABS_MAX)) {

   if (test_bit(code, input_abs_bypass)) {
    disposition = INPUT_PASS_TO_HANDLERS;
    break;
   }

   value = input_defuzz_abs_event(value,
     dev->abs[code], dev->absfuzz[code]);

   if (dev->abs[code] != value) {
    dev->abs[code] = value;
    disposition = INPUT_PASS_TO_HANDLERS;
   }
  }
  break;

 case EV_REL:
  if (is_event_supported(code, dev->relbit, REL_MAX) && value)
   disposition = INPUT_PASS_TO_HANDLERS;

  break;

 case EV_MSC:
  if (is_event_supported(code, dev->mscbit, MSC_MAX))
   disposition = INPUT_PASS_TO_ALL;

  break;

 case EV_LED:
  if (is_event_supported(code, dev->ledbit, LED_MAX) &&
      !!test_bit(code, dev->led) != value) {

   __change_bit(code, dev->led);
   disposition = INPUT_PASS_TO_ALL;
  }
  break;

 case EV_SND:
  if (is_event_supported(code, dev->sndbit, SND_MAX)) {

   if (!!test_bit(code, dev->snd) != !!value)
    __change_bit(code, dev->snd);
   disposition = INPUT_PASS_TO_ALL;
  }
  break;

 case EV_REP:
  if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
   dev->rep[code] = value;
   disposition = INPUT_PASS_TO_ALL;
  }
  break;

 case EV_FF:
  if (value >= 0)
   disposition = INPUT_PASS_TO_ALL;
  break;

 case EV_PWR:
  disposition = INPUT_PASS_TO_ALL;
  break;
 }

 if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
  dev->sync = 0;

 if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
  dev->event(dev, type, code, value);

 if (disposition & INPUT_PASS_TO_HANDLERS)
  input_pass_event(dev, type, code, value);
}

input_handle_event最终调用:input_pass_event

static void input_pass_event(struct input_dev *dev,
        unsigned int type, unsigned int code, int value)
{
 struct input_handle *handle;

 rcu_read_lock();

 handle = rcu_dereference(dev->grab);
 /*最终调用handler的event函数*/
 if (handle)
  
  handle->handler->event(handle, type, code, value);
 else
  list_for_each_entry_rcu(handle, &dev->h_list, d_node)
   if (handle->open)
    handle->handler->event(handle,
       type, code, value);
 rcu_read_unlock();
}

input_pass_event再调用evdev_event

static void evdev_event(struct input_handle *handle,
   unsigned int type, unsigned int code, int value)
{
 struct evdev *evdev = handle->private;
 struct evdev_client *client;
 struct input_event event;

 do_gettimeofday(&event.time);
 /*将按键信息存于event结构*/
 event.type = type;
 event.code = code;
 event.value = value;

 rcu_read_lock();
 /*获得client结构*/
 client = rcu_dereference(evdev->grab);
 if (client)
  evdev_pass_event(client, &event);
 else
  list_for_each_entry_rcu(client, &evdev->client_list, node)
   evdev_pass_event(client, &event);

 rcu_read_unlock();

 wake_up_interruptible(&evdev->wait);
}

evdev_event会调用evdev_pass_event函数:

static void evdev_pass_event(struct evdev_client *client,
        struct input_event *event)
{
 /*
  * Interrupts are disabled, just acquire the lock
  */
 spin_lock(&client->buffer_lock);
 /*将事件结构存在buffer中并且head加一*/
 client->buffer[client->head++] = *event;
 /*溢出处理*/
 client->head &= EVDEV_BUFFER_SIZE - 1;
 spin_unlock(&client->buffer_lock);

 kill_fasync(&client->fasync, SIGIO, POLL_IN);
}

evdev_pass_event函数我们可以得到:将事件结构存在buffe,clienthead指针加一,那么head加一之后,也就是报告键值完毕了,回到了evdev_read函数中:此时的head是不等于tail的,所以唤醒等待队列:

 

下面就会执行evdev_read的evdev_fetch_next_event函数:

static int evdev_fetch_next_event(struct evdev_client *client,

                              struct input_event *event)

{

       int have_event;

 

       spin_lock_irq(&client->buffer_lock);

 

       have_event = client->head != client->tail;

       if (have_event) {

              /*获取事件指针并且tail加一*/

              *event = client->buffer[client->tail++];

              /*溢出处理*/

              client->tail &= EVDEV_BUFFER_SIZE - 1;

       }

 

       spin_unlock_irq(&client->buffer_lock);

 

       return have_event;

}

evdev_fetch_next_event将client的尾节点加一(这时候head和tail又相等了)。

随后evdev_read调用input_event_to_user

int input_event_to_user(char __user *buffer,
   const struct input_event *event)
{
 if (copy_to_user(buffer, event, sizeof(struct input_event)))
  return -EFAULT;

 return 0;
}

这样就将按键事件结构event传递到了用户空间。

总结:由上我们看出,input子系统的确是很复杂的。下面画一个简单的流程图。


 

 

 


 

 

 

 

 

 


 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值