1. 程式人生 > >input子系統整體流程全面分析(觸控式螢幕驅動為例)

input子系統整體流程全面分析(觸控式螢幕驅動為例)

input輸入子系統整體流程

  input子系統在核心中的實現,包括輸入子系統(Input Core),事件處理層(Event Handler)和裝置驅動層。

  在開頭部分會從裝置驅動層做為線索,分析輸入子系統和事件處理層是如何配合的最後從使用者角度出發,從“/dev/input/*”介面如何使用輸入子系統提供的服務。

  既然需要詳細分析,有一個這樣的流程圖能夠幫助我們在被繞進程式碼的過程中,找到出口,你能夠知道你現在位於程式碼框架的什麼位置,不會忘記正在分析的程式碼的“身份”。

裝置驅動層註冊到input子系統

  以S3C2440觸控式螢幕的驅動程式碼代例,初始化函式定義了struct input_dev input

結構體,它用於描述一個輸入子系統裝置。

  任何驅動裝置如果想標明自己是輸入裝置,都應該通過初始化這樣的結構體,並且呼叫input_allocate_device()函式進行註冊。

  瞭解這一過程,先看一下struct input_dev結構體的內容:

 1 struct input_dev {  
 2   
 3     void *private;              //輸入裝置私有指標,一般指向用於描述裝置驅動層的裝置結構  
 4   
 5     const char *name;           //提供給使用者的輸入裝置的名稱  
 6     const char *phys;           //
提供給程式設計者的裝置節點的名稱 7 const char *uniq; //指定唯一的ID號,就像MAC地址一樣 8 struct input_id id; //輸入裝置標識ID,用於和事件處理層進行匹配 9 10 unsigned long evbit[NBITS(EV_MAX)]; //點陣圖,記錄裝置支援的事件型別 11 unsigned long keybit[NBITS(KEY_MAX)]; //點陣圖,記錄裝置支援的按鍵型別 12 unsigned long relbit[NBITS(REL_MAX)]; //
點陣圖,記錄裝置支援的相對座標 13 unsigned long absbit[NBITS(ABS_MAX)]; //點陣圖,記錄裝置支援的絕對座標 14 unsigned long mscbit[NBITS(MSC_MAX)]; //點陣圖,記錄裝置支援的其他功能 15 unsigned long ledbit[NBITS(LED_MAX)]; //點陣圖,記錄裝置支援的指示燈 16 unsigned long sndbit[NBITS(SND_MAX)]; //點陣圖,記錄裝置支援的聲音或警報 17 unsigned long ffbit[NBITS(FF_MAX)]; //點陣圖,記錄裝置支援的作用力功能 18 unsigned long swbit[NBITS(SW_MAX)]; //點陣圖,記錄裝置支援的開關功能 19 20 unsigned int keycodemax; //裝置支援的最大按鍵值個數 21 unsigned int keycodesize; //每個按鍵的位元組大小 22 void *keycode; //指向按鍵池,即指向按鍵值陣列首地址 23 int (*setkeycode)(struct input_dev *dev, int scancode, int keycode); //修改按鍵值 24 int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode); //獲取按鍵值 25 26 struct ff_device *ff; //用於強制更新輸入裝置的部分內容 27 28 unsigned int repeat_key; //重複按鍵的鍵值 29 struct timer_list timer; //設定當有連擊時的延時定時器 30 31 int state; //裝置狀態 32 33 int sync; //同步事件完成標識,為1說明事件同步完成 34 35 int abs[ABS_MAX + 1]; //記錄座標的值 36 int rep[REP_MAX + 1]; //記錄重複按鍵的引數值 37 38 unsigned long key[NBITS(KEY_MAX)]; //點陣圖,按鍵的狀態 39 unsigned long led[NBITS(LED_MAX)]; //點陣圖,led的狀態 40 unsigned long snd[NBITS(SND_MAX)]; //點陣圖,聲音的狀態 41 unsigned long sw[NBITS(SW_MAX)]; //點陣圖,開關的狀態 42 43 int absmax[ABS_MAX + 1]; //點陣圖,記錄座標的最大值 44 int absmin[ABS_MAX + 1]; //點陣圖,記錄座標的最小值 45 int absfuzz[ABS_MAX + 1]; //點陣圖,記錄座標的解析度 46 int absflat[ABS_MAX + 1]; //點陣圖,記錄座標的基準值 47 48 int (*open)(struct input_dev *dev); //輸入裝置開啟函式 49 void (*close)(struct input_dev *dev); //輸入裝置關閉函式 50 int (*flush)(struct input_dev *dev, struct file *file); //輸入裝置斷開後重新整理函式 51 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); //事件處理 52 53 struct input_handle *grab; //類似私有指標,可以直接訪問到事件處理介面event 54 55 struct mutex mutex; //用於open、close函式的連續訪問互斥 56 unsigned int users; //裝置使用計數 57 58 struct class_device cdev; //輸入裝置的類資訊 59 union { //裝置結構體 60 struct device *parent; 61 } dev; 62 63 struct list_head h_list; //handle連結串列 64 struct list_head node; //input_dev連結串列 65 };
View Code

  就這樣赤裸裸的看上面的結構體,會覺得摸不著頭腦,但是有一點是確定的,我們在寫輸入裝置驅動時會定義這樣一個輸入裝置結構體,並呼叫input_allocate_device()函式,這個函式的功能是為新新增的輸入裝置分配記憶體,如果成功,將返回input_dev *的指標結構,因此在寫驅動的時候應該接受返回值,作為驅動層獲得了一個新的輸入裝置操作的介面。

  那麼input_allocate_device()函式做了什麼呢?開啟函式看一下(input.c中實現):

 1 struct input_dev *input_allocate_device(void)  
 2 {  
 3     struct input_dev *dev;  
 4   
 5 //動態申請記憶體,使用GFP_KERNEL方式,注意GFP_KERNEL可能導致睡眠,不能在中斷中呼叫這個函式  
 6     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);  
 7 //分配成功執行的程式碼,進行成員的預設填充  
 8     if (dev) {  
 9         dev->cdev.class = &input_class;          //支援熱插拔的結構體  
10         dev->cdev.groups = input_dev_attr_groups; //描述裝置的硬體資訊和支援的事件型別  
11         class_device_initialize(&dev->cdev);     //類裝置初始化,新增進input類裝置模型中  
12         mutex_init(&dev->mutex);             //初始化互斥鎖  
13         INIT_LIST_HEAD(&dev->h_list);        //初始化handle連結串列  
14         INIT_LIST_HEAD(&dev->node);          //初始化輸入裝置連結串列  
15   
16     }  
17 }  
View Code

  通過input_allocate_device()函式,我們裝置驅動現在持有的input_dev裡面就被賦予了input的“形象”,但是還需要我們去充實一下“內在”,因此,裝置驅動程式,還需要為自己的裝置增加自己的特性,才能創造獨有的裝置“形象”。

 1 struct input_dev *input_dev = input_allocate_device();  
 2 input_dev->name = "s3c2410 Touchscreen";  
 3 input_dev->phys = "s3c2410ts/input0";  
 4 input_dev->id.bustype = BUS_HOST;  
 5 input_dev->id.vendor = 0x0001;  
 6 input_dev->id.product = 0x0002;  
 7 input_dev->id.version = 0x0100;  
 8 input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
 9 input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);   
10 input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);  
11 input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);  
12 input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);  
View Code

  這部分完成了輸入裝置的初始化工作。但是這僅是初始化自己的“特點”,還需要通知輸入子系統有這樣一個新裝置誕生了,這就需要呼叫輸入子系統的註冊函式input_register_device(input_dev)來完成。

  input_register_device()用於註冊一個輸入裝置。那麼註冊過程是怎樣的呢?這是一個重點,在下面的程式碼中進行註釋分析:

 1 int input_register_device(struct input_dev *dev)  
 2 {  
 3     /* 用於記錄輸入裝置名稱的索引值 */  
 4     static atomic_t input_no = ATOMIC_INIT(0);  
 5     /* 輸入事件的處理介面指標,用於和裝置的事件型別進行匹配 */  
 6     struct input_handler *handler;  
 7     const char *path;  
 8     int error;  
 9   
