我對linux驅動 輸入子系統的理解

前言:學習驅動有一段時間了,之前所學習的驅動都是驅動最原始的寫法,也移植過很多驅動,然而並沒有對內核自帶的驅動進行深入的瞭解,於是出於好奇,就認真的學習了一下內核自帶的驅動,我發現之前所學習的驅動都是將file_operations結構體直接定義在驅動定義的C代碼裏的,當然還有它裏面的一些函數操作,可是我看內核自帶的驅動代碼的時候我發現裏面是用另外一種機制來實現這些的,那便是子系統!初次接觸到子系統的我可謂一臉懵逼啊!腦海裏浮現一大堆問號,比如:子系統是個什麼鬼?,它是用來幹嘛的?以及它在內核中它是怎麼定義的?出於這些問題,爲了搞明白它是怎麼一回事兒,我並決定一探究竟!於是選擇內核自帶的按鍵驅動進行了具體的分析與學習!

     1.什麼是子系統?

        內核是操作系統的核心。Linux內核提供很多基本功能,如虛擬內存、多任務、共享庫、需求加載、共享寫時拷貝(Copy-On-Write)以及網絡功能等。增加各種不同功能導致內核代碼不斷增加。 
          Linux內核把不同功能分成不同的子系統的方法,通過一種整體的結構把各種功能集合在一起,提高了工作效率。同時還提供動態加載模塊的方式,爲動態修改內核功能提供了靈活性。

    2.linux 驅動子系統

         linux內核中自帶了很多的驅動子系統,其中比較典型的有:input子系統,led子系統,framebuffer子系統(LCD),I2c子系統等,這些子系統它是通過一層一層的函數傳遞封裝,它實現了設備驅動的註冊,以及定義了file-operations結構體裏面的各種函數操作等,不需要在單獨的設備驅動代碼中進行註冊,定義,直接調用相應的的子系統即可,

      3.linux input子系統

      以前學了單獨的按鍵設備驅動以及led驅動,實際上,在linux中實現這些設備驅動,有一種更爲推薦的方法,就是input輸入子系統。平常我們的按鍵,觸摸屏,鼠標等輸入型設備都可以利用input接口(這個接口是什麼呢?可以看我對按鍵驅動分析就一目瞭然)來簡化驅動程序並實現設備驅動。Input子系統處理輸入事務,任何輸入設備的驅動程序都可以通過Input輸入子系統提供的接口註冊到內核,利用子系統提供的功能來與用戶空間交互.

         我對input 子系統的理解是:其實input 子系統是對linux輸入設備驅動進行了高度抽象最終分成了三層:包括input設備驅動層,核心層,事件處理層,也就是說我們之前移植的按鍵,usb,觸摸屏驅動代碼也只是子系統的一部分,起初我們自己編寫的那些驅動代碼都是分散的,按鍵是按鍵,led是led,都沒有對這些不同類別的輸入設備驅動統一起來,也就是說input 子系統這種機制的出現,最大的優點我覺得就是爲內核大大的簡化了驅動程序!!!這個input子系統最神祕之處就是它的核心層部分,這個核心層做了什麼呢?它在內核中是怎麼定義的?對於這兩個問題只有深入理解了input.c這個代碼之後,並豁然開朗!!!當然我有分析,繼續看下文...

下面我們來好好學習一下這神祕的input子系統吧!

   4.input子系統原理

         linux輸入子系統的體系結構可以分爲三個層面,分別爲:硬件驅動層子系統核心層、事件處理層 ,意思就是每個層次只是負責單獨的一個功能,無需參與其他的功能,有點類似函數的封裝, 三個層面具體的功能如下:

         硬件驅動層:其中硬件驅動層負責操作具體的硬件設備,這層的代碼是針對具體的驅動程序的,需要驅動程序的作者來編寫。
    子系統核心層:子系統核心層是鏈接其他兩個層之間的紐帶與橋樑,向下提供驅動層的接口,向上提供事件處理層的接口。
       事件處理層:事件處理層負責與用戶程序打交道,將硬件驅動層傳來的事件報告給用戶程序。

 關於linux輸入子系統的框架結構如下圖所示:


****************************************************************************************************************************************************************************************

分析:由上圖所展現的內容就是linux輸入子系統的分層結構。

/dev/input目錄下顯示的是已經註冊在內核中的設備編程接口,用戶通過open這些設備文件來打開不同的輸入設備進行硬件操作。

事件處理層爲不同硬件類型提供了用戶訪問及處理接口。例如當我們打開設備/dev/input/mice時,會調用到事件處理層的Mouse Handler來處理輸入事件,這也使得設備驅動層無需關心設備文件的操作,因爲Mouse Handler已經有了對應事件處理的方法。

輸入子系統由內核代碼drivers/input/input.c構成,它的存在屏蔽了用戶到設備驅動的交互細節,爲設備驅動層和事件處理層提供了相互通信的統一界面。

*****************************************************************************************************************************************************************************************


  linux輸入子系統的事件處理機制


******************************************************************************************************************************************************************************

分析:由上圖可知輸入子系統核心層提供的支持以及如何上報事件到input event drivers。

作爲輸入設備的驅動開發者,需要做以下幾步:

1. 在驅動加載模塊中,設置你的input設備支持的事件類型。(事件類型有:EV_SYN     0x00    同步事件;EV_KEY     0x01    按鍵事件;EV_LED     0x11    按鍵/設備燈等)

2. 註冊中斷處理函數,例如鍵盤設備需要編寫按鍵的擡起、放下,觸摸屏設備需要編寫按下、擡起、絕對移動,鼠標設備需要編寫單擊、擡起、相對移動,並且需要在必要的時候提交硬件數據(鍵值/座標/狀態等等)

3.將輸入設備註冊到輸入子系統中

*****************************************************************************************************************************************************************************************

5.內核自帶的按鍵驅動分析

****************************************************************************************************************************************************************************

其實在linux內核中實現按鍵功能,主要的涉及到兩方面的內容:linux的中斷處理輸入子系統

Linux中斷分爲兩個部分:前半部後半部。前半部是實際響應中斷的函數,需要用request_irq註冊;後半部是由前半部調度,並在一個更安全的時間來執行的函數,該函數就是具體負責執行中斷的內容。前半部不允許被其他中斷干擾,因此它的內容短小,而執行後半部時可以響應其他中斷。這種機制的好處是可以迅速的響應中斷。

輸入子系統有三個核心結構體input_devinput_handleinput_handler。input_dev表示一個輸入設備,包含輸入設備的一些相關信息;input_handler表示對輸入事件的具體處理,它爲輸入設備的功能實現了一個接口;input_handle是用來連接輸入設備和輸入事件。輸入子系統主要的任務就是把這三個結構體連接在一起。

      參考網上很多人總結的博客,大部分人分析linux input子系統都是從硬件驅動層,核心層,事件處理層這三個層面來分析的,

感覺他們分析的蠻好,那麼我也按這個套路來學習一下按鍵驅動子系統!!!我將從底層向用戶層的方向來分析這個input 子系統!

*****************************************************************************************************************************************************************************

1.設備驅動層

在驅動層,Linux提供了一個通用的基於GPIO的按鍵驅動程序文件——gpio_keys.c(它是在drivers/input/keyboard目錄下),

我們的按鍵驅動也是調用該文件的。

[zoulei@CentOS linux-3.0]$ vim drivers/input/keyboard/gpio_keys.c

static int __init gpio_keys_init(void)/*按鍵的初始化*/
{
       return platform_driver_register(&gpio_keys_device_driver);//註冊驅動

}

/*gpio_keys_device_driver的內容爲*/
static struct platform_drivergpio_keys_device_driver = {
       .probe            = gpio_keys_probe,

       .remove          = __devexit_p(gpio_keys_remove),

       .driver            = {
              .name      = "gpio-keys",                            //與設備名一致,即與mach-smdk.c文件中的平臺設備中.name一致
              .owner    = THIS_MODULE,
              .pm  = &gpio_keys_pm_ops,
              .of_match_table= gpio_keys_of_match,
       }
};




