標(biāo)題: ARM Linux內(nèi)核Input輸入子系統(tǒng)淺解 [打印本頁]

作者: liuz    時間: 2014-9-15 19:36
標(biāo)題: ARM Linux內(nèi)核Input輸入子系統(tǒng)淺解

--觸摸屏驅(qū)動為例

第一章、了解linux input子系統(tǒng)
 
      Linux輸入設(shè)備總類繁雜,常見的包括有按鍵、鍵盤、觸摸屏、鼠標(biāo)、搖桿等等,他們本身就是字符設(shè)備linux內(nèi)核將這些設(shè)備共同性抽象出來,簡化驅(qū)動開發(fā)建立了一個input子系統(tǒng)。子系統(tǒng)共分為三層,如圖1所示
圖1  input輸入子系統(tǒng)
 
      驅(qū)動層和硬件相關(guān),直接捕捉和獲取硬件設(shè)備數(shù)據(jù)信息等(包括觸摸屏被按下、按下位置、鼠標(biāo)移動、鍵盤按下等等),然后數(shù)據(jù)信息報告到核心層。核心層負(fù)責(zé)連接驅(qū)動層事件處理層設(shè)備驅(qū)動(device driver)和處理程序(handler)的注冊需要通過核心層來完成,核心層接收來自驅(qū)動層的數(shù)據(jù)信息,并將數(shù)據(jù)信息選擇對應(yīng)handler去處理,最終handler數(shù)據(jù)復(fù)制到用戶空間。
      先了解三個定義在/linux/input.h重要的結(jié)構(gòu)體input_dev、input_handler、input_handle。
struct input_dev {
      void *private;
 
      const char *name;
      const char *phys;
      const char *uniq;
      struct input_id id;      //與input_handler匹配id
 
      unsigned long evbit[NBITS(EV_MAX)];            //設(shè)備支持的事件類型
      unsigned long keybit[NBITS(KEY_MAX)];      //按鍵事件支持的子事件類型
      unsigned long relbit[NBITS(REL_MAX)];
      unsigned long absbit[NBITS(ABS_MAX)];      //絕對坐標(biāo)事件支持的子事件類型
      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];      //絕對坐標(biāo)事件最大鍵值
      int absmin[ABS_MAX + 1];      //絕對坐標(biāo)事件的最小鍵值
      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;      //當(dāng)前占有該設(shè)備的handle
 
      struct mutex mutex;      /* serializes open and close operations */
      unsigned int users;            //打開該設(shè)備的用戶量
 
      struct class_device cdev;
      struct device *dev;      /* will be removed soon */
 
      int dynalloc;      /* temporarily */
 
      struct list_head      h_list;      //鏈表頭用于鏈接該設(shè)備所關(guān)聯(lián)的input_handle
      struct list_head      node;      //鏈表頭用于設(shè)備鏈接到input_dev_list
};
      Input_dev一個很強(qiáng)大的結(jié)構(gòu)體,把所有的input設(shè)備(觸摸屏、鍵盤、鼠標(biāo)等)的信息都考慮到了,對于觸摸屏來說只用到里面的一部分而已,尤其加粗的部分,注意該結(jié)構(gòu)體中最后兩行定義的兩個list_head結(jié)構(gòu)體list_head在/linux/list.h有定義,深入跟蹤
struct list_head {
      struct list_head *next, *prev;
};
結(jié)構(gòu)體內(nèi)部沒有定義數(shù)據(jù)而定義了兩個指向本身結(jié)構(gòu)體的指針,預(yù)先說明一下,所有input device在注冊后加入一個input_dev_list(輸入設(shè)備鏈表)所有的eventhandler在注冊后會加入一個input_handler_list(輸入處理程序鏈表),這里的list_head主要作用是作為input_dev_list和input_handler_list一個節(jié)點(diǎn)來保存地址。Input_dev_list和input_handler_list之間的對應(yīng)關(guān)系由input_handle結(jié)構(gòu)體橋接,具體后面說明。
 
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;      //提供給用戶對設(shè)備操作的函數(shù)指針
      int minor;
      char *name;
 
      struct input_device_id *id_table;      //與input_dev匹配id
      struct input_device_id *blacklist;      //標(biāo)記的黑名單
 
      struct list_head      h_list;            //用于鏈接和該handler相關(guān)handle
      struct list_head      node;            //用于將該handler鏈入input_handler_list
};
input_handler顧名思義,它是用來處理input_dev的一個結(jié)構(gòu)體,相關(guān)的處理函數(shù)在結(jié)構(gòu)里內(nèi)部都有定義,最后兩行定義的list_head結(jié)構(gòu)體作用input_dev所定義的一樣,這里不再說明。
:input_device_id結(jié)構(gòu)體/linux/mod_devicetable.h有定義
 
struct input_handle {
 
      void *private;
 
      int open;      //記錄設(shè)備打開次數(shù)
      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是用來關(guān)聯(lián)input_dev和input_handler。為什么用input_handle來關(guān)聯(lián)input_dev和input_handlerinput_dev和input_handler直接對應(yīng)呢?因?yàn)?/span>一個device可以對應(yīng)多個handler,一個handler也可處理多個device。就如一個觸摸屏設(shè)備可以對應(yīng)event handler可以對應(yīng)tseve handler。
      input_dev、input_handler、input_handle的關(guān)系如下圖2所示。
圖2  input_dev,input_handler,input_handle關(guān)系圖
 
第二章、input device注冊
      Input device的注冊實(shí)際上僅僅只有幾行代碼,因?yàn)樵趇nput.c中已經(jīng)將大量的代碼封裝好了,主需要調(diào)用幾個關(guān)鍵的函數(shù)就能完成對input device的注冊。
      xxx_ts.c中預(yù)先定義全局變量struct input_dev  tsdev;然后進(jìn)入到初始化函數(shù)
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;            //設(shè)備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);            //設(shè)置支持的事件類型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);           
      input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0);            //限定絕對坐標(biāo)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)      //注冊設(shè)備
            goto fail;
     
      …
     