10     /* 預設所有的輸入裝置都支援EV_SYN同步事件 */  
11     set_bit(EV_SYN, dev->evbit);  
12   
13     /* 
14      * 如果裝置驅動沒有指定重複按鍵(連擊),系統預設提供以下的支援 
15      * 其中init_timer為連擊產生的定時器,時間到呼叫input_repeat_key函式 
16      * 上報,REP_DELAY用於設定重複按鍵的鍵值,REP_PERIOD設定延時時間 
17      */  
18     init_timer(&dev->timer);  
19     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {  
20         dev->timer.data = (long) dev;  
21         dev->timer.function = input_repeat_key;  
22         dev->rep[REP_DELAY] = 250;  
23         dev->rep[REP_PERIOD] = 33;  
24     }  
25   
26     /* 如果裝置驅動沒有設定自己的獲取鍵值的函式,系統預設 */  
27     if (!dev->getkeycode)  
28         dev->getkeycode = input_default_getkeycode;  
29   
30     /* 如果裝置驅動沒有指定按鍵重置函式,系統預設 */  
31     if (!dev->setkeycode)  
32         dev->setkeycode = input_default_setkeycode;  
33   
34     /* 重要,把裝置掛到全域性的input子系統裝置連結串列input_dev_list上 */  
35     list_add_tail(&dev->node, &input_dev_list);  
36   
37     /* 動態獲取input裝置的ID號,名稱為input*,其中後面的“*”動態獲得,唯一的 */  
38     snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),  
39          "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);  
40   
41     /* 如果這個值沒有設定,系統把輸入裝置掛入裝置連結串列 */  
42     if (!dev->cdev.dev)  
43         dev->cdev.dev = dev->dev.parent;  
44   
45     /* 在/sys目錄下建立裝置目錄和檔案 */  
46     error = class_device_add(&dev->cdev);  
47     if (error)  
48         return error;  
49   
50     /* 獲取並列印裝置的絕對路徑名稱 */  
51     path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);  
52     printk(KERN_INFO "input: %s as %s\n",  
53         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");  
54     kfree(path);  
55   
56     /* 核心重點,input裝置在增加到input_dev_list連結串列上之後,會查詢 
57      * input_handler_list事件處理連結串列上的handler進行匹配,這裡的匹配 
58      * 方式與裝置模型的device和driver匹配過程很相似,所有的input 
59      * 都掛在input_dev_list上,所有型別的事件都掛在input_handler_list 
60      * 上,進行“匹配相親”*/  
61     list_for_each_entry(handler, &input_handler_list, node)  
62         input_attach_handler(dev, handler);  
63   
64     input_wakeup_procfs_readers();  
65   
66     return 0;  
67 }  
View Code

  上面的程式碼主要的功能有以下幾個功能,也是裝置驅動註冊為輸入裝置委託核心做的事情:

  • 進一步初始化輸入裝置,例如連擊事件;
  • 註冊輸入裝置到input類中;
  • 把輸入裝置掛到輸入裝置連結串列input_dev_list中;
  • 查詢並匹配輸入裝置對應的事件處理層,通過input_handler_list連結串列

   我們需要再分析下這個匹配的過程,但是需要注意的是下面分析的程式碼是我們暫時無法分析的,因為那樣會使得情況變得更加複雜,當我們從應用層往下分析的時候一切都會明白。input_attach_handler匹配過程如下:

 1 const struct input_device_id *id;  
 2     int error;  
 3   
 4 /* 如果handler的blacklist被賦值了並且則優先匹配 */  
 5     if (handler->blacklist && input_match_device(handler->blacklist, dev))  
 6         return -ENODEV;  
 7   
 8     /* 否則利用handler->id_table和dev進行匹配,後面講述匹配什麼和過程 */  
 9     id = input_match_device(handler->id_table, dev);  