在這個gpio_keys.c文件中我覺得最爲重要且最值得注意的就是裏面的probe函數,它與我們之前自己編寫的代碼最大的不同之處就是裏面出現了input結構體,話不多說請看源代碼!

448 static int __devinit gpio_keys_probe(struct platform_device *pdev)
449 {
450         struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
451         struct gpio_keys_drvdata *ddata;
452         struct device *dev = &pdev->dev;
453         struct input_dev *input;
454         int i, error;
455         int wakeup = 0;
456
457         ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +   //kzalloc爲驅動數據ddata開闢一段內存空間,並清零,它實現了kmalloc與memset兩個函數的功能
458                         pdata->nbuttons * sizeof(struct gpio_button_data),

459                         GFP_KERNEL);
460         input = input_allocate_device();//爲輸入設備input開闢一段內存空間
461         if (!ddata || !input) {
462                 dev_err(dev, "failed to allocate state\n");
463                 error = -ENOMEM;
464                 goto fail1;
465         }
466
467         ddata->input = input;
468         ddata->n_buttons = pdata->nbuttons;
469         ddata->enable = pdata->enable;
470         ddata->disable = pdata->disable;
471         mutex_init(&ddata->disable_lock);
472
473         platform_set_drvdata(pdev, ddata); //把驅動數據ddata以私有數據形式存放在平臺總線設備pdev中,以備日後使用
474         input_set_drvdata(input, ddata);  //把驅動數據ddata以私有數據形式存放在輸入設備input中,以備日後使用
475
476         input->name = pdata->name ? : pdev->name;  //爲輸入設備input幅值
477         input->phys = "gpio-keys/input0";
478         input->dev.parent = &pdev->dev;
479         input->open = gpio_keys_open;
480         input->close = gpio_keys_close;
481
482         input->id.bustype = BUS_HOST;
483         input->id.vendor = 0x0001;
484         input->id.product = 0x0001;
485         input->id.version = 0x0100;
486
487         /* Enable auto repeat feature of Linux input subsystem */ 
488         if (pdata->rep)
489                 __set_bit(EV_REP, input->evbit); //爲輸入設備input賦予自動重複特性,因爲按鍵可以反覆地按
490
491         for (i = 0; i < pdata->nbuttons; i++)
 {        //爲每個按鍵進行初始化,及設置屬性
492                 struct gpio_keys_button *button = &pdata->buttons[i];
493                 struct gpio_button_data *bdata = &ddata->data[i];
494                 unsigned int type = button->type ?: EV_KEY;
495
496                 bdata->input = input;
497                 bdata->button = button;
498
499                 error = gpio_keys_setup_key(pdev, bdata, button);// gpio_keys_setup_key爲關鍵函數,它的作用是初始化按鍵,即按鍵去抖,以及
500                 if (error)                                      //按鍵中斷函數的定義都是在這個函數中
501                         goto fail2;
502
503                 if (button->wakeup)
504                         wakeup = 1;
505
506                 input_set_capability(input, type, button->code);
507         }
508 /* 在指定目錄下生成sys屬性文件,即在執行完下面語句後,會在/sys/devices/platform/gpio-keys/目錄下生成四個文件:
disabled_keys、disabled_switches、switches、keys,前兩個是可讀可寫的,後兩個爲只讀    */
509         error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
510         if (error) {
511                 dev_err(dev, "Unable to export keys/switches, error: %d\n",
512                         error);
513                 goto fail2;
514         }
515
516         error = input_register_device(input);//註冊輸入設備input,把輸入設備加到輸入設備鏈表中,並尋找合適的handler與input_handler配對
517         if (error) {
518                 dev_err(dev, "Unable to register input device, error: %d\n",
519                         error);
520                 goto fail3;
521         }
522
523         /* get current state of buttons */  //把當前的按鍵狀態上報給系統,這個是在系統初始化,即上電的時候報告給系統的
524         for (i = 0; i < pdata->nbuttons; i++)
525                 gpio_keys_report_event(&ddata->data[i]);
526         input_sync(input);        //用於事件的同步,即告知系統設備已傳遞完一組完整的數據
527
528         device_init_wakeup(&pdev->dev, wakeup);
529
530         return 0;
531
532  fail3:
533         sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
534  fail2:
535         while (--i >= 0) {
536                 free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
537                 if (ddata->data[i].timer_debounce)
538                         del_timer_sync(&ddata->data[i].timer);
539                 cancel_work_sync(&ddata->data[i].work);
540                 gpio_free(pdata->buttons[i].gpio);
541         }
542
543         platform_set_drvdata(pdev, NULL);
544  fail1:
545         input_free_device(input);
546         kfree(ddata);
547
548         return error;
549 }
/*小節

           這個probe函數它是通過主要函數gpio_keys_setup_key,實現了按鍵去抖,按鍵中斷功能,並沒有想當初我們自己編寫的按鍵驅動代碼那樣,直接將按鍵去抖函數,和按鍵中斷函數定義在按鍵驅動代碼別處而是封裝在了gpio_keys_setup_key函數中,簡化了函數代碼!另外probe函數中調用了 input_set_capability函數,它標識輸入設備對哪些事件感興趣,因爲是按鍵,所以感興趣的事件一定是EV_KEY,這個函數就是子系統處理機制中,我們事先要進行設置input設備支持的事件類型,它是定義在input.c文件中的。

        這個中斷函數要注意,該中斷函數即爲中斷機制的前半部,它主要思想是在按鍵去抖間隔時間內不執行中斷後半部分內容,只有在經過去抖間隔時間達到以後,才執行工作隊列中的內容&bdata->work,即執行gpio_keys_gpio_work_func函數。中斷後半部函數gpio_keys_gpio_work_func調用的是gpio_keys_gpio_report_event函數,而gpio_keys_gpio_report_event函數調用的是input_event函數,該函數的功能是把輸入事件上報給系統input_event函數經過一次次傳遞封裝,最終到達用戶空間而被用戶所用。    */

