Linux輸入設備詳解

<什麼是Linux輸入設備>
➤簡介 
    Linux輸入設備總類繁雜,常見的包括有按鍵、鍵盤、觸摸屏、鼠標、搖桿等等,他們自己就是字符設備,而linux內核將這些設備的共同性抽象出來,簡化驅動開發創建了一個input子系統。子系統共分爲三層,如圖1所示。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
圖1  input輸入子系統
驅動層
    驅動層和硬件相關,直接捕捉和獲取硬件設備的數據信息等(包括觸摸屏被按下、按下位置、鼠標移動、鍵盤按下等等),而後將數據信息報告到核心層
核心層
    核心層負責鏈接驅動層和事件處理層設備驅動(device driver)和處理程序(handler)的註冊須要經過核心層來完成,核心層接收來自驅動層的數據信息,並將數據信息選擇對應的handler去處理,最終handler將數據複製到用戶空間。
重要的結構體input_dev、input_handler、input_handle。
input_dev
struct input_dev {
      void *private;
      const char *name;
      const char *phys;
      const char *uniq;
      struct inBITS(KEY_MAX)];      //按鍵事件支持的子事件類型
      unsigned long relbit[NBITS(REL_MAX)];
      unsigned long absbit[NBITS(ABS_Mput_id id;      //與input_handler匹配用的id
      unsigned long evbit[NBITS(EV_MAX)];            //設備支持的事件類型
      unsigned long keybit[NAX)];      //絕對座標事件支持的子事件類型
      unsigned long mscbit[NBITS(MSC_MAX)];
      unsigned long ledbit[NBITS(LED_MAX)];
      unsigned long sndbit[NBITS(SND_MAX)];
      unsigned long ffbit[NBITS(FF_MAX)];
      unsigned long swbit[NBITS(SW_MAX)];
      int ff_effects_max;
      unsigned int keycodemax;
      unsigned int keycodesize;
      void *keycode;
      unsigned int repeat_key;
      struct timer_list timer;
      struct pt_regs *regs;
      int state;
      int sync;
      int abs[ABS_MAX + 1];
      int rep[REP_MAX + 1];
      unsigned long key[NBITS(KEY_MAX)];
      unsigned long led[NBITS(LED_MAX)];
      unsigned long snd[NBITS(SND_MAX)];
      unsigned long sw[NBITS(SW_MAX)];
      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 (*accept)(struct input_dev *dev, struct file *file);
      int (*flush)(struct input_dev *dev, struct file *file);
      int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
      int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
      int (*erase_effect)(struct input_dev *dev, int effect_id);
      struct input_handle *grab;      //當前佔有該設備的handle
      struct mutex mutex;      /* serializes open and close operations */
      unsigned int users;            //打開該設備的用戶量
      struct class_device cdev;
      struct device *dev;      /* will be removed soon */
      int dynalloc;      /* temporarily */
      struct list_head      h_list;      //該鏈表頭用於連接該設備所關聯的input_handle
      struct list_head      node;      //該鏈表頭用於將設備連接到input_dev_list
};

 

 
 
      Input_dev一個很強大的結構體,它把全部的input設備(觸摸屏、鍵盤、鼠標等)的信息都考慮到了,對於觸摸屏來講只用到它裏面的一部分而已,尤爲是加粗的部分,注意該結構體中最後兩行定義的兩個list_head結構體,list_head在/linux/list.h中有定義,深刻跟蹤
struct list_head {
      struct list_head *next, *prev;
};

 

 


 
     該結構體內部並無定義數據而只定義了兩個指向自己結構體的指針,預先說明一下:全部的input device在註冊後會加入一個input_dev_list(輸入 設備鏈表)。全部的eventhandler在註冊後會加入一個input_handler_list(輸入處理程序鏈表),這裏的list_head主要的做用是做爲input_dev_list和input_handler_list的一個節點來保存地址。Input_dev_list和input_handler_list之間的對應關係由input_handle結構體橋接,具體後面說明。
 