10     if (!id)  
11         return -ENODEV;  
12   
13     /* 這是一根“紅線”,雖然你可能覺的是黑色的,但不可否認,他們真的匹配上了 
14          * 呼叫handler->connnect函式進行匹配,匹配詳細過程後面講述 
15         */  
16     error = handler->connect(handler, dev, id);  
17     if (error && error != -ENODEV)  
18         printk(KERN_ERR  
19             "input: failed to attach handler %s to device %s, "  
20             "error: %d\n",  
21             handler->name, kobject_name(&dev->cdev.kobj), error);  
22   
23     return error;  
View Code

  先來看下input_match_device()函式,看一下這個匹配的條件是什麼,如何匹配的過程是怎樣的,匹配的結果會是什麼?

 1 /* 事件處理層中的對應flags如果設定或者driver_info被設定則進行匹配 */  
 2     for (; id->flags || id->driver_info; id++) {  
 3         /* 以下通過flags中設定的位來匹配裝置的匯流排型別、經銷商、生產ID和版本ID 
 4           如果沒有匹配上將進行MATCH_BIT匹配 */  
 5         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
 6             if (id->bustype != dev->id.bustype)  
 7                 continue;  
 8   
 9         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  
10             if (id->vendor != dev->id.vendor)  
11                 continue;  
12   
13         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  
14             if (id->product != dev->id.product)  
15                 continue;  
16   
17         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  
18             if (id->version != dev->id.version)  
19                 continue;  
20           
21         /* MATCH_BIT用於匹配裝置驅動中是否設定了這些為,MATCH_BIT的巨集 
22         * 被定義在input.c中,我們在裝置驅動中設定的事件型別會與事件連結串列中的 
23         * 所有事件型別進行比較,匹配成功了將返回id,證明真的很合適,否則NULL 
24         */  
25         MATCH_BIT(evbit,  EV_MAX);  
26         MATCH_BIT(keybit, KEY_MAX);  
27         MATCH_BIT(relbit, REL_MAX);  
28         MATCH_BIT(absbit, ABS_MAX);  
29         MATCH_BIT(mscbit, MSC_MAX);  
30         MATCH_BIT(ledbit, LED_MAX);  
31         MATCH_BIT(sndbit, SND_MAX);  
32         MATCH_BIT(ffbit,  FF_MAX);  
33         MATCH_BIT(swbit,  SW_MAX);  
34   
35         return id;  
36     }  
37   
38     return NULL;  
View Code

  既然證明是合適的,接下來就應該登記註冊,並公證了。還記得handler->connect(handler, dev, id)函式吧。

  當input_match_device()找到最合適的事件處理層驅動時,便執行handler->connect函式進行公證了,看下面這部分程式碼(假如說找到了evdev型別的驅動,在input/evdev.c中):

 1 struct evdev *evdev;  
 2     struct class_device *cdev;  
 3     dev_t devt;  
 4     int minor;  
 5     int error;  
 6   
 7     /* EVDEV_MINORS為32,代表共能容納32個evdev事件層裝置,下面程式碼在找到空的地方,用於儲存evdev事件層的資料,即上面定義的evdev */  
 8     for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);  
 9     /* 這說明核心已經沒辦法再分配這種型別的裝置了 */  