2.input 核心層

之前說過,這個核心層我覺得是input子系統最重要,也是最神祕的一層,接下來讓我們來揭開其神祕的面紗吧!

[zoulei@CentOS linux-3.0]$ vim drivers/input/input.c

2133 static const struct file_operations input_fops = {
2134         .owner = THIS_MODULE,
2135         .open = input_open_file,
2136         .llseek = noop_llseek,
2137 };
     static int __init input_init(void)
2140 {
2141         int err;
2142
2143         err = class_register(&input_class);//註冊input類,這裏就是生成了sys/class/input目錄,在該目錄下會看到按鍵驅動移植鏈接文件event0
2144         if (err) {
2145                 pr_err("unable to register input_dev class\n");
2146                 return err;
2147         }
2148
2149         err = input_proc_init();//在proc目錄下建立input子系統目錄及交互文件,即/proc/bus/input目錄下的devices文件和handlers文件
2150         if (err)
2151                 goto fail1;
2152
2153         err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
2154         if (err) {     //註冊一個主設備號爲INPUT_MAJOR(13),次設備號爲0~255,文件操作符爲input_fops的字符設備
2155                 pr_err("unable to register char major %d", INPUT_MAJOR);
2156                 goto fail2;
2157         }
2158
2159         return 0;
2160
2161  fail2: input_proc_exit();
2162  fail1: class_unregister(&input_class);
2163         return err;
2164 }
        這個初始化函數中,有一個註冊字符設備函數register_chrdev,這個函數有一個參數input_fops值得注意,所有主設備號13的字符設備的操作最終都會轉入到input_fops中。例如event0的主設備號爲13,對其的操作會落在input_fops中,這個結構體中,源代碼中就只有兩個函數操作成員,分別是open與llseek,這個open對應的是源代碼中的 input_open_file,通過對input_open_file函數的學習,我發現input_fops只是輸入子系統通用的file_operations,對於不同的輸入子系統(如鍵盤、鼠標、觸摸屏等),通過input_open_file函數會調用各自不同的file_operations。對於按鍵來說,它調用的是drivers/input目錄下Evdev.c文件中的evdev_fops。

 struct input_dev *input_allocate_device(void)
