input輸入子系統

1、什麼是input輸入子系統?node

一、Linux系統支持的輸入設備繁多,例如鍵盤、鼠標、觸摸屏、手柄或者是一些輸入設備像體感輸入等等,Linux系統是如何管理如此之多的不一樣類型、不一樣原理、不一樣的輸入信息的數組

輸入設備的呢?其實就是經過input輸入子系統這套軟件體系來完成的。從總體上來講,input輸入子系統分爲3層:上層(輸入事件驅動層)、中層(輸入核心層)、數據結構

下層(輸入設備驅動層),以下圖所示:架構

聯繫以前學過的驅動框架作對比,input輸入子系統其實就是input輸入設備的驅動框架,與以前的學過的驅動框架不一樣的是,input輸入子系統分爲3層:上、中、下,因此他的複雜度框架

要高於以前講的lcd、misc、fb等的驅動框架。async

 

二、圖中Drivers對應的就是下層設備驅動層,對應各類各樣不一樣的輸入設備,Input Core對應的就是中層核心層,Handlers對應的就是上層輸入事件驅動層,最右邊的表明的是用戶空間。函數

(1)從圖中能夠看出,系統中能夠註冊多個輸入設備,每一個輸入設備的能夠是不一樣的,例如一臺電腦上能夠帶有鼠標,鍵盤....。源碼分析

(2)上層中的各個handler(Keyboard/Mouse/Joystick/Event)是屬於平行關係,他們都是屬於上層。不一樣的handler下對應的輸入設備在應用層中的接口命名方式不同,例如fetch

Mouse下的輸入設備在應用層的接口是 /dev/input/mousen (n表明0、一、2...),Joystick下的輸入設備在應用層的接口是 /dev/input/jsn(n表明0、一、2...),atom

Event下的輸入設備在應用層的接口是 /dev/input/eventn(n表明0、一、2...),這個是在input輸入子系統中實現的,下面會分析其中的起因。

(3)輸入核心層實際上是負責協調上層和下層,使得上層和下層之間可以完成數據傳遞。當下層發生輸入事件的時候,整個系統就被激活了,事件就會經過核心層傳遞到上層對應的一個/多個

handler中,最終會傳遞到應用空間。

 

三、輸入子系統解決了什麼問題?

(1)在GUI界面中,用戶的自由度太大了,能夠作的事情太多了,能夠響應不一樣的輸入類設備,並且還可以對不一樣的輸入類設備的輸入作出不一樣的動做。例如window中的一個軟

件既能夠響應鼠標輸入事件,也能夠相應鍵盤輸入事件,並且這些事件都是預先不知道的。

(2)input子系統解決了不一樣的輸入類設備的輸入事件與應用層之間的數據傳輸,使得應用層可以獲取到各類不一樣的輸入設備的輸入事件,input輸入子系統可以囊括全部的不一樣種

類的輸入設備,在應用層都可以感知到全部發生的輸入事件。

 

四、input輸入子系統如何工做?

例如以一次鼠標按下事件爲例子來講明咱們的input輸入子系統的工做過程:

當咱們按下鼠標左鍵的時候就會觸發中斷(中斷是早就註冊好的),就會去執行中斷所綁定的處理函數,在函數中就會去讀取硬件寄存器來判斷按下的是哪一個按鍵和狀態 ---->

將按鍵信息上報給input core層  ---> input core層處理好了以後就會上報給input event層,在這裏會將咱們的輸入事件封裝成一個input_event結構體放入一個緩衝區中 --->  

應用層read就會將緩衝區中的數據讀取出去。

 