10     if (minor == EVDEV_MINORS) {  
11         printk(KERN_ERR "evdev: no more free evdev devices\n");  
12         return -ENFILE;  
13     }  
14     /* 開始給evdev事件層驅動分配空間了 */  
15     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);  
16     if (!evdev)  
17         return -ENOMEM;  
18   
19     /* 初始化client_list列表和evdev_wait佇列,後面介紹 */  
20     INIT_LIST_HEAD(&evdev->client_list);  
21     init_waitqueue_head(&evdev->wait);  
22   
23     /* 初始化evdev結構體,其中handle為輸入裝置和事件處理的關聯介面 */  
24     evdev->exist = 1;  
25     evdev->minor = minor;  
26     evdev->handle.dev = dev;  
27     evdev->handle.name = evdev->name;  
28     evdev->handle.handler = handler;  
29     evdev->handle.private = evdev;  
30     sprintf(evdev->name, "event%d", minor);  
31   
32     /* 重要,上層訪問時通過次裝置號找到事件處理的介面 */  
33     evdev_table[minor] = evdev;  
34   
35     /* evdev事件裝置的此裝置號的基準值INPUT_MAJOR, EVDEV_MINOR_BASE */  
36     devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),  
37   
38     /* 建立使用者事件驅動層裝置訪問介面/dev/input/event* */  
39     cdev = class_device_create(&input_class, &dev->cdev, devt,  
40                    dev->cdev.dev, evdev->name);  
41     if (IS_ERR(cdev)) {  
42         error = PTR_ERR(cdev);  
43         goto err_free_evdev;  
44     }  
45   
46     /* 提供/sys目錄的使用者空間介面 */  
47     error = sysfs_create_link(&input_class.subsys.kobj,  
48                   &cdev->kobj, evdev->name);  
49     if (error)  
50         goto err_cdev_destroy;  
51   
52     /* input_dev裝置驅動和handler事件處理層的關聯,由handle完成 */  
53     error = input_register_handle(&evdev->handle);  
View Code

  通過上述程式碼的執行,最終,輸入裝置在input_register_handle()的關聯下與已經匹配上的handler結合

 1 struct input_handler *handler = handle->handler;  
 2     /* 將d_node連結到輸入裝置的h_list,h_node連結到事件層的h_list連結串列上 
 3     * 因此,在handle中是輸入裝置和事件層的關聯結構體,通過輸入裝置可以 
 4     * 找到對應的事件處理層介面,通過事件處理層也可找到匹配的輸入裝置 
 5     */  
 6     list_add_tail(&handle->d_node, &handle->dev->h_list);  
 7     list_add_tail(&handle->h_node, &handler->h_list);  
 8   
 9     /* 如果start函式有定義則呼叫,但是evdev結構體中並未初始化這個函式 */  