fail:
      input_free_device(tsdev);
      printk(“ts probe failed\n”);
      return err;
}
先看該函數(shù)中的tsdev = input_allocate_device(),深入最終,該函數(shù)在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;
}
學(xué)過C語言應(yīng)該都知道m(xù)alloc函數(shù)(開辟內(nèi)存空間),這里的kzalloc也類似于C語言中的malloc一樣,是linux內(nèi)核空間分配內(nèi)存函數(shù),后面的GFP_KERNEL標(biāo)志意為常規(guī)的內(nèi)存分配,更多的分配標(biāo)志參閱先關(guān)資料(我也不懂)。再回到前面的函數(shù)中來,接著后面的代碼為對tsdev結(jié)構(gòu)體中的成員進(jìn)行賦值初始化,賦值完成后就要開始進(jìn)入主題:注冊設(shè)備了,進(jìn)入到函數(shù)input_register_device(tsdev)該函數(shù)在input.c中有定義。
int input_register_device(struct input_dev *dev)
{
      static atomic_t input_no = ATOMIC_INIT(0);      //定義原子變量,禁止線程并發(fā)訪問
      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);            //互斥鎖初始化,防止臨界區(qū)代碼被并發(fā)訪問
      set_bit(EV_SYN, dev->evbit);            //設(shè)置支持同步事件,input設(shè)備全部默認(rèn)支持同步事件
 
      /*
      * 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);      //初始化需要關(guān)聯(lián)的handle鏈表頭
      list_add_tail(&dev->node, &input_dev_list);      //設(shè)備添加到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,尋找與該設(shè)備匹配的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;
}
先看函數(shù)中前面代碼加粗的部分mutex_init(&dev->mutex),與互斥鎖相關(guān)的東西(我也不太懂),set_bit(EV_SYN, dev->evbit)設(shè)置支持同步事件,linux的input子系統(tǒng)默認(rèn)要支持同步事件。
 
接著看中間代碼加粗的部分,有關(guān)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指向結(jié)構(gòu)體的成員再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)將new指向的結(jié)構(gòu)體作為一節(jié)點(diǎn)插入到head所指向鏈表節(jié)點(diǎn)之前。因此函數(shù)中的list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加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)
顯然這在最開始就已經(jīng)定義了input_dev_list和input_handler_list兩個鏈表,且這兩個鏈表都只有一個節(jié)點(diǎn),device和handler注冊的時候會在這兩條鏈表中加入節(jié)點(diǎn),list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加input_dev_list
 
注意最后代碼加粗的部分,該部分完成了input_dev和input_handler的橋接。先看list_for_each_entry(handler, &input_handler_list, node),list_for_each_entrylist.h中有定義,作用相當(dāng)于一個for循環(huán)。其等價于:(個人理解)
for(handler = input_handler_list表頭所屬input_handler結(jié)構(gòu)體地址;
 handler != input_handler_list表尾所屬input_handler結(jié)構(gòu)體地址;
 handler++)
handler第一次指向input_handler_list頭部所在的input_handler地址。每循環(huán)一次handler就沿著input_handler_list移動到下一個節(jié)點(diǎn),得到下一節(jié)點(diǎn)所屬的handler地址,直到input_handler_list的結(jié)尾。而每次的循環(huán)需要做什么?做的就是遍歷input_handler_list上的每一個handler,看有哪些handler能與該設(shè)備匹配的上。匹配過程
 
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))//判斷該handler沒有被列入黑名單或者黑名單匹配不成功的話則繼續(xù)
      if ((id = input_match_device(handler->id_table, dev)))      //設(shè)備id與handler的id進(jìn)行匹配,成功則繼續(xù)往下
if ((handle = handler->connect(handler, dev, id)))      //鏈接device與handler,成功則繼續(xù)往下
      input_link_handle(handle);      //將handle鏈入input_handler_list和input_dev_list
 
繼續(xù)跟蹤進(jìn)這些函數(shù)
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  idflag標(biāo)志位
                  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相關(guān)標(biāo)志位
            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;
}
函數(shù)用于匹配input_dev結(jié)構(gòu)體和input_handler結(jié)構(gòu)體里面定義的input_device_id,兩者里面任何相關(guān)變量不一樣則匹配失敗(條件真苛刻)。
 
再看handle = handler->connect(handler, dev, id),connect函數(shù)即為static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)為什么呢會是這個呢,我們先看evdev.c中的input_handler結(jié)構(gòu)體定義
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結(jié)構(gòu)體里邊的connect函數(shù)即為evdev_connect函數(shù)
 
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1      struct evdev *evdev;            //定義一個evdev結(jié)構(gòu)體指針
      struct class_device *cdev;
      int minor;
 
2      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
      if (minor == EVDEV_MINORS) {
            printk(KERN_ERR "evdev: no more free evdev devices\n");
            return NULL;
      }
 
3      if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
            return NULL;
 
      INIT_LIST_HEAD(&evdev->list);
      init_waitqueue_head(&evdev->wait);
 
      evdev->exist = 1;
      evdev->minor = minor;
      evdev->handle.dev = dev;
      evdev->handle.name = evdev->name;
      evdev->handle.handler = handler;
      evdev->handle.private = evdev;
      sprintf(evdev->name, "event%d", minor);
 
      evdev_table[minor] = evdev;
 
      cdev = class_device_create(&input_class, &dev->cdev,
                  MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
                  dev->cdev.dev, evdev->name);
 
      /* temporary symlink to keep userspace happy */
      sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
                    evdev->name);
 
      return &evdev->handle;
}
1處,這里有個定義在evdev.c里邊的新面孔
struct evdev {
      int exist;
      int open;
      int minor;
      char name[16];
      struct input_handle handle;      //關(guān)聯(lián)input_handler和input_dev的input_handle
      wait_queue_head_t wait;
      struct evdev_list *grab;
      struct list_head list;
};
evdev這個結(jié)構(gòu)體就是拿來應(yīng)用開發(fā)操作的,在這里就是觸摸屏對應(yīng)的設(shè)備文件實(shí)體,結(jié)構(gòu)體前邊定義了記錄設(shè)備的一些信息(設(shè)備號,打開狀態(tài)、設(shè)備名字等),這里還定義了一個input_handle的實(shí)體handle,沒錯這個handle就是要用來關(guān)聯(lián)input_dev和input_handler的后面還有一加粗的部分后面再做介紹。
2處evdev_table[]一個全局變量的數(shù)組,在evdev.c中有定義
#define EVDEV_MINORS            32
static struct evdev *evdev_table[EVDEV_MINORS];
前面已經(jīng)說明了,一個device可以對應(yīng)多個handler,一個handler也可處理多個device,這里體現(xiàn)出了后者。既然evdev這個結(jié)構(gòu)體是對應(yīng)的設(shè)備文件實(shí)體,因?yàn)檫@個handler可能會處理多個device,因此handler要處理n個device就會應(yīng)該有nevdev實(shí)體,而這些實(shí)體的地址存放在evdev_table[]這個指針數(shù)組中,也就是說該handler最多只能處理EVDEV_MINORSdevice2處的這幾句代碼就是要遍歷evdev_table[]這個數(shù)組還有沒有空著的位置,有話才會繼續(xù)進(jìn)行下面的程序。
3處,開辟一個evdev結(jié)構(gòu)體的內(nèi)存空間,前面有說明過kzalloc函數(shù),這里不再說明。
后面的代碼就是為evdev結(jié)構(gòu)體變量賦初始值了,其中init_waitqueue_head(&evdev->wait)初始化等待隊列,具體介紹結(jié)合/linux/wait.h和查看相關(guān)資料(本人不懂)。
函數(shù)最后得到evdev結(jié)構(gòu)體內(nèi)的hanlde地址并返回,此時返回到我們的int input_register_device(struct input_dev *dev)函數(shù)里面,最后一句
input_link_handle(handle),進(jìn)入到該函數(shù)中發(fā)現(xiàn)
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);
}
該函數(shù)中也只是將handle中的d_node和h_node分別接入到input_dev和input_handler的h_list中此時input_dev、input_handler、input_handle三者形成了如圖2所示的關(guān)系。
 