五、相關的數據結構

 1 struct input_dev {
 2     const char *name;             //  input設備的名字
 3     const char *phys;              //  
 4     const char *uniq;              //
 5     struct input_id id;             //  
 6 
 7 //  這些是用來表示該input設備可以上報的事件類型有哪些   是用位的方式來表示的
 8     unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
 9     unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
10     unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
11     unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
12     unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
13     unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
14     unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
15     unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
16     unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
17 
18     unsigned int keycodemax;
19     unsigned int keycodesize;
20     void *keycode;
21     int (*setkeycode)(struct input_dev *dev,
22               unsigned int scancode, unsigned int keycode);
23     int (*getkeycode)(struct input_dev *dev,
24               unsigned int scancode, unsigned int *keycode);
25 
26     struct ff_device *ff;
27 
28     unsigned int repeat_key;
29     struct timer_list timer;
30 
31     int sync;
32 
33     int abs[ABS_CNT];
34     int rep[REP_MAX + 1];
35 
36     unsigned long key[BITS_TO_LONGS(KEY_CNT)];
37     unsigned long led[BITS_TO_LONGS(LED_CNT)];
38     unsigned long snd[BITS_TO_LONGS(SND_CNT)];
39     unsigned long sw[BITS_TO_LONGS(SW_CNT)];
40 
41     int absmax[ABS_CNT];
42     int absmin[ABS_CNT];
43     int absfuzz[ABS_CNT];
44     int absflat[ABS_CNT];
45     int absres[ABS_CNT];
46 
47     int (*open)(struct input_dev *dev);              //    設備的open函數
48     void (*close)(struct input_dev *dev);          
49     int (*flush)(struct input_dev *dev, struct file *file);
50     int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);     //  上報事件
51 
52     struct input_handle *grab;
53 
54     spinlock_t event_lock;
55     struct mutex mutex;
56 
57     unsigned int users;
58     bool going_away;
59 
60     struct device dev;                 //  內置的device結構體變量
61 
62     struct list_head    h_list;    //  用來掛接input_dev 設備鏈接的全部handle 的一個鏈表頭
63     struct list_head    node;    //  做爲鏈表節點掛接到  input_dev_list 鏈表上  (input_dev_list鏈表是input核心層維護的一個用來掛接全部input設備的一個鏈表頭)
64 };

 

 1 struct input_handler {
 2 
 3     void *private;            //  私有數據
 4 
 5     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);   //  handler用於向上層上報輸入事件的函數
 6     bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
 7     bool (*match)(struct input_handler *handler, struct input_dev *dev);            //   match 函數用來匹配handler 與 input_dev 設備
 8     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);   //  當handler 與 input_dev 匹配成功以後用來鏈接
 9     void (*disconnect)(struct input_handle *handle);          //  斷開handler 與 input_dev 之間的鏈接
10     void (*start)(struct input_handle *handle);                    
11 
12     const struct file_operations *fops;             //  一個file_operations 指針
13     int minor;                                      //  該handler 的編號 (在input_table 數組中用來計算數組下標) input_table數組就是input子系統用來管理註冊的handler的一個數據結構
14     const char *name;                               //  handler的名字
15 
16     const struct input_device_id *id_table;      //  指向一個 input_device_id  類型的數組,用來進行與input設備匹配時用到的信息
17  
18     struct list_head    h_list;       //  用來掛接handler 上鍊接的全部handle 的一個鏈表頭
19     struct list_head    node;        //  做爲一個鏈表節點掛接到 input_handler_list 鏈表上(input_handler_list 鏈表是一個由上層handler參維護的一個用來掛接全部註冊的handler的鏈表頭)
20 };

 

 1 struct input_handle {
 2 
 3     void *private;               //   handle  的私有數據
 4 
 5     int open;                     //  這個也是用來作打開計數的
 6     const char *name;       //   該handle 的名字
 7 
 8     struct input_dev *dev;                //  用來指向該handle 綁定的input_dev 結構體
 9     struct input_handler *handler;    //  用來指向該handle 綁定的 handler 結構體
10 
11     struct list_head    d_node;      //  做爲一個鏈表節點掛接到與他綁定的input_dev ->hlist 鏈表上
12     struct list_head    h_node;      //  做爲一個鏈表節點掛接到與他綁定的handler->hlist 鏈表上
13 };

 

 1 struct input_device_id {
 2 
 3     kernel_ulong_t flags;    //  這個flag 表示咱們的這個 input_device_id 是用來匹配下面的4個狀況的哪一項
 4                                     //  flag == 1表示匹配總線  2表示匹配供應商   4表示匹配產品  8表示匹配版本
 5     __u16 bustype;
 6     __u16 vendor;
 7     __u16 product;
 8     __u16 version;
 9 
10     kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
11     kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
12     kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
13     kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
14     kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
15     kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
16     kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
17     kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
18     kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
19 
20     kernel_ulong_t driver_info;
21 };

 

2、輸入核心層源碼分析(內核版本:2.6.35.7)

input輸入子系統中的全部源碼都放在 drivers\input 這個目錄中,input.c文件就是核心層的源代碼文件。在input目錄中還能夠看到一些文件夾,例如gameport、joystick

keyboard、misc、mouse....,這些文件夾裏面存放的就是屬於這類的input輸入設備的設備驅動源代碼,能夠理解爲input輸入子系統的下層。