1648 {
1649         struct input_dev *dev;
1650
1651         dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
1652         if (dev) {
1653                 dev->dev.type = &input_dev_type;//初始化設備的類型
1654                 dev->dev.class = &input_class;
1655                 device_initialize(&dev->dev);
1656                 mutex_init(&dev->mutex);   //初始化互斥鎖
1657                 spin_lock_init(&dev->event_lock);  //初始化自旋鎖
1658                 INIT_LIST_HEAD(&dev->h_list);   //初始化鏈表
1659                 INIT_LIST_HEAD(&dev->node);
1660
1661                 __module_get(THIS_MODULE);
1662         }
1663
1664         return dev;
1665 }

 該函數返回一個指向 input_dev 類型的指針,該結構體是一個輸入設備結構體,包含了輸入設備的一些相關信息,如

設備支持的按鍵碼、設備的名稱、設備支持的事件等

 static void input_handle_event(struct input_dev *dev,
 217                                unsigned int type, unsigned int code, int value)
 218 {
 219         int disposition = INPUT_IGNORE_EVENT;
 221         switch (type) {
 223         case EV_SYN:
 224                 switch (code) {
 225                 case SYN_CONFIG:
 226                         disposition = INPUT_PASS_TO_ALL;
 227                         break;
 228
 229                 case SYN_REPORT:
 230                         if (!dev->sync) {
 231                                 dev->sync = true;
 232                                 disposition = INPUT_PASS_TO_HANDLERS;
 233                         }
 234                         break;
 235                 case SYN_MT_REPORT:
 236                         dev->sync = false;
 237                         disposition = INPUT_PASS_TO_HANDLERS;
 238                         break;
 239                 }
                      ......
       }

 347 void input_event(struct input_dev *dev,
 348                  unsigned int type, unsigned int code, int value)
 349 {
 350         unsigned long flags;
 352         if (is_event_supported(type, dev->evbit, EV_MAX)) {
 354                 spin_lock_irqsave(&dev->event_lock, flags);
 355                 add_input_randomness(type, code, value);
 356                 input_handle_event(dev, type, code, value);
 357                 spin_unlock_irqrestore(&dev->event_lock, flags);
 358         }
 359 }
上面我們已經分析過input_event函數的功能是上報事件給子系統,但是分析這個函數起着主要作用的是裏面的input_handle_event函數,而input_handle_event

中有一個event()函數與input_pass_event()函數有些事件是發送給設備,而不是發送給 handler 處理的。event()函數用來向輸入子系統報告一個將要發送給設備的事件,例如讓 LED 燈點亮事件、蜂鳴器鳴叫事件等。當事件報告給輸入子系統後,就要求設備處理這個事件。這裏就體現了函數的一次次傳遞與封裝!

*****************************************************************************************************************************************************************************************

/*小節

       其實這個核心層,事件處理層,還有設備驅動層三者之間的關係就好像一個單獨驅動中的device與driver的關係一樣,設備驅動層定義了設備註冊的接口函數input_register_handle,因爲設備驅動層input device與事件處理層hander 有一個匹配的過程,匹配成功就會調用事件處理層中的connect函數,connect函數會調用input_register_handle,當然就好比device與driver匹配成功之後會調用probe函數一樣,

所有的input device都掛在

input_dev_list

鏈上

所有的handler都掛在input_handler_list上,

所以事件層要與設備驅動層進行匹配就必須的提前註冊。

      事件層與設備驅動層之間的匹配調用的是input_attach_handler-> input_match_device函數。

    總的來說,這個核心層所扮演的角色就好比一家人,一對夫妻上要照顧自己的爸媽,下要照顧自己的子女,而照顧所需要的錢就是函數接口,所以說子系統核心層是鏈接其他兩個層之間的紐帶與橋樑,向下提供驅動層的接口(input_register_handle),向上提供事件處理層的接口(connect)。


3.事件處理層

事件處理層文件主要是用來支持輸入設備並與用戶空間交互,這部分代碼一般不需要我們自己去編寫,因爲Linux內核已經自帶有一些事件處理器,可以支持大部分輸入設備,比如Evdev.c、mousedev.c、joydev.c等。對按鍵來說,用到的是Evdev.c文件(它在drivers/input目錄下)。

[zoulei@CentOS linux-3.0]$ vim drivers/input/evdev.c

 998 static int __init evdev_init(void)
 999 {
1000         return input_register_handler(&evdev_handler);//註冊evdev_handler
1001 }
evdev_handler類型就是前面介紹過的三個重要結構體中的一個——input_handler

 input_handler 是輸入子系統的主要數據結構,一般將其稱爲 handler 處理器,表示對輸入事件的具體處理。

input_handler 爲輸入設備的功能實現了一個接口,輸入事件最終傳遞到handler 處理器,handler 處理器根據一定的規則,

然後對事件進行處理,具體的規則將在下面詳細介紹

它的定義爲:

struct input_handler {
void *private; 
void (*event)(struct input_handle *handle, unsigned int type,
unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev* dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;  
const char *name;
const struct input_device_id *id_table; //定義了一個 name, 表示 handler 的名字,顯示在/proc/bus/input/handlers 目錄                                                            //中。
const struct input_device_id *blacklist; //指向一個 input_device_id 表,這個表包含 handler 應該忽略的設備
struct list_head h_list;
struct list_head node;
 };

988 static struct input_handler evdev_handler = {
 989         .event          = evdev_event,//傳遞信息是調用,在input_pass_event
 990         .connect        = evdev_connect,//device與handler匹配時調用
 991         .disconnect     = evdev_disconnect,
 992         .fops           = &evdev_fops,
 993         .minor          = EVDEV_MINOR_BASE,
 994         .name           = "evdev",
 995         .id_table       = evdev_ids,
 996 };
其中evdev_connect主要用來連接input_dev和input_handler;evdev_event是把input事件發給所有的client。另外在該結構體內還定義了一個fops集合,它被賦值爲evdev_fops的指針。evdev_fops就是在介紹核心層input.c文件時,提到的當處理按鍵輸入子系統時,file_operations所對應的就是evdev_fops

  

input_handler註冊

1923 int input_register_handler(struct input_handler *handler)
1924 {
1925         struct input_dev *dev;
1926         int retval;
1927
1928         retval = mutex_lock_interruptible(&input_mutex);
1929         if (retval)
1930                 return retval;
1931
1932         INIT_LIST_HEAD(&handler->h_list);
1933//其中的 handler->minor 表示對應 input 設備結點的次設備號。 handler->minor以右移 5 位作爲索引值插入到 //input_table[ ]
1934         if (handler->fops != NULL) {
1935                 if (input_table[handler->minor >> 5]) {
1936                         retval = -EBUSY;
1937                         goto out;
1938                 }
1939                 input_table[handler->minor >> 5] = handler;
1940         }
1941
1942         list_add_tail(&handler->node, &input_handler_list);
1943
1944         list_for_each_entry(dev, &input_dev_list, node)
1945                 input_attach_handler(dev, handler);
//input_attach_handler()函數的作用是匹配 input_dev_list 鏈表中的 input_dev 與 handler。如果成功會將 input_dev 

//與 handler 聯繫起來。也就是說在註冊handler和dev時都會去調用該函數。

1947 input_wakeup_procfs_readers(); 1948 1949 out: 1950 mutex_unlock(&input_mutex); 1951 return retval; 1952 }

evdev_open函數分析

 275 static int evdev_open(struct inode *inode, struct file *file)
 276 {
 277         struct evdev *evdev;
 278         struct evdev_client *client;
 279         int i = iminor(inode) - EVDEV_MINOR_BASE;//得到設備列表中的序號
 280         unsigned int bufsize;
 281         int error;
 283         if (i >= EVDEV_MINORS)
 284                 return -ENODEV;
 286         error = mutex_lock_interruptible(&evdev_table_mutex);
 287         if (error)
 288                 return error;
 289         evdev = evdev_table[i];//得到設備列表中的序號
 290         if (evdev)
 291                 get_device(&evdev->dev);  //增加計數
 292         mutex_unlock(&evdev_table_mutex);
 294         if (!evdev)
 295                 return -ENODEV;
 297         bufsize = evdev_compute_buffer_size(evdev->handle.dev);
 299         client = kzalloc(sizeof(struct evdev_client) +              //分配並初始化client結構體
 300                                 bufsize * sizeof(struct input_event),
 301                          GFP_KERNEL);
 302         if (!client) {
 303                 error = -ENOMEM;
 304                 goto err_put_evdev;
 305         }
 307         client->bufsize = bufsize;
 308         spin_lock_init(&client->buffer_lock);
 309         client->evdev = evdev;
 310         evdev_attach_client(evdev, client);
 312         error = evdev_open_device(evdev); //evdev_open_device函數的作用是打開設備,當設備是第一次被打開時,則調用input_open_device函數
 313         if (error)
 314                 goto err_free_client;
 316         file->private_data = client;
 317         nonseekable_open(inode, file);
 318

evdev_open函數負責打開一個輸入設備,但是真正起到打開設備的是evdev_open_device

evdev_read分析:

383 static ssize_t evdev_read(struct file *file, char __user *buffer,//evdev_read函數負責讀取設備
 384                           size_t count, loff_t *ppos)
 385 {
 386         struct evdev_client *client = file->private_data;
 387         struct evdev *evdev = client->evdev;
 388         struct input_event event;
 389         int retval;
 390
 391         if (count < input_event_size())  //對讀取的數據大小進行判斷
 392                 return -EINVAL;
 393
 394         if (client->packet_head == client->tail && evdev->exist &&   //如果緩存中沒有數據可讀而設備又存在,並且被設置爲O_NONBLOCK方式,
 395             (file->f_flags & O_NONBLOCK))                            //則退出
 396                 return -EAGAIN;
 397
 398         retval = wait_event_interruptible(evdev->wait,    //等待緩存中是否有數據可讀,或設備是否被移走
 399                 client->packet_head != client->tail || !evdev->exist);
 400         if (retval)
 401                 return retval;
 402
 403         if (!evdev->exist)//如果設備不存在,則退出
 404                 return -ENODEV;
 405
 406         while (retval + input_event_size() <= count &&
 407                evdev_fetch_next_event(client, &event)) {
 408
 409                 if (input_event_to_user(buffer + retval, &event))
 410                         return -EFAULT;
 411
 412                 retval += input_event_size();
 413         }
 414
 415         return retval;
 416 }

evdev_connect分析:

908 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 909                          const struct input_device_id *id)
 910 {
 911         struct evdev *evdev;
 912         int minor;
 913         int error;
 914
 915         for (minor = 0; minor < EVDEV_MINORS; minor++)
 916                 if (!evdev_table[minor])
 917                         break;
 918
 919         if (minor == EVDEV_MINORS) {
 920                 pr_err("no more free evdev devices\n");
 921                 return -ENFILE;
 922         }
 923
 924         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
 925         if (!evdev)
 926                 return -ENOMEM;
 927
 928         INIT_LIST_HEAD(&evdev->client_list);
 929         spin_lock_init(&evdev->client_lock);
 930         mutex_init(&evdev->mutex);
 931         init_waitqueue_head(&evdev->wait);
 932
 933         dev_set_name(&evdev->dev, "event%d", minor);
 934         evdev->exist = true;
 935         evdev->minor = minor;
 937         evdev->handle.dev = input_get_device(dev);
 938         evdev->handle.name = dev_name(&evdev->dev);
 939         evdev->handle.handler = handler;
 940         evdev->handle.private = evdev;
 942         evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
 943         evdev->dev.class = &input_class;
 944         evdev->dev.parent = &dev->dev;
 945         evdev->dev.release = evdev_free;
 946         device_initialize(&evdev->dev);

//在這段代碼裏主要完成 evdev封裝的 device的初始化 .注意在這裏 ,使它所屬的類指向 input_class.這樣在 /sysfs中創

//建的設備目錄就會在 /sys/class/input/下面顯示

948 error = input_register_handle(&evdev->handle); ...... }

 將handle掛到所對應input deviceh_list鏈表上.還將handle掛到對應的handlerhlist鏈表上.如果handler

義了start函數,將調用之到這裏,我們已經看到了input device, handlerhandle是怎麼關聯起來的了

到這裏input子系統按鍵驅動就分析完了。。。












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章