至此設(shè)備注冊過程算是全部完成了,但是貌似還有點(diǎn)亂整個設(shè)備的注冊過程中函數(shù)的嵌套一個接著一個,不僅函數(shù)嵌套,結(jié)構(gòu)體也使用了結(jié)構(gòu)體和函數(shù)嵌套等,在各個函數(shù)內(nèi)也用了大量的結(jié)構(gòu)體指針等等。在整個過程中input_dev、input_handler、input_handle三個結(jié)構(gòu)體變量的實(shí)體也只有一個。其中input_dev結(jié)構(gòu)體實(shí)體在xxx_ts.c中直接定義了一個input_dev指針全局變量
struct input_dev *tsdev;
在初始化函數(shù)開辟了一個input_dev的內(nèi)存空間并讓tsdev指針指向它:
w55fa95_dev = input_allocate_device();
input_handler結(jié)構(gòu)體在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,
};
關(guān)聯(lián)input_dev和input_handler的input_handle則是在調(diào)用鏈接連接函數(shù)static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)時候,在該函數(shù)的內(nèi)部調(diào)用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)開辟了一個evdev結(jié)構(gòu)體的內(nèi)存空間創(chuàng)建了,input_handle就是使用了evdev結(jié)構(gòu)體內(nèi)部定義的input_handle
      一個完整input設(shè)備系統(tǒng)不僅要有設(shè)備,還需要有處理程序input_handler,上文中主要介紹的是設(shè)備注冊、生成、以及和handler搭配的一個過程,而handler在何時生成?
 
第三章、input_handler注冊。
      Input_handler是要和用戶層打交道的,在evdev.c中直接定義了一個input_handler結(jié)構(gòu)體并初始化了一些內(nèi)部成員變量。
static struct input_handler evdev_handler = {
      .event =      evdev_event,
      .connect =      evdev_connect,
      .disconnect =      evdev_disconnect,
      .fops =            &evdev_fops,            //用戶對設(shè)備操作的函數(shù)指針
      .minor =      EVDEV_MINOR_BASE,
      .name =            "evdev",
      .id_table =      evdev_ids,            //指向一個evedev的指針數(shù)組
};
先看第一加粗的代碼,evedev_fops結(jié)構(gòu)體的定義如下
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設(shè)備驅(qū)動編程的對這都很熟悉了,就是一大堆的用戶接口函數(shù),包括對設(shè)備的open、close、read、write、ioctl等函數(shù)。
看第二行代碼加粗的部分.id_table =      evdev_ids, id.table就是前面所說過要和input_dev的id匹配的這么一個結(jié)構(gòu)體,這里讓它初始化為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);
這里是一個結(jié)構(gòu)體數(shù)組,數(shù)組中第一個結(jié)構(gòu)體的該成員變量driver_info的值為1,其他成員變量均定義,說明這個handler對所有device的id都能匹配得上。數(shù)組中的第二個結(jié)構(gòu)體為空表示結(jié)束,用來標(biāo)識結(jié)束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,關(guān)于MODULE_DEVICE_TABLE宏定義介紹自行查看相關(guān)文獻(xiàn)(我也不懂)。
     
      接下來進(jìn)入正題,input_handler的注冊!
      Input_handler的注冊和input_dev的注冊很相似,大同小異罷了,在evdev.c源碼中顯示定義并初始化了一個input_handler結(jié)構(gòu)體并直接給相關(guān)的成員變量賦值了,就是本章開始所將的部分,然后再初始化函數(shù)中注冊一個input_handler:
static int __init evdev_init(void)
{
      input_register_handler(&evdev_handler);
      return 0;
}
這里只調(diào)用了一個input_register_handler()函數(shù),看起來應(yīng)該是很簡單的樣子(相比input_dev的注冊),繼續(xù)跟蹤進(jìn)入到該函數(shù)中:
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();
}
該函數(shù)中代碼加粗的部分,與input_register_device()函數(shù)里的如出一轍,這里不再做說明。
 
至此input_handler的注冊已經(jīng)結(jié)束
 