input目錄下的evdev.c、joydev.c、mousedev.c..分別對應上層的各個不一樣的handler的源代碼。

一、輸入核心層模塊註冊函數input_init

在Linux中實現爲一個模塊的方法,因此能夠在內核配置的進行動態的加載和卸載,這樣作的起因是,存在有些系統中不須要任何

的輸入類設備,這樣就能夠將input輸入子系統這個模塊去掉(上層也是實現爲模塊的),使得內核儘可能變得更小。

 1 static int __init input_init(void)
 2 {
 3     int err;
 4 
 5     input_init_abs_bypass();
 6 
 7     err = class_register(&input_class);                //  建立設備類    /sys/class/input
 8     if (err) {
 9         printk(KERN_ERR "input: unable to register input_dev class\n");
10         return err;
11     }
12 
13     err = input_proc_init();           //    proc文件系統相關的初始化
14     if (err)
15         goto fail1;
16 
17     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);       //   註冊字符設備驅動   主設備號13   input_fops 中只實現了open函數,因此他的原理其實和misc實際上是同樣的
18     if (err) {
19         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
20         goto fail2;
21     }
22 
23     return 0;
24 
25  fail2:    input_proc_exit();
26  fail1:    class_unregister(&input_class);
27     return err;
28 }

 

(1)input_proc_init函數

 1 static int __init input_proc_init(void)
 2 {
 3     struct proc_dir_entry *entry;
 4 
 5     proc_bus_input_dir = proc_mkdir("bus/input", NULL);    /* 在/proc/bus/目錄下建立input目錄 */
 6     if (!proc_bus_input_dir)
 7         return -ENOMEM;
 8 
 9     entry = proc_create("devices", 0, proc_bus_input_dir,  /* 在/proc/bus/input/目錄下建立devices文件 */
10                 &input_devices_fileops);
11     if (!entry)
12         goto fail1;
13 
14     entry = proc_create("handlers", 0, proc_bus_input_dir, /* 在/proc/bus/input/目錄下建立handlers文件 */
15                 &input_handlers_fileops);
16     if (!entry)
17         goto fail2;
18 
19     return 0;
20 
21  fail2:    remove_proc_entry("devices", proc_bus_input_dir);
22  fail1: remove_proc_entry("bus/input", NULL);
23     return -ENOMEM;
24 }

當咱們啓動系統以後進入到proc文件系統中,確實能夠看到在/proc/bus/input/目錄下有兩個文件devices和handlers,這兩個文件就是在這裏被建立的。咱們cat devices 和 cat handlers

時對應的操做方法(show)就被封裝在input_devices_fileops和input_handlers_fileops結構體中。

 

(2)input_fops變量

 1 static int input_open_file(struct inode *inode, struct file *file)
 2 {
 3     struct input_handler *handler;                                                //  定義一個input_handler指針
 4     const struct file_operations *old_fops, *new_fops = NULL;   //  定義兩個file_operations指針
 5     int err;
 6 
 7     err = mutex_lock_interruptible(&input_mutex);
 8     if (err)
 9         return err;
10 
11     /* No load-on-demand here? */
12     handler = input_table[iminor(inode) >> 5];         //  經過次設備號在 input_table  數組中找到對應的 handler 
13     if (handler)
14         new_fops = fops_get(handler->fops);           //  將handler 中的fops 指針賦值給 new_fops
15 
16     mutex_unlock(&input_mutex);
17 
18     /*
19      * That's _really_ odd. Usually NULL ->open means "nothing special",
20      * not "no device". Oh, well...
21      */
22     if (!new_fops || !new_fops->open) {
23         fops_put(new_fops);
24         err = -ENODEV;
25         goto out;
26     }
27 
28     old_fops = file->f_op;           //   將 file->fops 先保存到 old_fops 中,以便出錯時可以恢復
29     file->f_op = new_fops;          //   用new_fops 替換 file 中 fops 
30 
31     err = new_fops->open(inode, file);       //  執行 file->open  函數
32     if (err) {
33         fops_put(file->f_op);
34         file->f_op = fops_get(old_fops);
35     }
36     fops_put(old_fops);
37 out:
38     return err;
39 }

 

二、核心層提供給設備驅動層的接口函數

input設備驅動框架留給設備驅動層的接口函數主要有3個:

      input_allocate_device。分配一塊input_dev結構體類型大小的內存

      input_set_capability。設置輸入設備能夠上報哪些輸入事件

      input_register_device。向input核心層註冊設備