➣input_handler
 
struct input_handler {
      void *private;
      void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
      struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);
      void (*disconnect)(struct input_handle *handle);
      const struct file_operations *fops;      //提供給用戶對設備操做的函數指針
      int minor;
      char *name;
      struct input_device_id *id_table;      //與input_dev匹配用的id
      struct input_device_id *blacklist;      //標記的黑名單
      struct list_head      h_list;            //用於連接和該handler相關的handle
      struct list_head      node;            //用於將該handler鏈入input_handler_list
};

 

 
 
 
    input_handler顧名思義,它是用來處理input_dev的一個結構體獲取回來的數據,相關的處理函數在結構裏內部都有定義,最後兩行定義的list_head結構體做用同input_dev所定義的同樣,這裏再也不說明。
注:input_device_id結構體在/linux/mod_devicetable.h中有定義
input_handle
 
struct input_handle {
      void *private;
      int open;      //記錄設備打開次數
      char *name;
      struct input_dev *dev;      //指向所屬的input_dev
      struct input_handler *handler;      //指向所屬的input_handler
      struct list_head      d_node;            //用於鏈入所指向的input_dev的handle鏈表
      struct list_head      h_node;            //用於鏈入所指向的input_handler的handle鏈表
};

 

 
 
 
    能夠看到input_handle中擁有指向input_dev和input_handler的指針,即input_handle是用來關聯input_dev和input_handler
爲何用input_handle來關聯input_dev和input_handler而不將input_dev和input_handler直接對應呢?
     由於一個device能夠對應多個handler,而一個handler也可處理多個device 。就如一個觸摸屏設備能夠對應event handler也能夠對應tseve handler。
input_dev、input_handler、input_handle的關係以下圖2所示。
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
圖2  input_dev,input_handler,input_handle關係圖
<input device的註冊>
      Input device的註冊實際上僅僅只有幾行代碼,由於在input.c中已經將大量的代碼封裝好了,主須要調用幾個關鍵的函數就能完成對input device的註冊。
      在xxx_ts.c中預先定義全局變量struct input_dev  tsdev;而後進入到初始化函數
static int __init xxx_probe(struct platform_device *pdev)
{
      …
      if (!(tsdev = input_allocate_device()))
      {
            printk(KERN_ERR "tsdev: not enough memory\n");
            err = -ENOMEM;
            goto fail;
      }
      …
      tsdev->name = "xxx TouchScreen";            //xxx爲芯片型號
      tsdev ->phys = "xxx/event0";
      tsdev ->id.bustype = BUS_HOST;            //設備id,用於匹配handler的id
      tsdev ->id.vendor  = 0x0005;
      tsdev ->id.product = 0x0001;
      tsdev ->id.version = 0x0100;
      tsdev ->open    = xxx_open;
      tsdev ->close   =xxx_close;
      tsdev ->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_SYN);            //設置支持的事件類型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);           
      input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0);            //限定絕對座標X的取值範圍
      input_set_abs_params(tsdev, ABS_Y, 0, 0x400, 0, 0);            //同上
      input_set_abs_params(tsdev, ABS_PRESSURE, 0, 1000, 0, 0);      //觸摸屏壓力值範圍
      …
     
      If(input_register_device(tsdev) == error)      //註冊設備
            goto fail;
     
      …
     