第四章、input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)
      3是以觸摸屏設(shè)備為例子的input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖。
圖3  input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖
     
進(jìn)行到這里,又多冒出來了一個struct evdev_list的結(jié)構(gòu)體,在之前并沒有提到過,因?yàn)樵谧詉nput_dev和input_handler過程中并沒有用到過,查看該結(jié)構(gòu)體:
struct evdev_list {
      struct input_event buffer[EVDEV_BUFFER_SIZE];      //存放設(shè)備數(shù)據(jù)信息
      int head;      //buffer下標(biāo),標(biāo)識從設(shè)備中過來存放到buffer的數(shù)據(jù)的位置
      int tail;            //buffer的下標(biāo),標(biāo)識用戶讀取buffer中數(shù)據(jù)的下標(biāo)位置
      struct fasync_struct *fasync;
      struct evdev *evdev;
      struct list_head node;
};
注意里面的input_event結(jié)構(gòu)體,在/linux/input.h有定義:
struct input_event {
      struct timeval time;
      __u16 type;
      __u16 code;
      __s32 value;
};
input_event結(jié)構(gòu)體就是存放著用戶層所需的數(shù)據(jù)信息,這里想必我們都明白,則在struct evdev_list結(jié)構(gòu)體中的struct input_event buffer[EVDEV_BUFFER_SIZE];就是存放著用戶所需的數(shù)據(jù)信息,從硬件設(shè)備(觸摸屏)獲取得到的數(shù)據(jù)信息最終會存放到該數(shù)組中,而struct evdev_list結(jié)構(gòu)體是在什么時候定義的呢?
當(dāng)然是在open的時候,查看open函數(shù):
static int evdev_open(struct inode * inode, struct file * file)
{
      struct evdev_list *list;      //定義一個evdev_list結(jié)構(gòu)體
      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結(jié)構(gòu)體內(nèi)存空間
            return -ENOMEM;
 
      list->evdev = evdev_table[ i];            //結(jié)構(gòu)體中的evdev指針指向evdec_table[ i]
      list_add_tail(&list->node, &evdev_table[ i]->list);      //加入鏈表
      file->private_data = list;
 
      if (!list->evdev->open++)            //如果設(shè)備沒有被open,則繼續(xù)if操作
            if (list->evdev->exist)
                  input_open_device(&list->evdev->handle);      //打開設(shè)備
 
      return 0;
}
注意函數(shù)中代碼加粗的部分,有注釋說明,在最后以一個操作input_open_device(&list->evdev->handle);繼續(xù)跟蹤進(jìn)入該函數(shù)
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的內(nèi)部成員open++
      if (!dev->users++ && dev->open)
            err = dev->open(dev);
      if (err)
            handle->open--;
 
      mutex_unlock(&dev->mutex);
 
      return err;
}
函數(shù)加粗的部分,為什么要讓open++呢?因?yàn)?/span>數(shù)據(jù)要從device傳到handler過程中有一個環(huán)節(jié)需要判斷handle->open是否為真,為真表示設(shè)備已經(jīng)打開數(shù)據(jù)可以傳遞,這點(diǎn)在下一章將會介紹到。
 
第四章、數(shù)據(jù)傳遞過程
      圖1所示,從硬件設(shè)備(觸摸屏)中獲得的數(shù)據(jù)需要經(jīng)過input.c選擇相應(yīng)的handler進(jìn)行處理,最后上報到用戶空間。如何硬件設(shè)備(觸摸屏)獲得數(shù)據(jù),就得看xxx_ts.c中的代碼了,在xxx_ts.c中的源代碼是直接和硬件設(shè)備相關(guān)的。xxx_ts.c中我們一方面要完成觸摸屏設(shè)備相關(guān)的寄存器配置,另一方面要完成將獲得的數(shù)據(jù)上報。
      至于設(shè)備初始化配置方面,根據(jù)每個人使用的ARM芯片的Datasheet去初始化配置寄存器,這里也不需要多說了。不管是通過查詢法還是中斷(沒見過用查詢的),當(dāng)觸摸屏按下時候我們會得到觸摸屏被按下的相關(guān)數(shù)據(jù)(主要是被按下的XY坐標(biāo)值)然后需要將數(shù)據(jù)信息上報,在觸摸屏被按下的時候需要上報:
            input_report_key(tsdev, BTN_TOUCH, 1);      //報告按鍵被按下事件
            input_report_abs(tsdev, ABS_X, x);            //報告觸摸屏被按下的x坐標(biāo)值
            input_report_abs(tsdev, ABS_Y, y);                  //報告觸摸屏被按下的y坐標(biāo)值
            input_report_abs(tsdev, ABS_PRESSURE, 1);      //報告觸摸屏被按下的壓力值(0或者1)
input_sync(tsdev);            //報告同步事件,表示一次事件結(jié)束
當(dāng)觸筆從觸摸屏上抬起時需要上報:
            input_report_key(tsdev, BTN_TOUCH, 0);      //報告按鍵被松開事件
            input_report_abs(tsdev, ABS_PRESSURE, 0);      //報告觸摸屏被按下的壓力值(0或者1)
            input_sync(tsdev);            //報告同步事件,表示一次事件結(jié)束