(1)input_allocate_device函數

 1 struct input_dev *input_allocate_device(void)
 2 {
 3     struct input_dev *dev;                 //   定義一個 input_dev  指針
 4 
 5     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);   //  申請分配內存
 6     if (dev) {
 7         dev->dev.type = &input_dev_type;          //  肯定input設備的 設備類型     input_dev_type
 8         dev->dev.class = &input_class;                //  肯定input設備所屬的設備類   class
 9         device_initialize(&dev->dev);                   //  input設備的初始化
10         mutex_init(&dev->mutex);                        //  互斥鎖初始化
11         spin_lock_init(&dev->event_lock);            //  自旋鎖初始化
12         INIT_LIST_HEAD(&dev->h_list);                 //  input_dev -> h_list 鏈表初始化
13         INIT_LIST_HEAD(&dev->node);                 //  input_dev -> node 鏈表初始化
14 
15         __module_get(THIS_MODULE);
16     }
17 
18     return dev;
19 }

 

(2)input_set_capability函數:

函數原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

參數:dev就是設備的input_dev結構體變量

        type表示設備能夠上報的事件類型

        code表示上報這類事件中的那個事件

注意:input_set_capability函數一次只能設置一個具體事件,若是設備能夠上報多個事件,則須要重複調用這個函數來進行設置,例如:

input_set_capability(dev, EV_KEY, KEY_Q);         // 至於函數內部是怎麼設置的,將會在後面進行分析。

input_set_capability(dev, EV_KEY, KEY_W);

input_set_capability(dev, EV_KEY, KEY_E);

具體的這些類下面有哪些具體的輸入事件,請看 drivers\input\input.h 這個文件。

 

(3)input_register_device函數:

 1 int input_register_device(struct input_dev *dev)      //  註冊input輸入設備
 2 {
 3     static atomic_t input_no = ATOMIC_INIT(0);
 4     struct input_handler *handler;                          //  定義一個  input_handler 結構體指針
 5     const char *path;
 6     int error;
 7 
 8     /* Every input device generates EV_SYN/SYN_REPORT events. */
 9     __set_bit(EV_SYN, dev->evbit);                  //   每個input輸入設備都會發生這個事件
10 
11     /* KEY_RESERVED is not supposed to be transmitted to userspace. */
12     __clear_bit(KEY_RESERVED, dev->keybit);  //  清除KEY_RESERVED 事件對應的bit位,也就是不傳輸這種類型的事件
13 
14     /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
15     input_cleanse_bitmasks(dev);           //   確保input_dev中的用來記錄事件的變量中沒有提到的位掩碼是乾淨的。
16 
17     /*
18      * If delay and period are pre-set by the driver, then autorepeating
19      * is handled by the driver itself and we don't do it in input.c.
20      */
21     init_timer(&dev->timer);
22     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
23         dev->timer.data = (long) dev;
24         dev->timer.function = input_repeat_key;
25         dev->rep[REP_DELAY] = 250;
26         dev->rep[REP_PERIOD] = 33;
27     }
28 
29     if (!dev->getkeycode)
30         dev->getkeycode = input_default_getkeycode;
31 
32     if (!dev->setkeycode)
33         dev->setkeycode = input_default_setkeycode;
34 
35     dev_set_name(&dev->dev, "input%ld",                                  //   設置input設備對象的名字    input+數字
36              (unsigned long) atomic_inc_return(&input_no) - 1);
37 
38     error = device_add(&dev->dev);         //   添加設備       例如:          /sys/devices/virtual/input/input0     
39     if (error)
40         return error;
41 
42     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  //  獲取input設備對象所在的路徑      /sys/devices/virtual/input/input_xxx   
43     printk(KERN_INFO "input: %s as %s\n",
44         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
45     kfree(path);
46 
47     error = mutex_lock_interruptible(&input_mutex);
48     if (error) {
49         device_del(&dev->dev);
50         return error;
51     }
52 
53     list_add_tail(&dev->node, &input_dev_list);             //   鏈表掛接:    將 input_dev->node 做爲節點掛接到 input_dev_list  鏈表上
54 
55     list_for_each_entry(handler, &input_handler_list, node)  //  遍歷input_handler_list 鏈表上的全部handler
56         input_attach_handler(dev, handler);                        //  將handler與input設備進行匹配
57 
58     input_wakeup_procfs_readers();                //  更新proc 文件系統
59 
60     mutex_unlock(&input_mutex);
61 
62     return 0;
63 }

 