fail:
      input_free_device(tsdev);
      printk(「ts probe failed\n」);
      return err;
}

 

 
 
 
先看該函數中的tsdev = input_allocate_device(),深刻最終,該函數在input.c中有定義
struct input_dev *input_allocate_device(void)
{
      struct input_dev *dev;
      dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
      if (dev) {
            dev->dynalloc = 1;
            dev->cdev.class = &input_class;
            class_device_initialize(&dev->cdev);
            INIT_LIST_HEAD(&dev->h_list);
            INIT_LIST_HEAD(&dev->node);
      }
      return dev;
}
 
    學過C語言應該都知道malloc函數(開闢內存空間),而這裏的kzalloc也相似於C語言中的malloc同樣,是linux內核空間分配內存函數,後面的GFP_KERNEL標誌意爲常規的內存分配,更多的分配標誌可參閱先關資料。再回到前面的函數中來,接着後面的代碼爲對tsdev結構體中的成員進行賦值初始化,賦值完成後就要開始進入主題:註冊設備了,進入到函數input_register_device(tsdev),該函數在input.c中有定義。
 
int input_register_device(struct input_dev *dev)
{
      static atomic_t input_no = ATOMIC_INIT(0);      //定義原子變量,禁止線程併發訪問
      struct input_handle *handle;            //定義一些變量備後文使用
      struct input_handler *handler;
      struct input_device_id *id;
      const char *path;
      int error;
      if (!dev->dynalloc) {
            printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
                  "Please convert to input_allocate_device() or contact dtor_core@ameritech.net\n",
                  dev->name ? dev->name : "<Unknown>");
            return -EINVAL;
      }
      mutex_init(&dev->mutex);            //互斥鎖初始化,防止臨界區代碼被併發訪問
      set_bit(EV_SYN, dev->evbit);            //設置支持同步事件,input設備所有默認支持同步事件
      /*
      * 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;
      }
      INIT_LIST_HEAD(&dev->h_list);      //初始化須要關聯的handle鏈表頭
      list_add_tail(&dev->node, &input_dev_list);      //將設備添加到input_dev_list中
      dev->cdev.class = &input_class;
      snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
            "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
      error = class_device_add(&dev->cdev);
      if (error)
            return error;
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group);
      if (error)
            goto fail1;
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group);
      if (error)
            goto fail2;
      error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
      if (error)
            goto fail3;
      __module_get(THIS_MODULE);
      path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
      printk(KERN_INFO "input: %s as %s\n",
            dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
      kfree(path);
/*** 遍歷input_handler_list上所有的handler,尋找與該設備匹配的handler  ***/
      list_for_each_entry(handler, &input_handler_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
      input_wakeup_procfs_readers();
      return 0;
 fail3:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
 fail2:      sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
 fail1:      class_device_del(&dev->cdev);
      return error;
}

 

 
 
     先看函數中前面代碼加粗的部分mutex_init(&dev->mutex),與互斥鎖相關的東西,set_bit(EV_SYN, dev->evbit)設置支持同步事件,linux的input子系統默認要支持同步事件。
 
接着看中間代碼加粗的部分,有關list操做的在/linux/list.h中有定義
static inline void INIT_LIST_HEAD(struct list_head *list)
{
      list->next = list;
      list->prev = list;
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
      __list_add(new, head->prev, head);
}
static inline void __list_add(struct list_head *new,
                        struct list_head *prev,
                        struct list_head *next)
{
      next->prev = new;
      new->next = next;
      new->prev = prev;
      prev->next = new;
}

 

 


 
    能夠看得出INIT_LIST_HEAD(struct list_head *list)就是讓list指向結構體的成員再指向其自己完成初始化操做,list_add_tail(struct list_head *new, struct list_head *head)是將new所指向的結構體做爲一節點插入到head所指向鏈表節點以前。所以函數中的list_add_tail(&dev->node, &input_dev_list)就是將該設備添加到input_dev_list中。而input_dev_list這個雙向鏈表在何時被定義了呢,且看input.c源代碼開始部分全局部分有定義:
 
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

 

 
 
 
而LIST_HEAD的宏定義:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
      struct list_head name = LIST_HEAD_INIT(name)

 
 

 


 
 
    顯然這在最開始就已經定義了input_dev_list和input_handler_list這兩個鏈表,且這兩個鏈表都只有一個節點,而在device和handler註冊的時候會在這兩條鏈表中加入節點,list_add_tail(&dev->node, &input_dev_list)就是將該設備添加到input_dev_list中。
 
