找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 20219|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

ARM Linux內(nèi)核Input輸入子系統(tǒng)淺解

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:50962 發(fā)表于 2014-9-15 19:36 | 只看該作者 回帖獎勵 |倒序?yàn)g覽 |閱讀模式

--觸摸屏驅(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ù)信息報(bào)告到核心層。核心層負(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)初始化等待隊(duì)列,具體介紹結(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)位置
第一章、了解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ù)信息報(bào)告到核心層。核心層負(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_handlerinput_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)初始化等待隊(duì)列,具體介紹結(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)位置
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表