(4)input_attach_handler函數:

input_attach_handler就是input_register_device函數中用來對下層的設備驅動和上層的handler進行匹配的一個函數,只有匹配成功以後就會調用上層handler中的connect函數

進行鏈接綁定。

 1 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
 2 {
 3     const struct input_device_id *id;                //   定義一個input_device_id 的指針
 4     int error;
 5 
 6     id = input_match_device(handler, dev);   //  經過這個函數進行handler與input設備的匹配工做
 7     if (!id)
 8         return -ENODEV;
 9 
10     error = handler->connect(handler, dev, id);  //  匹配成功則調用 handler 中的 connect 函數進行鏈接
11     if (error && error != -ENODEV)
12         printk(KERN_ERR
13             "input: failed to attach handler %s to device %s, "
14             "error: %d\n",
15             handler->name, kobject_name(&dev->dev.kobj), error);
16 
17     return error;
18 }
19 
20 
21 
22 static const struct input_device_id *input_match_device(struct input_handler *handler,
23                             struct input_dev *dev)
24 {
25     const struct input_device_id *id;            //   定義一個 input_device_id  指針
26     int i;
27 
28     for (id = handler->id_table; id->flags || id->driver_info; id++) {  //  依次遍歷handler->id_table 所指向的input_device_id 數組中的各個元素
29                                                                                                                     //  依次進行下面的匹配過程
30         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)         //    匹配總線
31             if (id->bustype != dev->id.bustype)
32                 continue;
33 
34         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)  //  匹配供應商
35             if (id->vendor != dev->id.vendor)
36                 continue;
37 
38         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)  //  匹配產品
39             if (id->product != dev->id.product)
40                 continue;
41 
42         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)  //  匹配版本
43             if (id->version != dev->id.version)
44                 continue;
45 
46     //    下面的這些是匹配咱們上傳的事件是否屬實
47         MATCH_BIT(evbit,  EV_MAX);
48         MATCH_BIT(keybit, KEY_MAX);
49         MATCH_BIT(relbit, REL_MAX);
50         MATCH_BIT(absbit, ABS_MAX);
51         MATCH_BIT(mscbit, MSC_MAX);
52         MATCH_BIT(ledbit, LED_MAX);
53         MATCH_BIT(sndbit, SND_MAX);
54         MATCH_BIT(ffbit,  FF_MAX);
55         MATCH_BIT(swbit,  SW_MAX);
56 
57         if (!handler->match || handler->match(handler, dev))
58             return id;        //    若是數組中的某個匹配成功了就返回他的地址
59     }
60 
61     return NULL;
62 }

input_attach_handler函數作的事情有兩件:調用input_match_device函數進行設備與handler的匹配、匹配成功調用handler的鏈接函數進行鏈接(至於如何鏈接將會在後面說到)。

 

三、核心層提供給事件驅動層的接口函數

在input輸入核心層向事件驅動層提供的接口主要有兩個:

    input_register_handler。事件驅動層向核心層註冊handler

    input_register_handle。事件驅動層向核心層註冊handle。   注意上面的是handler,這裏是handle,不同,後面會說到。

(1)input_register_handler函數:

 1 int input_register_handler(struct input_handler *handler)    //  向核心層註冊handler
 2 {
 3     struct input_dev *dev;          //  定義一個input_dev 指針
 4     int retval; 
 5 
 6     retval = mutex_lock_interruptible(&input_mutex);
 7     if (retval)
 8         return retval;
 9 
10     INIT_LIST_HEAD(&handler->h_list);      //  初始化 handler->h_list 鏈表
11 
12     if (handler->fops != NULL) {          //  若是 handler -> fops 存在
13         if (input_table[handler->minor >> 5]) {  //  若是input_table 數組中沒有該handler  的位置了 則返回
14             retval = -EBUSY;
15             goto out;
16         }
17         input_table[handler->minor >> 5] = handler;  //  將 handler 指針存放在input_table 數組中去
18     }
19 
20     list_add_tail(&handler->node, &input_handler_list);   //  將 handler 經過 handler -> node 節點 掛接到 input_handler_list 鏈表上
21 
22     list_for_each_entry(dev, &input_dev_list, node)     //  遍歷 input_dev_list 鏈表下掛接的全部的 input_dev 設備
23         input_attach_handler(dev, handler);          //  而後進行匹配
24 
25     input_wakeup_procfs_readers();             //  更新proc 文件系統
26 
27  out:
28     mutex_unlock(&input_mutex);
29     return retval;
30 }