注意最後代碼加粗的部分,該部分完成了input_dev和input_handler的橋接。
 
//先看list_for_each_entry(handler, &input_handler_list, node),list_for_each_entry在list.h中有定義,其做用至關於一個for循環。其約等價於:(我的理解)
for(handler = input_handler_list表頭所屬的input_handler結構體地址;handler != input_handler_list表尾所屬的input_handler結構體地址;handler++)
{
    
}
 
 
 
    即handler第一次指向input_handler_list的頭部所在的input_handler地址。每循環一次handler就沿着input_handler_list移動到下一個節點,獲得下一節點所屬的handler地址,直到input_handler_list的結尾。而每次的循環須要作什麼呢?要作的就是遍歷input_handler_list上的每個handler,看有哪些handler能與該設備匹配的上。匹配過程:
 
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))//判斷該handler沒有被列入黑名單或者黑名單匹配不成功的話則繼續
      if ((id = input_match_device(handler->id_table, dev)))      //將設備id與handler的id進行匹配,成功則繼續往下
if ((handle = handler->connect(handler, dev, id)))      //連接device與handler,成功則繼續往下
      input_link_handle(handle);      //將handle鏈入input_handler_list和input_dev_list
繼續跟蹤進這些函數
static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
{
      int i;
      for (; id->flags || id->driver_info; id++) {
            if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)      //匹配handler和device  id的flag標誌位
                  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;
            MATCH_BIT(evbit,  EV_MAX);            //匹配id相關標誌位
            MATCH_BIT(keybit, KEY_MAX);
            MATCH_BIT(relbit, REL_MAX);
            MATCH_BIT(absbit, ABS_MAX);
            MATCH_BIT(mscbit, MSC_MAX);
            MATCH_BIT(ledbit, LED_MAX);
            MATCH_BIT(sndbit, SND_MAX);
            MATCH_BIT(ffbit,  FF_MAX);
            MATCH_BIT(swbit,  SW_MAX);
            return id;
      }
      return NULL;
}

 

 
 
 
    該函數用於匹配input_dev結構體和input_handler結構體裏面定義的input_device_id,若二者裏面有任何相關變量不同則匹配失敗(條件真苛刻)。
 
再看handle = handler->connect(handler, dev, id),connect函數即爲static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id),爲何呢會是這個呢,咱們先看evdev.c中的input_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,
};

 

 
 
 
 
可只這裏input_handler結構體裏邊的connect函數即爲evdev_connect函數
 
➣"1",這裏有個定義在evdev.c裏邊的新面孔
struct evdev {
      int exist;
      int open;
      int minor;
      char name[16];
      struct input_handle handle;      //關聯input_handler和input_dev的input_handle
      wait_queue_head_t wait;
      struct evdev_list *grab;
      struct list_head list;
};
 
 
    evdev這個結構體就是拿來應用開發操做的,在這裏就是觸摸屏對應的設備文件實體,結構體前邊定義了記錄設備的一些信息(設備號,打開狀態、設備名字等),這裏還定義了一個input_handle的實體handle(再input_device 中定義並初始化該結構體,再input_handler中查找該結構體,並註冊到其中)沒錯這個handle就是要用來關聯input_dev和input_handler的,後面還有一行加粗的部分後面再作介紹。
➣"2"處,evdev_table[]是一個全局變量的數組,在evdev.c中有定義
#define EVDEV_MINORS            32
static struct evdev *evdev_table[EVDEV_MINORS];

 

 
 
 
 
    在前面已經說明了,一個device能夠對應多個handler,而一個handler也可處理多個device,這裏體現出了後者。既然evdev這個結構體是對應的設備文件實體,由於這個handler可能會處理多個device,所以該handler要處理n個device就會應該有n個evdev實體,而這些實體的地址存放在evdev_table[]這個指針數組中,也就是說該handler最多隻能處理EVDEV_MINORS個device.而2處的這幾句代碼就是要遍歷evdev_table[]這個數組看還有沒有空着的位置,有的話纔會繼續進行下面的程序。