先不管input_sync()函數(shù),先來看input_report_key()input_report_abs(),這兩個函數(shù)在/linux/input.h中有定義:
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);
}
太坑爹了,這兩個函數(shù)里面也就值調(diào)用了一個函數(shù)(我也不明白linux內(nèi)核開發(fā)者為什么要這么做),繼續(xù)跟蹤查看input_event()函數(shù),在input.c中有定義:
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:      //判斷為絕對坐標(biāo)事件
 
                  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)      //判斷有沒有聲明自定義的處理函數(shù)(初始化過程中顯然沒有定義)
            dev->grab->handler->event(dev->grab, type, code, value);
      else
            list_for_each_entry(handle, &dev->h_list, d_node)           //遍歷handle鏈表
                  if (handle->open)                  //如果某節(jié)點(diǎn)上的處理程序被打開了
                        handle->handler->event(handle, type, code, value);      //調(diào)用處理函數(shù)
}
先看該函數(shù)中的一個switch結(jié)構(gòu),這里只列出了EV_KEY和EV_ABS兩個事件的處理,經(jīng)過switch選擇處理后看最后代碼加粗的部分。list_for_each_entry(handle, &dev->h_list, d_node)      這句類似的前面已經(jīng)介紹過,這里不再說明,接下來if (handle->open),判斷處理程序是否被打開,還記得是什么時候打開的嗎?前面有介紹過在evdev.c中的open函數(shù)里面調(diào)用了input_open_device(&list->evdev->handle)而在input_open_device()有這么一句handle->open++;沒有錯,只有在經(jīng)過evdev_open之后才能這里的條件才能為真,接著看下面的handle->handler->event(handle, type, code, value);繼續(xù)深入跟蹤:
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并沒有被設(shè)置,該條件為
            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);      //用來喚醒一個等待隊列(我也不懂)
}
先看前面struct evdev *evdev = handle->private還記得handler->private嗎,在input_handle結(jié)構(gòu)體中它是一個空指針,在前面有講到在執(zhí)行static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)函數(shù)的時候就被賦值了(evdev->handle.private = evdev;)。
看最后代碼加粗的部分,顯然是給evdev_list結(jié)構(gòu)體中的buffer成員賦從設(shè)備中傳過來的數(shù)據(jù),當(dāng)數(shù)據(jù)存放好了之后,head加1,當(dāng)head的值達(dá)到EVDEV_BUFFER_SIZE又回到0;
 
至此數(shù)據(jù)的傳遞就算是結(jié)束了,接下來就是等著讀走,只要在用戶空間進(jìn)行read操作即可。
 
第五章、數(shù)據(jù)讀取過程
      讀取就變得很簡單了,做過linux編程的都能知道,只要在應(yīng)用中定義了個input_event結(jié)構(gòu)體,通過open打開設(shè)備,然后進(jìn)行read即可,再來看該設(shè)備的read函數(shù):
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())//每次讀取的字節(jié)數(shù)至少是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));      //等待喚醒,前面等待隊列對應(yīng)(我也不懂)
      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))      //復(fù)制數(shù)據(jù)到用戶空間
                  return -EFAULT;
            list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
            retval += evdev_event_size();
      }
      return retval;
}
函數(shù)前面基本上都是在做一系列的條件判斷,判斷是否符合讀取的條件,evdev_event_to_user(buffer + retval, event)這句是真正的把數(shù)據(jù)復(fù)制到用戶空間,進(jìn)入查看
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;
}
函里面也只是調(diào)用了copy_to_user()函數(shù)而已,該函數(shù)相比我們都不會陌生了,就是把event所指向地址空間的數(shù)據(jù)復(fù)制到buffer指向的地址空間中而已。
回到read函數(shù)中來,函數(shù)在末尾返回讀取到的字節(jié)數(shù),至于要把這些數(shù)據(jù)拿去干什么用,那就是應(yīng)用層的事情了。
      可以看到接口函數(shù)中除了read以外還有write、ioctl函數(shù),因?yàn)?/span>對于觸摸屏來說我們主要就是要獲取觸筆在觸摸屏上點(diǎn)擊的坐標(biāo)位置而已,所以write、ioctl等函數(shù)會很少用到,這里就不再做介紹。
 
 
      本文只在一定的層面(本人的理解)linux下的input子系統(tǒng)介紹,里邊還有很多要點(diǎn)沒有辦法去涉及,只是一個入門級的學(xué)習(xí)過程,緊限于在完善的input子系統(tǒng)下進(jìn)行做驅(qū)動修改,而不具備做驅(qū)動開發(fā)的能力。--觸摸屏驅(qū)動為例
第一章、了解linux input子系統(tǒng)
 
      Linux輸入設(shè)備總類繁雜,常見的包括有按鍵、鍵盤、觸摸屏、鼠標(biāo)、搖桿等等,他們本身就是字符設(shè)備linux內(nèi)核將這些設(shè)備共同性抽象出來,簡化驅(qū)動開發(fā)建立了一個input子系統(tǒng)。子系統(tǒng)共分為三層,如圖1所示。
圖1  input輸入子系統(tǒng)
 
      驅(qū)動層和硬件相關(guān),直接捕捉和獲取硬件設(shè)備數(shù)據(jù)信息等(包括觸摸屏被按下、按下位置、鼠標(biāo)移動、鍵盤按下等等),然后數(shù)據(jù)信息報告到核心層。核心層負(fù)責(zé)連接驅(qū)動層事件處理層設(shè)備驅(qū)動(device driver)和處理程序(handler)的注冊需要通過核心層來完成,核心層接收來自驅(qū)動層的數(shù)據(jù)信息,并將數(shù)據(jù)信息選擇對應(yīng)handler去處理,最終handler數(shù)據(jù)復(fù)制到用戶空間。
      先了解三個定義在/linux/input.h重要的結(jié)構(gòu)體input_dev、input_handler、input_handle。
struct input_dev {
      void *private;
 
      const char *name;
      const char *phys;
      const char *uniq;
      struct input_id id;      //與input_handler匹配id
 
      unsigned long evbit[NBITS(EV_MAX)];            //設(shè)備支持的事件類型
      unsigned long keybit[NBITS(KEY_MAX)];      //按鍵事件支持的子事件類型
      unsigned long relbit[NBITS(REL_MAX)];
      unsigned long absbit[NBITS(ABS_MAX)];      //絕對坐標(biāo)事件支持的子事件類型
      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];      //絕對坐標(biāo)事件最大鍵值
      int absmin[ABS_MAX + 1];      //絕對坐標(biāo)事件的最小鍵值
      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;      //當(dāng)前占有該設(shè)備的handle
 
      struct mutex mutex;      /* serializes open and close operations */
      unsigned int users;            //打開該設(shè)備的用戶量
 
      struct class_device cdev;
      struct device *dev;      /* will be removed soon */
 
      int dynalloc;      /* temporarily */
 
      struct list_head      h_list;      //鏈表頭用于鏈接該設(shè)備所關(guān)聯(lián)的input_handle
      struct list_head      node;      //鏈表頭用于設(shè)備鏈接到input_dev_list
};
      Input_dev一個很強(qiáng)大的結(jié)構(gòu)體,把所有的input設(shè)備(觸摸屏、鍵盤、鼠標(biāo)等)的信息都考慮到了,對于觸摸屏來說只用到里面的一部分而已,尤其加粗的部分,注意該結(jié)構(gòu)體中最后兩行定義的兩個list_head結(jié)構(gòu)體,list_head在/linux/list.h有定義,深入跟蹤