經過分析了上面的input_register_device和這裏的input_register_handler函數能夠知道:註冊設備的時候,不必定是先註冊了handler纔可以註冊設備。當註冊設備時,會先將

設備掛接到設備管理鏈表(input_dev_list)上,而後再去遍歷input_handler_list鏈表匹配hander。一樣對於handler註冊的時候,也會先將handler掛接到handler管理鏈表

(input_handler_list)上,而後再去遍歷input_dev_list鏈表匹配設備。因此從這裏能夠看出來,這種機制好像以前說過的platform總線下設備和驅動的匹配過程。

並且一個input_dev能夠與多個handler匹配成功,從而能夠在sysfs中建立多個設備文件,也能夠在/dev/目錄下建立多個設備節點,而且他們的次設備號是不同的,這個很好理解。

因此就是致使一個設備對應多個次設備號,那這樣有沒有錯呢?固然是沒有錯的。例如在咱們的Ubuntu中,/dev/input/event3 和 

/dev/input/mouse1 都是對應鼠標這個設備。

 

(2)input_register_handle函數

這個函數的做用就是註冊一個handle,也就是實現上圖中的將各個handle鏈接起來構成一個環形的結構,再調用這個函數以前已經將handle中的dev和handler已是填充好了的,

具體的這個函數代碼就不去分析了。

其實handler、input_dev、handle3這之間的關係,在以前就已經接觸過了,講Linux設備驅動模型底層架構的時候遇到過,下面用一副關係圖來描述他們之間的一個關係:

從本質上講,input_dev與handler是多對多的關係,從上圖能夠看出來,一個input_dev能夠對應多個handler,一個handler也能夠對應多個input_dev。由於在匹配的時候,

一個input_dev會與全部的handler都進行匹配的,並非匹配成功一次就退出。

從圖中能夠看出來,一個handle就是用來記錄系統中一對匹配成功的handler和device,咱們能夠從這個handle出發獲得handler的信息,還能夠獲得device的信息。因此正由於有這樣的

功能,因此能夠由handler通過handle最終獲取到device的信息,同理也能夠從device從發通過handle最終獲取到handler的信息。這種運用方法將會在後面的分析中看到。

 

四、總結:

核心層(其實就是驅動框架)提供的服務有哪些:

(1)建立設備類、註冊字符設備

(2)向設備驅動層提供註冊接口

(3)提供上層handler和下層device之間的匹配函數

(4)向上層提供註冊handler的接口

 

2、輸入事件驅動層源碼分析

input輸入子系統的輸入事件驅動層(上層)實際上是由各個handler構成的,各個handler之間是屬於平行關係,不存在相互調用的現象。目前用的最可能是event,今天就以這個handler

爲例分析他的源代碼,以便對handler的實現有必定的瞭解,前面說到過,input輸入子系統的源代碼都在 drivers\input\這個目錄下,其中 drivers\input\evdev.c就是event

的源代碼文件。

從evdev.c文件的末尾能夠看到使用了module_init、module_exit這些宏,說明內核中將這部分實現爲模塊的方式,這其實很好理解,由於input核心層都是實現爲模塊的方式,而

上層是要依賴於核心層纔可以註冊、纔可以工做的,而核心層都已經實現爲模塊了,那麼上層不更得須要這樣作嗎。好了,廢話很少說開始分析代碼。

一、模塊註冊函數:

evdev_handler變量就是本次分析的handler對應的結構體變量,變量中填充最重要的有3個:

evdev_event函數:

evdev_connect函數:

evdev_fops變量:

 

二、相關的數據結構

 1 struct evdev {     
 2     int exist;
 3     int open;                                //  這個是用來做爲設備被打開的計數
 4     int minor;                               //   handler 與 input設備匹配成功以後建立的設備對應的device的次設備號相對於基準次設備號的偏移量
 5     struct input_handle handle;   //   內置的一個  handle ,裏面記錄了匹配成功的input_dev 和 handler            
 6     wait_queue_head_t wait;
 7     struct evdev_client *grab;
 8     struct list_head client_list;       //   用來掛接與 evdev 匹配成功的evdev_client 的一個鏈表頭 
 9     spinlock_t client_lock; /* protects client_list */
10     struct mutex mutex;             //  互斥鎖
11     struct device dev;                 //  這個是handler 與 input設備匹配成功以後建立的設備對應的device
12 };

 

 1 struct evdev_client {
 2     struct input_event buffer[EVDEV_BUFFER_SIZE];    //  用來存放input_dev 事件的緩衝區
 3     int head;
 4     int tail;
 5     spinlock_t buffer_lock; /* protects access to buffer, head and tail */
 6     struct fasync_struct *fasync;
 7     struct evdev *evdev;              //   evdev 指針
 8     struct list_head node;            //  做爲一個鏈表節點掛接到相應的 evdev->client_list 鏈表上
 9     struct wake_lock wake_lock;
10     char name[28];            //  名字
11 };

 