➣「3」處,開闢一個evdev結構體的內存空間
    後面的代碼就是爲evdev結構體變量賦初始值了,其中init_waitqueue_head(&evdev->wait)初始化等待隊列,具體介紹結合/linux/wait.h和查看相關資料。 函數最後獲得evdev結構體內的hanlde地址並返回,此時返回到咱們的int input_register_device(struct input_dev *dev)函數裏面,最後一句
input_link_handle(handle),進入到該函數中發現
static void input_link_handle(struct input_handle *handle)
{
      list_add_tail(&handle->d_node, &handle->dev->h_list);
      list_add_tail(&handle->h_node, &handle->handler->h_list);
}

 

 
 
 
 
    在該函數中也只是將handle中的d_node和h_node分別接入到input_dev和input_handler的h_list中,此時input_dev、input_handler、input_handle三者造成了如圖2所示的關係。
 
    至此設備註冊過程算是所有完成了,可是貌似還有點亂。在整個設備的註冊過程當中函數的嵌套一個接着一個,不只函數嵌套,結構體也使用告終構體和函數嵌套等,在各個函數內也用了大量的結構體指針等等。可是在整個過程當中input_dev、input_handler、input_handle這三個結構體變量的實體也都只有一個。其中input_dev結構體實體在xxx_ts.c中直接定義了一個input_dev的指針全局變量:
struct input_dev *tsdev;
並在初始化函數中開闢了一個input_dev的內存空間並讓tsdev指針指向它:
w55fa95_dev = input_allocate_device();
input_handler結構體在evdev.c中也直接被定義了並初始化了
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,
};

 

 
 
 
    而關聯input_dev和input_handler的input_handle則是在調用連接鏈接函數static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)的時候,在該函數的內部調用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)開闢了一個evdev結構體的內存空間時建立了,input_handle就是使用了evdev結構體內部定義的input_handle。
      一個完整input設備系統不只要有設備,還須要有處理程序input_handler,而上文中主要介紹的是設備註冊、生成、以及和handler搭配的一個過程,接下來討論一下input_handler的註冊。
 
<input_handler的註冊>
      Input_handler是要和用戶層打交道的,在evdev.c中直接定義了一個input_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,            //指向一個evedev的指針數組
};

 
 

 

evedev_fops結構體的定義以下
static 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
};

 

 
 
 
    相信作過linux設備驅動編程的對這都很熟悉了,就是一大堆的用戶接口函數,包括對設備的open、close、read、write、ioctl等函數。
再看第二行代碼加粗的部分.id_table =  evdev_ids, id.table就是前面所說過要和input_dev的id匹配的這麼一個結構體,這裏讓它初始化爲evdev_ids,在看evdev_ids的定義:
static struct input_device_id evdev_ids[] = {
      { .driver_info = 1 },      /* Matches all devices */
      { },                  /* Terminating zero entry */
};

 
 MODULE_DEVICE_TABLE(input, evdev_ids);

 

 
    這裏是一個結構體數組,令數組中第一個結構體的該成員變量driver_info的值爲1,其餘成員變量均未定義,說明這個handler對全部device的id都能匹配得上。數組中的第二個結構體爲空表示結束,用來標識結束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,關於MODULE_DEVICE_TABLE宏定義介紹自行查看相關文獻。
    Input_handler的註冊和input_dev的註冊很類似,大同小異罷了,在evdev.c源碼中顯示定義並初始化了一個input_handler結構體並直接給相關的成員變量賦值了,就是本章開始所將的部分,而後再初始化函數中註冊一個input_handler:
static int __init evdev_init(void)
{
      input_register_handler(&evdev_handler);
      return 0;
}

 
 

 

