天天看點

深層次分析linux2.6input子系統

在linux下,按鍵、觸摸屏、滑鼠等都可以利用input接口函數來實作裝置驅動。

深層次分析linux2.6input子系統

從上圖可知:

輸入子系統由三部分構成:

1 驅動

2 輸入子系統

3 處理函數

其中2,3都是核心已經完成,我們要完成的就是1驅動

裝置用input_dev結構體描述,使用input子系統實作輸入裝置驅動的時候,驅動的核心工作是向系統報告按鍵、觸摸屏、鍵盤、滑鼠等輸入事件(event,通過input_event結構體描述),不再需要關心檔案操作接口,因為input子系統已經完成了檔案操作接口。驅動報告的事件經過InputCore和Eventhandler最終到達使用者空間。

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_KEY、EV_REL和EV_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;

                                  }

       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)

{

 s3c24xx_request_irq();

      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;

}

加載核心子產品:

深層次分析linux2.6input子系統

多了event0裝置檔案

運作應用程式:

深層次分析linux2.6input子系統

下面我們分析input子系統源碼:

在分析源代碼之前,我們要先了解幾個主要的結構:

Input_dev是代表一個輸入裝置。

struct input_dev {

 const char *name;

 const char *phys;

 const char *uniq;

 struct input_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;  

 spinlock_t event_lock;

 struct mutex mutex;

 unsigned int users;

 bool going_away;

 struct device dev;

 struct list_head h_list;  

 struct list_head node;

};

 input_handler表示一個Input裝置對應的handler

struct input_handler {

 void *private;

 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

 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;

 struct list_head h_list;

 struct list_head node;

};

input_handle是用來連接配接input_handler和input_dev的結構

struct input_handle {

 void *private;

 int open;   

 const char *name;

 struct input_dev *dev; 

 struct input_handler *handler;

 struct list_head d_node;

 struct list_head h_node;

};

在核心中,因為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_init_abs_bypass();

 err = class_register(&input_class);

 if (err) {

  printk(KERN_ERR "input: unable to register input_dev class\n");

  return err;

 }

 err = input_proc_init();

 if (err)

  goto fail1;

 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();

 handler = input_table[iminor(inode) >> 5];

 if (!handler || !(new_fops = fops_get(handler->fops))) {

  err = -ENODEV;

  goto out;

 }

 if (!new_fops->open) {

  fops_put(new_fops);

  err = -ENODEV;

  goto out;

 }

 old_fops = file->f_op;

 file->f_op = new_fops;

 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);

 if (handler->fops != NULL) {

  if (input_table[handler->minor >> 5]) {

   retval = -EBUSY;

   goto out;

  }

  input_table[handler->minor >> 5] = handler;

 }

 list_add_tail(&handler->node, &input_handler_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;

  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;

 __set_bit(EV_SYN, dev->evbit);

 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);

 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\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;

 }

 list_add_tail(&dev->node, &input_dev_list);

 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連結清單,找到一個handler的id_table和input_dev的id一直的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;

 if (handler->blacklist && input_match_device(handler->blacklist, dev))

  return -ENODEV;

 id = input_match_device(handler->id_table, dev);

 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\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;

 for (minor = 0; minor < EVDEV_MINORS; minor++)

  if (!evdev_table[minor])

   break;

 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);

 error = input_register_handle(&evdev->handle);

 if (error)

  goto err_free_evdev;

 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_handle将hander handle input_dev連接配接起來,其中handle分别和input_dev和handler相連。(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;

 error = mutex_lock_interruptible(&dev->mutex);

 if (error)

  return error;

 list_add_tail_rcu(&handle->d_node, &dev->h_list);

 mutex_unlock(&dev->mutex);

 list_add_tail(&handle->h_node, &handler->h_list);

 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.c的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();

 handler = input_table[iminor(inode) >> 5];

 if (!handler || !(new_fops = fops_get(handler->fops))) {

  err = -ENODEV;

  goto out;

 }

 if (!new_fops->open) {

  fops_put(new_fops);

  err = -ENODEV;

  goto out;

 }

 old_fops = file->f_op;

 file->f_op = new_fops;

 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_handler。(2)如果evdev_handler的open函數存在就執行evdev_handler的open函數。

上面已經提到:evdev_handler的fops:下面就執行evdev_handler的fops的open了。

static int evdev_open(struct inode *inode, struct file *file)

{

 struct evdev *evdev;

 struct evdev_client *client;

 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 = 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;

 evdev_attach_client(evdev, client);

 error = evdev_open_device(evdev);

 if (error)

  goto err_free_client;

 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;

      }

      handle->open++;

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

             retval = dev->open(dev);

      if (retval) {

             dev->users--;

             if (!--handle->open) {

                    synchronize_rcu();

             }

      }

 out:

      mutex_unlock(&dev->mutex);

      return retval;

}

因為在本按鍵驅動中,沒有設定button_dev的open域,是以這裡input_open_device無意義。

下面就是read函數監聽鍵值了:

Read函數對應的驅動程式是 evdev.c的evdev_read。

static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

{

 struct evdev_client *client = file->private_data;

 struct evdev *evdev = client->evdev;

 struct input_event event;

 int retval;

 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 {

 struct input_event buffer[EVDEV_BUFFER_SIZE];

 int head;

 int tail;

 spinlock_t buffer_lock;

 struct fasync_struct *fasync;

 struct evdev *evdev;

 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;

 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類型做相應處理,該驅動去type為EV_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:

  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);

 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.type = type;

 event.code = code;

 event.value = value;

 rcu_read_lock();

 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)

{

 spin_lock(&client->buffer_lock);

 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,client的head指針加一,那麼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) {

              *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子系統的确是很複雜的。下面畫一個簡單的流程圖。

深層次分析linux2.6input子系統

繼續閱讀