前言:學習驅動有一段時間了,之前所學習的驅動都是驅動最原始的寫法,也移植過很多驅動,然而並沒有對內核自帶的驅動進行深入的瞭解,於是出於好奇,就認真的學習了一下內核自帶的驅動,我發現之前所學習的驅動都是將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_dev,input_handle和input_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
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 device的h_list鏈表上.還將handle掛到對應的handler的hlist鏈表上.如果handler定
義了start函數,將調用之. 到這裏,我們已經看到了input device, handler和handle是怎麼關聯起來的了
到這裏input子系統按鍵驅動就分析完了。。。