這裏只調用了一個input_register_handler()函數,看起來應該是很簡單的樣子(相比input_dev的註冊),繼續跟蹤進入到該函數中:
void input_register_handler(struct input_handler *handler)
{
      struct input_dev *dev;
      struct input_handle *handle;
      struct input_device_id *id;
      if (!handler) return;
      INIT_LIST_HEAD(&handler->h_list);
      if (handler->fops != NULL)
            input_table[handler->minor >> 5] = handler;
      list_add_tail(&handler->node, &input_handler_list);
      list_for_each_entry(dev, &input_dev_list, node)
            if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
                  if ((id = input_match_device(handler->id_table, dev)))
                        if ((handle = handler->connect(handler, dev, id)))
                              input_link_handle(handle);
      input_wakeup_procfs_readers();
}

 

 
 
 
    該函數中代碼加粗的部分,與input_register_device()函數裏的一模一樣,這裏再也不作說明。 至此input_handler的註冊已經結束。
 
<input子系統數據結構>
      下圖3是以觸摸屏設備爲例子的input子系統數據結構圖。
 
     
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
圖3  input子系統數據結構圖
    進行到這裏,又多冒出來了一個struct evdev_list的結構體,這在以前並無提到過,由於在註冊input_dev和input_handler過程當中並無用到過,查看該結構體:
struct evdev_list {
      struct input_event buffer[EVDEV_BUFFER_SIZE];      //存放設備數據信息
      int head;      //buffer的下標,標識從設備中過來要存放到buffer的數據的位置
      int tail;            //buffer的下標,標識用戶讀取buffer中數據的下標位置
      struct fasync_struct *fasync;
      struct evdev *evdev;
      struct list_head node;
};

 
 

 

struct input_event {
      struct timeval time;
      __u16 type;
      __u16 code;
      __s32 value;
};

 

 
 
 
 