struct list_head {
      struct list_head *next, *prev;
};
結(jié)構(gòu)體內(nèi)部沒有定義數(shù)據(jù)而定義了兩個指向本身結(jié)構(gòu)體的指針,預(yù)先說明一下,所有input device在注冊后加入一個input_dev_list(輸入設(shè)備鏈表),所有的eventhandler在注冊后會加入一個input_handler_list(輸入處理程序鏈表),這里的list_head主要作用是作為input_dev_list和input_handler_list一個節(jié)點(diǎn)來保存地址。Input_dev_list和input_handler_list之間的對應(yīng)關(guān)系由input_handle結(jié)構(gòu)體橋接,具體后面說明。
 
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;      //提供給用戶對設(shè)備操作的函數(shù)指針
      int minor;
      char *name;
 
      struct input_device_id *id_table;      //與input_dev匹配id
      struct input_device_id *blacklist;      //標(biāo)記的黑名單
 
      struct list_head      h_list;            //用于鏈接和該handler相關(guān)handle
      struct list_head      node;            //用于將該handler鏈入input_handler_list
};
input_handler顧名思義,它是用來處理input_dev的一個結(jié)構(gòu)體,相關(guān)的處理函數(shù)在結(jié)構(gòu)里內(nèi)部都有定義,最后兩行定義的list_head結(jié)構(gòu)體作用input_dev所定義的一樣,這里不再說明。
:input_device_id結(jié)構(gòu)體/linux/mod_devicetable.h有定義
 
struct input_handle {
 
      void *private;
 
      int open;      //記錄設(shè)備打開次數(shù)
      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是用來關(guān)聯(lián)input_dev和input_handler。為什么用input_handle來關(guān)聯(lián)input_dev和input_handlerinput_dev和input_handler直接對應(yīng)呢?因?yàn)?/span>一個device可以對應(yīng)多個handler,一個handler也可處理多個device。就如一個觸摸屏設(shè)備可以對應(yīng)event handler可以對應(yīng)tseve handler。
      input_dev、input_handler、input_handle的關(guān)系如下圖2所示
圖2  input_dev,input_handler,input_handle關(guān)系圖
 
第二章、input device注冊
      Input device的注冊實(shí)際上僅僅只有幾行代碼,因?yàn)樵趇nput.c中已經(jīng)將大量的代碼封裝好了,主需要調(diào)用幾個關(guān)鍵的函數(shù)就能完成對input device的注冊。
      xxx_ts.c中預(yù)先定義全局變量struct input_dev  tsdev;然后進(jìn)入到初始化函數(shù)
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;            //設(shè)備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);            //設(shè)置支持的事件類型
tsdev ->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);           
      input_set_abs_params(tsdev, ABS_X, 0, 0x400, 0, 0);            //限定絕對坐標(biāo)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)      //注冊設(shè)備
            goto fail;
     
      …
     