10     if (handler->start)  
11         handler->start(handle);  
View Code

  以上是輸入裝置驅動註冊的全過程,牽涉的程式碼比較多,需要從巨集觀上理順。

  縱觀整個過程:

  輸入裝置驅動最終的目的就是能夠與事件處理層的事件驅動相互匹配,但是在drivers/input目錄下有evdev.c事件驅動、mousedev.c事件驅動、joydev.c事件驅動等等,我們的輸入裝置產生的事件應該最終上報給誰,然後讓事件驅動再去處理呢?

  知道了這麼個原因再看上面程式碼就會明白,其實evdev.c、mousedev.c等根據硬體輸入裝置的處理方式的不同抽象出了不同的事件處理介面幫助上層去呼叫,而我們寫的裝置驅動程式只不過是完成了硬體暫存器中資料的讀寫,但提交給使用者的事件必須是經過事件處理層的封裝和同步才能夠完成的,事件處理層提供給使用者一個統一的介面來操作。

  由於以上的這些原因,才有了上述程式碼的關聯過程,看一下整個關聯註冊的過程:

通過上圖我們可以看到input輸入裝置匹配關聯的關鍵過程以及涉及到的關鍵函式和資料

  以上主要是從input裝置驅動程式的角度去看輸入子系統的註冊過程和三層之間的關聯。

  下面將從應用層的角度分析事件的接受過程和處理過程以及三層之間是如何配合處理輸入事件的。

從應用層的角度出發看input子系統

  以上部分已經藉助input子系統把input裝置驅動層與事件驅動層進行了關聯,以s3c2440_ts.c(輸入裝置層驅動)和evdev.c(事件處理層驅動)為例,來分析這一過程。