static int evdev_open(struct inode * inode, struct file * file)
{
      struct evdev_list *list;      //定義一個evdev_list結構體
      int i = iminor(inode) - EVDEV_MINOR_BASE;
      int accept_err;
 
      if (i >= EVDEV_MINORS || !evdev_table[ i] || !evdev_table[ i]->exist)
            return -ENODEV;
 
      if ((accept_err = input_accept_process(&(evdev_table[ i]->handle), file)))
            return accept_err;
 
      if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))     //開闢evdev_list結構體內存空間
            return -ENOMEM;
 
      list->evdev = evdev_table[ i];            //令結構體中的evdev指針指向evdec_table[ i]
      list_add_tail(&list->node, &evdev_table[ i]->list);      //加入鏈表
      file->private_data = list;
 
      if (!list->evdev->open++)            //若是設備沒有被open,則繼續if操做
            if (list->evdev->exist)
                  input_open_device(&list->evdev->handle);      //打開設備
 
      return 0;
}

 

 
 
 
int input_open_device(struct input_handle *handle)
{
      struct input_dev *dev = handle->dev;
      int err;
 
      err = mutex_lock_interruptible(&dev->mutex);
      if (err)
            return err;
      handle->open++;      //handle的內部成員open++
      if (!dev->users++ && dev->open)
            err = dev->open(dev);
      if (err)
            handle->open--;
 
      mutex_unlock(&dev->mutex);
 
      return err;
}

 

 
 
 
input_report_key(tsdev, BTN_TOUCH, 1);      //報告按鍵被按下事件
input_report_abs(tsdev, ABS_X, x);          //報告觸摸屏被按下的x座標值
input_report_abs(tsdev, ABS_Y, y);          //報告觸摸屏被按下的y座標值
input_report_abs(tsdev, ABS_PRESSURE, 1);   //報告觸摸屏被按下的壓力值(0或者1)
input_sync(tsdev);                          //報告同步事件,表示一次事件結束

 

 
 
 
input_report_key(tsdev, BTN_TOUCH, 0);      //報告按鍵被鬆開事件
input_report_abs(tsdev, ABS_PRESSURE, 0);      //報告觸摸屏被按下的壓力值(0或者1)
input_sync(tsdev);            //報告同步事件,表示一次事件結束

 

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

 

 


 
 
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
      struct input_handle *handle;
 
      if (type > EV_MAX || !test_bit(type, dev->evbit))
            return;
 
      add_input_randomness(type, code, value);
 
      switch (type) {
 
                  …
 
            case EV_KEY:            //判斷爲按鍵事件
 
                  if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
                        return;
 
                  if (value == 2)
                        break;
 
                  change_bit(code, dev->key);
 
                  if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
                        dev->repeat_key = code;
                        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
                  }
 
            case EV_ABS:      //判斷爲絕對座標事件
 
                  if (code > ABS_MAX || !test_bit(code, dev->absbit))
                        return;
 
                  if (dev->absfuzz[code]) {
                        if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
                            (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
                              return;
 
                        if ((value > dev->abs[code] - dev->absfuzz[code]) &&
                            (value < dev->abs[code] + dev->absfuzz[code]))
                              value = (dev->abs[code] * 3 + value) >> 2;
 
                        if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
                            (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
                              value = (dev->abs[code] + value) >> 1;
                  }
 
                  if (dev->abs[code] == value)
                        return;
 
                  dev->abs[code] = value;
                  break;
 
                  …
 
      }
 
      if (type != EV_SYN)
            dev->sync = 0;
 
      if (dev->grab)      //判斷有沒有聲明自定義的處理函數(初始化過程當中顯然沒有定義)
            dev->grab->handler->event(dev->grab, type, code, value);
      else
            list_for_each_entry(handle, &dev->h_list, d_node)           //遍歷handle鏈表
                  if (handle->open)                  //若是某節點上的處理程序被打開了
                        handle->handler->event(handle, type, code, value);      //調用處理函數
}

 

 
 
 
static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
      struct evdev *evdev = handle->private;
      struct evdev_list *list;
 
      if (evdev->grab) {                  //顯然grab並無被設置,該條件爲假
            list = evdev->grab;
 
            do_gettimeofday(&list->buffer[list->head].time);
            list->buffer[list->head].type = type;
            list->buffer[list->head].code = code;
            list->buffer[list->head].value = value;
            list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
 
            kill_fasync(&list->fasync, SIGIO, POLL_IN);
      } else
            list_for_each_entry(list, &evdev->list, node) {
 
                  do_gettimeofday(&list->buffer[list->head].time);     //給buffer成員賦值
                  list->buffer[list->head].type = type;
                  list->buffer[list->head].code = code;
                  list->buffer[list->head].value = value;
                  list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
 
                  kill_fasync(&list->fasync, SIGIO, POLL_IN);
            }
 
      wake_up_interruptible(&evdev->wait);      //用來喚醒一個等待隊列(我也不懂)
}

 

 
 
 
static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
{
      struct evdev_list *list = file->private_data;
      int retval;
 
if (count < evdev_event_size())//每次讀取的字節數至少是input_event的大小
      return -EINVAL;
      if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))      //是否知足讀取條件
            return -EAGAIN;
      retval = wait_event_interruptible(list->evdev->wait,
            list->head != list->tail || (!list->evdev->exist));      //等待喚醒,和前面說的等待隊列對應(我也不懂)
      if (retval)
            return retval;
      if (!list->evdev->exist)
            return -ENODEV;
      while (list->head != list->tail && retval + evdev_event_size() <= count) {
            struct input_event *event = (struct input_event *) list->buffer + list->tail;
            if (evdev_event_to_user(buffer + retval, event))      //複製數據到用戶空間
                  return -EFAULT;
            list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
            retval += evdev_event_size();
      }
      return retval;
}

 

 
 
 
 
 
static int evdev_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;
}

 

 
 
 
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">node

相關文章
相關標籤/搜索