fail:
      input_free_device(tsdev);
      printk(“ts probe failed\n”);
      return err;
}
先看該函數(shù)中的tsdev = input_allocate_device()深入最終,該函數(shù)在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;
}
學(xué)過C語言應(yīng)該都知道m(xù)alloc函數(shù)(開辟內(nèi)存空間),這里的kzalloc也類似于C語言中的malloc一樣,是linux內(nèi)核空間分配內(nèi)存函數(shù),后面的GFP_KERNEL標(biāo)志意為常規(guī)的內(nèi)存分配,更多的分配標(biāo)志參閱先關(guān)資料(我也不懂)。再回到前面的函數(shù)中來,接著后面的代碼為對tsdev結(jié)構(gòu)體中的成員進(jìn)行賦值初始化,賦值完成后就要開始進(jìn)入主題:注冊設(shè)備了,進(jìn)入到函數(shù)input_register_device(tsdev),該函數(shù)在input.c中有定義。
int input_register_device(struct input_dev *dev)
{
      static atomic_t input_no = ATOMIC_INIT(0);      //定義原子變量,禁止線程并發(fā)訪問
      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);            //互斥鎖初始化,防止臨界區(qū)代碼被并發(fā)訪問
      set_bit(EV_SYN, dev->evbit);            //設(shè)置支持同步事件,input設(shè)備全部默認(rèn)支持同步事件
 
      /*
      * 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);      //初始化需要關(guān)聯(lián)的handle鏈表頭
      list_add_tail(&dev->node, &input_dev_list);      //設(shè)備添加到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,尋找與該設(shè)備匹配的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;
}
先看函數(shù)中前面代碼加粗的部分mutex_init(&dev->mutex),與互斥鎖相關(guān)的東西(我也不太懂),set_bit(EV_SYN, dev->evbit)設(shè)置支持同步事件,linux的input子系統(tǒng)默認(rèn)要支持同步事件。
 
接著看中間代碼加粗的部分,有關(guān)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指向結(jié)構(gòu)體的成員再指向其本身完成初始化操作,list_add_tail(struct list_head *new, struct list_head *head)將new指向的結(jié)構(gòu)體作為一節(jié)點(diǎn)插入到head所指向鏈表節(jié)點(diǎn)之前。因此函數(shù)中的list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加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)
顯然這在最開始就已經(jīng)定義了input_dev_list和input_handler_list兩個鏈表,且這兩個鏈表都只有一個節(jié)點(diǎn),device和handler注冊的時候會在這兩條鏈表中加入節(jié)點(diǎn),list_add_tail(&dev->node, &input_dev_list)就是將該設(shè)備添加input_dev_list。
 
注意最后代碼加粗的部分,該部分完成了input_dev和input_handler的橋接。先看list_for_each_entry(handler, &input_handler_list, node)list_for_each_entrylist.h中有定義,作用相當(dāng)于一個for循環(huán)。其等價于:(個人理解)
for(handler = input_handler_list表頭所屬input_handler結(jié)構(gòu)體地址;
 handler != input_handler_list表尾所屬input_handler結(jié)構(gòu)體地址;
 handler++)
handler第一次指向input_handler_list頭部所在的input_handler地址。每循環(huán)一次handler就沿著input_handler_list移動到下一個節(jié)點(diǎn),得到下一節(jié)點(diǎn)所屬的handler地址,直到input_handler_list的結(jié)尾。而每次的循環(huán)需要做什么?做的就是遍歷input_handler_list上的每一個handler,看有哪些handler能與該設(shè)備匹配的上。匹配過程
 
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))//判斷該handler沒有被列入黑名單或者黑名單匹配不成功的話則繼續(xù)
      if ((id = input_match_device(handler->id_table, dev)))      //設(shè)備id與handler的id進(jìn)行匹配,成功則繼續(xù)往下
if ((handle = handler->connect(handler, dev, id)))      //鏈接device與handler,成功則繼續(xù)往下
      input_link_handle(handle);      //將handle鏈入input_handler_list和input_dev_list
 
繼續(xù)跟蹤進(jìn)這些函數(shù)
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  idflag標(biāo)志位
                  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相關(guān)標(biāo)志位
            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;
}
函數(shù)用于匹配input_dev結(jié)構(gòu)體和input_handler結(jié)構(gòu)體里面定義的input_device_id,兩者里面任何相關(guān)變量不一樣則匹配失敗(條件真苛刻)。
 
再看handle = handler->connect(handler, dev, id),connect函數(shù)即為static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id),為什么呢會是這個呢,我們先看evdev.c中的input_handler結(jié)構(gòu)體定義
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結(jié)構(gòu)體里邊的connect函數(shù)即為evdev_connect函數(shù)
 
static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
1      struct evdev *evdev;            //定義一個evdev結(jié)構(gòu)體指針
      struct class_device *cdev;
      int minor;
 
2      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
      if (minor == EVDEV_MINORS) {
            printk(KERN_ERR "evdev: no more free evdev devices\n");
            return NULL;
      }
 
3      if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)))
            return NULL;
 
      INIT_LIST_HEAD(&evdev->list);
      init_waitqueue_head(&evdev->wait);
 
      evdev->exist = 1;
      evdev->minor = minor;
      evdev->handle.dev = dev;
      evdev->handle.name = evdev->name;
      evdev->handle.handler = handler;
      evdev->handle.private = evdev;
      sprintf(evdev->name, "event%d", minor);
 
      evdev_table[minor] = evdev;
 
      cdev = class_device_create(&input_class, &dev->cdev,
                  MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
                  dev->cdev.dev, evdev->name);
 
      /* temporary symlink to keep userspace happy */
      sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj,
                    evdev->name);
 
      return &evdev->handle;
}
1處,這里有個定義在evdev.c里邊的新面孔
struct evdev {
      int exist;
      int open;
      int minor;
      char name[16];
      struct input_handle handle;      //關(guān)聯(lián)input_handler和input_dev的input_handle
      wait_queue_head_t wait;
      struct evdev_list *grab;
      struct list_head list;
};
evdev這個結(jié)構(gòu)體就是拿來應(yīng)用開發(fā)操作的,在這里就是觸摸屏對應(yīng)的設(shè)備文件實(shí)體,結(jié)構(gòu)體前邊定義了記錄設(shè)備的一些信息(設(shè)備號,打開狀態(tài)、設(shè)備名字等),這里還定義了一個input_handle的實(shí)體handle,沒錯這個handle就是要用來關(guān)聯(lián)input_dev和input_handler的,后面還有一加粗的部分后面再做介紹。
2處,evdev_table[]一個全局變量的數(shù)組在evdev.c中有定義
#define EVDEV_MINORS            32
static struct evdev *evdev_table[EVDEV_MINORS];
前面已經(jīng)說明了,一個device可以對應(yīng)多個handler,一個handler也可處理多個device,這里體現(xiàn)出了后者。既然evdev這個結(jié)構(gòu)體是對應(yīng)的設(shè)備文件實(shí)體,因?yàn)檫@個handler可能會處理多個device,因此handler要處理n個device就會應(yīng)該有nevdev實(shí)體,而這些實(shí)體的地址存放在evdev_table[]這個指針數(shù)組中,也就是說該handler最多只能處理EVDEV_MINORSdevice,2處的這幾句代碼就是要遍歷evdev_table[]這個數(shù)組還有沒有空著的位置,有話才會繼續(xù)進(jìn)行下面的程序。
3處,開辟一個evdev結(jié)構(gòu)體的內(nèi)存空間,前面有說明過kzalloc函數(shù),這里不再說明。
后面的代碼就是為evdev結(jié)構(gòu)體變量賦初始值了,其中init_waitqueue_head(&evdev->wait)初始化等待隊列,具體介紹結(jié)合/linux/wait.h和查看相關(guān)資料(本人不懂)。
函數(shù)最后得到evdev結(jié)構(gòu)體內(nèi)的hanlde地址并返回,此時返回到我們的int input_register_device(struct input_dev *dev)函數(shù)里面,最后一句
input_link_handle(handle),進(jìn)入到該函數(shù)中發(fā)現(xiàn)
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);
}
該函數(shù)中也只是將handle中的d_node和h_node分別接入到input_dev和input_handler的h_list中此時input_dev、input_handler、input_handle三者形成了如圖2所示的關(guān)系。
 