由於s3c2440_ts.c中上報的事件型別為按鍵、絕對值座標,而evdev事件驅動程式是全匹配的,因此早在s3c2440_ts.c註冊的過程中,就會建立裝置節點/dev/input/event0(假設核心中沒有其他的event型別的輸入裝置,這裡就是event0)

  我們知道,應用層使用裝置的第一步,是open(“/dev/event0”),因此這裡event0的主裝置號成為關鍵,因為主裝置號將表明你是什麼裝置,我們ls -l檢視/dev/event0發現:

crw-r-----1 root root 13, 64 2012-07-26 14:32 /dev/input/event0

  由此可見主裝置是13,輸入命令cat /proc/devices檢視主裝置為13的是input裝置,因此可以確定當我們執行open函式開啟event0裝置的時候,會呼叫input裝置的open驅動函式,這個函式在input.c中,為了說明這一問題,需要從input驅動註冊過程開始,還是input.c檔案:

1 /* 輸入裝置初始化函式 */  
2 static int __init input_init(void)  
3 {  
4        class_register(&input_class);  
5        input_proc_init();  
6        register_chrdev(INPUT_MAJOR,"input", &input_fops);  
7 }  
View Code

  可以看到,輸入裝置初始化的過程首先建立了input類,初始化input在proc下的節點,然後註冊input裝置,裝置名稱為input,操作介面是input_fops,主裝置號是INPUT_MAJOR=13。

  由以上可知,只要是主裝置號為13的裝置驅動程式,都是用input_fops介面,即當event0裝置使用open函式開啟時,會呼叫到input_fops介面中的open驅動函式,這個結構體的初始化為:

1 static const struct file_operations input_fops = {  
2        .owner = THIS_MODULE,  
3        .open = input_open_file,  
4 };  
View Code

  可以看到,只實現了一個open功能欄位,再看input_open_file的實現:

 1 static int input_open_file(struct inode *inode, struct file *file)  
 2 {  
 3        struct input_handler *handler =input_table[iminor(inode) >> 5];  
 4        const struct file_operations *old_fops,*new_fops = NULL;  
 5        if (!handler || !(new_fops =fops_get(handler->fops)))  
 6               return -ENODEV;  
 7        old_fops = file->f_op;  
 8        file->f_op = new_fops;  
 9        new_fops->open(inode, file);  
10 }  
View Code

  以上程式碼的功能為找到對應事件驅動層的fops,即進行fops的介面轉換,指向對應裝置的事件處理介面。

  其中input_table[iminor(inode)]>>5的input_table是一個全域性的input_handler型別的陣列,iminor(inode)取得次裝置號,並且右移5位索引input_table表中對應的位置,為什麼這樣做呢?這是因為這個表格中填寫的就是事件處理的指標,待會分析。

  繼續檢視下面的程式碼。if中將判斷是否為空並且事件處理層中的fops有沒有初始化,如果沒有就不能進行介面轉換,報出裝置不存在的錯誤,如果裝置存在則把input裝置的f_op驅動介面指向input_table表中存在的介面,並呼叫其open函式。

  那麼這個input_table裡面到底存放了什麼呢?我們還是拿觸控式螢幕驅動來講解。由於觸控式螢幕驅動已經完成了和evdev.c事件處理層的匹配,且次裝置號為64,裝置名稱為/dev/event0,這是我們通過分析驅動註冊中獲得的內容,既然input核心設備註冊了,s3c2440觸控式螢幕驅動也註冊了,那會不會evdev裝置也會註冊了呢?答案是肯定的,要想知道input_table裡面放了什麼,必須要去檢視evdev裝置的註冊過程,開啟input/evdev.c檢視它的註冊過程:

 1 static struct input_handler evdev_handler = {  
 2        .event =   evdev_event,                               //事件處理  
 3        .connect =      evdev_connect,                    //裝置連線  
 4        .disconnect =  evdev_disconnect,                //登出連線  
 5        .fops =           &evdev_fops,                      //驅動功能介面  
 6        .minor =  EVDEV_MINOR_BASE,              //evdev的值為64  
 7        .name =          "evdev",                              //裝置名稱  
 8        .id_table =      evdev_ids,                           //用於匹配裝置驅動的陣列  
 9 };  