1 struct input_event {
2     struct timeval time;        //  事件發生的事件
3     __u16 type;                    //  事件的類型
4     __u16 code;                   //   事件的碼值
5     __s32 value;                   //  事件的狀態
6 };

 

三、函數詳解

(1)evdev_connect函數分析:

 1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 2              const struct input_device_id *id)
 3 {
 4     struct evdev *evdev;                 //  定義一個 evdev 指針
 5     int minor;
 6     int error;
 7 
 8     for (minor = 0; minor < EVDEV_MINORS; minor++)  //  從evdev_table 數組中找到一個沒有被使用的最小的數組項  最大值32
 9         if (!evdev_table[minor])
10             break;
11 
12     if (minor == EVDEV_MINORS) {
13         printk(KERN_ERR "evdev: no more free evdev devices\n");
14         return -ENFILE;
15     }
16 
17     evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //  給evdev 申請分配內存
18     if (!evdev)
19         return -ENOMEM;
20 
21     INIT_LIST_HEAD(&evdev->client_list);            //  初始化 evdev->client_list 鏈表
22     spin_lock_init(&evdev->client_lock);              //  初始化自旋鎖 evdev->client_lock
23     mutex_init(&evdev->mutex);                          //  初始化互斥鎖 evdev->mutex
24     init_waitqueue_head(&evdev->wait);
25 
26     dev_set_name(&evdev->dev, "event%d", minor);  // 設置input設備的名字
27     evdev->exist = 1;
28     evdev->minor = minor;                                     //  input設備的次設備號的偏移量 
29 
30     evdev->handle.dev = input_get_device(dev);              //  將咱們傳進來的 input_dev 指針存放在 evdev->handle.dev 中
31     evdev->handle.name = dev_name(&evdev->dev);     //  設置 evdev -> dev 對象的名字,而且把名字賦值給 evdev->handle.name
32     evdev->handle.handler = handler;          //  將咱們傳進來的 handler 指針存放在 handle.handler 中
33     evdev->handle.private = evdev;             //  把evdev 做爲handle 的私有數據
34 
35     evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);        //  設置 evdev->device 設備的設備號
36     evdev->dev.class = &input_class;                                                  //  將 input_class 做爲 evdev->device 的設備類
37     evdev->dev.parent = &dev->dev;                                                // 將input_dev  -> device 做爲evdev->device 的父設備
38     evdev->dev.release = evdev_free;                   //  evdev -> device 設備的卸載函數
39     device_initialize(&evdev->dev);                      //  設備初始化
40 
41     error = input_register_handle(&evdev->handle);       //  註冊handle 
42     if (error)
43         goto err_free_evdev;
44 
45     error = evdev_install_chrdev(evdev);       // 安裝evdev   其實就是將evdev 結構體指針存放在evdev_table數組當中  下標就是evdev->minor
46     if (error)
47         goto err_unregister_handle;
48 
49     error = device_add(&evdev->dev);     //  添加設備到系統          /sys/devices/virtual/input/input0/event0        event0就是表示創建的設備文件
50     if (error)
51         goto err_cleanup_evdev;
52 
53     return 0;
54 
55  err_cleanup_evdev:
56     evdev_cleanup(evdev);
57  err_unregister_handle:
58     input_unregister_handle(&evdev->handle);
59  err_free_evdev:
60     put_device(&evdev->dev);
61     return error;
62 }

這裏搞清楚:   /sys/devices/virtual/input/input0  這個設備是在註冊input_dev時建立的,而input0/event0就是在handler和input_dev匹配成功以後建立的,也會在/dev/目錄

下建立設備節點。 

 