至此設(shè)備注冊過程算是全部完成了,但是貌似還有點(diǎn)亂。整個設(shè)備的注冊過程中函數(shù)的嵌套一個接著一個,不僅函數(shù)嵌套,結(jié)構(gòu)體也使用了結(jié)構(gòu)體和函數(shù)嵌套等,在各個函數(shù)內(nèi)也用了大量的結(jié)構(gòu)體指針等等。在整個過程中input_dev、input_handler、input_handle三個結(jié)構(gòu)體變量的實(shí)體也只有一個。其中input_dev結(jié)構(gòu)體實(shí)體在xxx_ts.c中直接定義了一個input_dev指針全局變量
struct input_dev *tsdev;
在初始化函數(shù)開辟了一個input_dev的內(nèi)存空間并讓tsdev指針指向它:
w55fa95_dev = input_allocate_device();
input_handler結(jié)構(gòu)體在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,
};
關(guān)聯(lián)input_dev和input_handler的input_handle則是在調(diào)用鏈接連接函數(shù)static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)時候,在該函數(shù)的內(nèi)部調(diào)用evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL)開辟了一個evdev結(jié)構(gòu)體的內(nèi)存空間創(chuàng)建了,input_handle就是使用了evdev結(jié)構(gòu)體內(nèi)部定義的input_handle
      一個完整input設(shè)備系統(tǒng)不僅要有設(shè)備,還需要有處理程序input_handler,上文中主要介紹的是設(shè)備注冊、生成、以及和handler搭配的一個過程,而handler在何時生成
 
第三章、input_handler注冊。
      Input_handler是要和用戶層打交道的,在evdev.c中直接定義了一個input_handler結(jié)構(gòu)體并初始化了一些內(nèi)部成員變量。
static struct input_handler evdev_handler = {
      .event =      evdev_event,
      .connect =      evdev_connect,
      .disconnect =      evdev_disconnect,
      .fops =            &evdev_fops,            //用戶對設(shè)備操作的函數(shù)指針
      .minor =      EVDEV_MINOR_BASE,
      .name =            "evdev",
      .id_table =      evdev_ids,            //指向一個evedev的指針數(shù)組
};
先看第一加粗的代碼,evedev_fops結(jié)構(gòu)體的定義如下
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設(shè)備驅(qū)動編程的對這都很熟悉了,就是一大堆的用戶接口函數(shù),包括對設(shè)備的open、close、read、write、ioctl等函數(shù)。
看第二行代碼加粗的部分.id_table =      evdev_ids, id.table就是前面所說過要和input_dev的id匹配的這么一個結(jié)構(gòu)體,這里讓它初始化為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);
這里是一個結(jié)構(gòu)體數(shù)組,數(shù)組中第一個結(jié)構(gòu)體的該成員變量driver_info的值為1,其他成員變量均定義,說明這個handler對所有device的id都能匹配得上。數(shù)組中的第二個結(jié)構(gòu)體為空表示結(jié)束,用來標(biāo)識結(jié)束配合下面的MODULE_DEVICE_TABLE(input, evdev_ids)使用,關(guān)于MODULE_DEVICE_TABLE宏定義介紹自行查看相關(guān)文獻(xiàn)(我也不懂)。
     
      接下來進(jìn)入正題,input_handler的注冊!
      Input_handler的注冊和input_dev的注冊很相似,大同小異罷了,在evdev.c源碼中顯示定義并初始化了一個input_handler結(jié)構(gòu)體并直接給相關(guān)的成員變量賦值了,就是本章開始所將的部分,然后再初始化函數(shù)中注冊一個input_handler:
static int __init evdev_init(void)
{
      input_register_handler(&evdev_handler);
      return 0;
}
這里只調(diào)用了一個input_register_handler()函數(shù),看起來應(yīng)該是很簡單的樣子(相比input_dev的注冊),繼續(xù)跟蹤進(jìn)入到該函數(shù)中:
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();
}
該函數(shù)中代碼加粗的部分,與input_register_device()函數(shù)里的如出一轍,這里不再做說明。
 
至此input_handler的注冊已經(jīng)結(jié)束
 
第四章、input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)
      3是以觸摸屏設(shè)備為例子的input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖。
圖3  input子系統(tǒng)數(shù)據(jù)結(jié)構(gòu)圖
     
進(jìn)行到這里,又多冒出來了一個struct evdev_list的結(jié)構(gòu)體,在之前并沒有提到過,因?yàn)樵谧詉nput_dev和input_handler過程中并沒有用到過,查看該結(jié)構(gòu)體:
struct evdev_list {
      struct input_event buffer[EVDEV_BUFFER_SIZE];      //存放設(shè)備數(shù)據(jù)信息
      int head;      //buffer下標(biāo),標(biāo)識從設(shè)備中過來存放到buffer的數(shù)據(jù)的位置
      int tail;            //buffer的下標(biāo),標(biāo)識用戶讀取buffer中數(shù)據(jù)的下標(biāo)位置
      struct fasync_struct *fasync;
      struct evdev *evdev;
      struct list_head node;
};
注意里面的input_event結(jié)構(gòu)體在/linux/input.h有定義:
struct input_event {
      struct timeval time;
      __u16 type;
      __u16 code;
      __s32 value;
};
input_event結(jié)構(gòu)體就是存放著用戶層所需的數(shù)據(jù)信息,這里想必我們都明白,則在struct evdev_list結(jié)構(gòu)體中的struct input_event buffer[EVDEV_BUFFER_SIZE];就是存放著用戶所需的數(shù)據(jù)信息,從硬件設(shè)備(觸摸屏)獲取得到的數(shù)據(jù)信息最終會存放到該數(shù)組中,而struct evdev_list結(jié)構(gòu)體是在什么時候定義的呢?
當(dāng)然是在open的時候,查看open函數(shù):
static int evdev_open(struct inode * inode, struct file * file)
{
      struct evdev_list *list;      //定義一個evdev_list結(jié)構(gòu)體
      int i = iminor(inode) - EVDEV_MINOR_BASE;
      int accept_err;
 
      if (i >= EVDEV_MINORS || !evdev_table || !evdev_table->exist)
            return -ENODEV;
 
      if ((accept_err = input_accept_process(&(evdev_table->handle), file)))
            return accept_err;
 
      if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))     //開辟evdev_list結(jié)構(gòu)體內(nèi)存空間
            return -ENOMEM;
 
      list->evdev = evdev_table;            //結(jié)構(gòu)體中的evdev指針指向evdec_table





歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1