10    
11 static int __init evdev_init(void)  
12 {  
13        return input_register_handler(&evdev_handler);          //evdev裝置驅動註冊  
14 }  
View Code

  由以上的內容可以知道evdev_handler也被作為一個裝置來操作,但是它屬於input handler事件處理裝置,然而我們在evdev_handler結構體的.fops欄位又發現它的驅動介面為字元裝置型別,在input中,如果input_table匹配到了evdev_handler,將會把file->f_op=&evdev_fops,那麼如果使用read、write等函式操作,將會呼叫到evdev_fops中的read、write。

  為了進一步檢視input_table表中的內容是如何填充的,還需要檢視這個註冊的過程:

1 int input_register_handler(struct input_handler *handler)  
2 {  
3 
            
           

相關推薦

input子系統整體流程全面分析觸控式螢幕驅動

input輸入子系統整體流程   input子系統在核心中的實現,包括輸入子系統(Input Core),事件處理層(Event Handler)和裝置驅動層。   在開頭部分會從裝置驅動層做為線索,分析輸入子系統和事件處理層是如何配合的,最後從使用者角度出發,從“/de

linux input輸入子系統分析《四》:input子系統整體流程全面分析

總線 返回值 分代 並不是 事件 等等 lag pri 位置 1 input輸入子系統整體流程 本節分析input子系統在內核中的實現,包括輸入子系統(Input Core),事件處理層(Event Handler)和設備驅動層。由於上節代碼講解了設備驅動層的寫法

[GIS演算法] DEM相關處理 - 視窗分析以平均值| 裁剪 - C語言實現

#include<stdio.h> #include<stdlib.h> typedef struct{ double x0,y0; //左下角點的座標值 int dx,dy; //柵格單元大小 int ycount,xcount; //行列號 doub

Arduino核心檔案分析以Stm32duino

       這篇部落格主要是分析stm32duino的底層檔案結構,來分析stm32duino 的實現原理和它的基本框架。 使用的工具是Source Insight ,新建工程,新增原始碼路徑之後可以進行分析。 開啟工程原始碼的資料夾後,有四個資料夾,我們主要分

視窗分析以平均值| 裁剪

#include<stdio.h> #include<stdlib.h> typedef struct{ double x0,y0; //左下角點的座標值 int dx,dy; //柵格單元大小 int ycount,xcount

linux驅動由淺入深系列:塊裝置驅動之三塊裝置驅動結構分析,以mmc

linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)前一篇文章介紹了塊裝置驅動在linux框架張的位置關係,本文來分析一下驅動本身。塊裝置驅動的模型還是基本基於字元裝置驅動的,可以簡單理解為塊裝置僅僅增加了操作緩衝區,對使用者操作請求進行佇列重排。因此只在有了

【Python專案】基於文字情感分析的電商評論重排序以京東附程式碼

一、背景 隨著網際網路的普及,網路購物已經成了人們購物的首選。使用者只需在電商平臺搜尋商品名,便可得到成百上千條商品資訊。商品資訊的排序演算法很複雜,但總的說來基本上都是根據與搜尋關鍵詞的關聯度和商品的人氣或商家排名來排序最終對使用者進行展示的。而好評率即是排

【Kaggle】參加競賽基本流程以Titanic

前言 第一次參加Kaggle的時候,看了很多入門帖,但是還是看不懂不知道到底怎麼參加,是在Kaggle上提交程式碼嗎,像網際網路公司程式設計師線上考試一樣?還是提交預測的結果? 沒有一個像”Hello World”一樣簡單但是又完整的流程,因此寫了這篇文章,

XBMC原始碼分析 4:視訊播放器dvdplayer-解碼器以ffmpeg