(2)evdev_open分析

 1 static int evdev_open(struct inode *inode, struct file *file)
 2 {
 3     struct evdev *evdev;                       //  定義一個 evdev 結構體指針
 4     struct evdev_client *client;             //   定義一個evdev_client 指針
 5     int i = iminor(inode) - EVDEV_MINOR_BASE;   //  經過inode 獲取 須要打開的設備對應的evdev_table 數組中的下標變量
 6     int error;
 7 
 8     if (i >= EVDEV_MINORS)
 9         return -ENODEV;
10 
11     error = mutex_lock_interruptible(&evdev_table_mutex);
12     if (error)
13         return error;
14     evdev = evdev_table[i];             //  從evdev_table  數組中找到evdev 
15     if (evdev)
16         get_device(&evdev->dev);
17     mutex_unlock(&evdev_table_mutex);
18 
19     if (!evdev)
20         return -ENODEV;
21 
22     client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);      //  給 client 申請分配內存
23     if (!client) {
24         error = -ENOMEM;
25         goto err_put_evdev;
26     }
27 
28     spin_lock_init(&client->buffer_lock);
29     snprintf(client->name, sizeof(client->name), "%s-%d",
30             dev_name(&evdev->dev), task_tgid_vnr(current));
31     wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
32     client->evdev = evdev;                          //  經過client->evdev 指針指向 evdev
33     evdev_attach_client(evdev, client);   //  其實這個函數就是作了一個鏈表掛接:  client->node  掛接到 evdev->client_list
34 
35     error = evdev_open_device(evdev); //  打開 evdev 設備   最終就會打開 input_dev -> open 函數
36     if (error)
37         goto err_free_client;
38 
39     file->private_data = client;              //   將evdev_client 做爲file 的私有數據存在
40     nonseekable_open(inode, file);
41 
42     return 0;
43 
44  err_free_client:
45     evdev_detach_client(evdev, client);
46     kfree(client);
47  err_put_evdev:
48     put_device(&evdev->dev);
49     return error;
50 }

 

四、總結:

(1)其實下層能夠上報的事件都在咱們的內核中是定義好的,咱們均可以上報這些事,可是input子系統的上層輸入事件驅動層的各個handler只可以處理某一些事件(event除外),

例如joy handler只能處理搖桿類型的事件,key handler只能處理鍵盤,內部實現的原理就是會在覈心層作handler和device匹配的過程。若是咱們的上報的事件與多個handler都

可以匹配成功,那麼綁定以後核心層會向這多個handler都上報事件,再由handler上報給應用層。

(2)input設備註冊的流程:

下層經過調用核心層的函數來向子系統註冊input輸入設備

/******************************************************************************/

input_register_device

    device_add:  /sys/devices/virtual/input/input0

    鏈表掛接: input_dev->node    ------->  input_dev_list

    input_attach_handler                          //  進行input_dev和handler之間的匹配

        調用handler->connect進行鏈接

            構建evdev結構體,加入evdev_table數組

            input_register_handle

            device_add:  /sys/devices/virtual/input/input0/event0

/*******************************************************************************/

(3)handler註冊流程

/****************************************************************/

input_register_handler

    input_table[handler->minor >> 5] = handler

    鏈表掛接:  handler->node  ----->   input_handler_list

    input_attach_handler

        handler->connect                  // 調用handler的connect函數進行鏈接

/****************************************************************/

(4)事件如何傳遞到應用層

input子系統下層經過調用input_event函數項核心層上報數據

input_event

    input_handle_event

        input_pass_event

            handler->event()            //  最終會調用到handler 中的event函數

                evdev_pass_event

                    client->buffer[client->head++] = *event;     //  會將input輸入事件數據存放在evdev_client結構體中的緩衝去中

 

當咱們的應用層經過open打開event0這個設備節點時最終會調用到input_init函數中註冊的字符設備input時註冊的file_operations->open() 函數

input_open_file

    handler = input_table[iminor(inode) >> 5]

    handler->fops->open()          

        evdev = evdev_table[i];

        evdev_open_device

            input_open_device

                input_dev->open()         //  最終就是執行input設備中的open函數

        file->private_data = evdev_client; 

 

因此當咱們在應用層調用read函數時,最終會調用到handler->fops->read函數

evdev_read

    evdev_fetch_next_event

        *event = client->buffer[client->tail++]      //  將evdev_client->buffer中的數據取走

    input_event_to_user

        copy_to_user                  //  拷貝到用戶空間

/********************************************************************************************/

 

到此爲止,input輸入子系統中還有設備驅動層沒有說到,將會在下一篇博文中補充。。。。。。。。。。。

相關文章
相關標籤/搜索