XBMC分析系列文章: 本文我們分析XBMC中視訊播放器(dvdplayer)中的解碼器部分。由於解碼器種類很多,不可能一一分析,因此以ffmpeg解碼器為例進行分析。 XBMC解碼器部分檔案目錄如下圖所示: 解碼器分為音訊解碼器和視訊解碼器。在這裡我們看一下視訊

XBMC原始碼分析 6:視訊播放器dvdplayer-檔案頭以ffmpeg

XBMC分析系列文章: XBMC原始碼簡析 5:視訊播放器(dvdplayer)-解複用器(以ffmpeg為例)本文我們分析XBMC中視訊播放器(dvdplayer)中的檔案頭部分。檔案頭部分裡包含的是封裝Dll用到的標頭檔案。由於檔案頭種類很多,不可能一一分析,

通過JDBC進行簡單的增刪改查以MySQL

mage ron end main exce javax xtend 探索 rman 通過JDBC進行簡單的增刪改查(以MySQL為例) 目錄 前言:什麽是JDBC 一、準備工作(一):MySQL安裝配置和基礎學習 二、準備工作(二):下載數據庫對應的jar包並

自定義shell終端提示符及顏色 以Centos

工作目錄 con bashrc func global 目錄 藍色 顯示 inux Linux修改Shell命令提示符及顏色 1. Linux登錄過程中加載配置文件順序: /etc/profile → /etc/profile.d/*.sh → ~/.bash_pro

11代碼重用思想以遊戲

class 我想 就是 cas == 點擊 ~~ 重用 body 1)情況說明:      假如 我想在我的遊戲上 顯示以惡搞菜單 然後這個菜單 顯示1和2           要是我用鼠標點擊1 ,就可以玩 貪吃蛇遊戲           要是我用鼠標點擊

Ajax數據的爬取淘女郎

gen exce on() version ref write charset 匹配 char mmtao Ajax數據的爬取(淘女郎為例) 如有疑問,轉到 Wiki 淘女郎模特抓取教程 網址:https://0x9.me/xrh6z 判斷一個頁面是不是 Ajax 加載

如何在redhat中安裝中文輸入法redhat7.3

桌面 智能拼音 出現 就會 AC 14. 文字 選項 輸入法 1.點擊桌面環境設置 2.點擊區域和語言選項 3.在輸入源中添加漢語拼音 4.配置成功後桌面環境就會出現這樣的一個標識 默認顯示為【zh】,表示中文的鍵盤,及英文字符輸入。 點擊更換“漢語(Intel

通過JDBC進行簡單的增刪改查以MySQL轉載

IE trac archive solver ttl 賦值 TP 定義 for 轉載:https://www.cnblogs.com/wuyuegb2312/p/3872607.html 目錄 前言:什麽是JDBC 一、準備工作(一):MySQL安裝配置和基礎學習 二、準備

阿裏雲修改主機名以centOS

主機 str name 按鍵 ID In 保存 ray ike 需要更改配置文件生效,修/etc/sysconfig/network裏的 HOSTNAME=主機名(可自定義),重啟生效。 如何修改? 1.[root@aliyunbaike ~]# cd /etc/sysco

OpenGL學習 著色器渲染以Android

back lse setw %d 編譯 clas ddr attr type 一、Android平臺上下文環境的創建及初始化 1. 首先實例化Android上下文環境,即EGL的初始化。 bool EGLCore::init(EGLContext sharedContex

linux正則表達式,以grep

ant per egrep 大小寫 則表達式 spa 表示 中括號 第四章 第一章 基礎正則表達式^word 匹配以word開頭的內容word$ 匹配以我word結尾的內容^$ 表示空行. 代表有且只代表任意一個字符\ 轉義符號,例如. 就只代表點本身,讓有著特殊身份意義

安裝配置mysqlwindows環境

安裝配置mysql(windows環境為例) 1.安裝 從mysql官網下載,並按照說明進行安裝 2.安裝目錄 windows 下預設安裝目錄在 C:\Program Files\MySQL Notes:安裝完成後將C:\Program Files\My