1、Linux系統支援的輸入裝置繁多,例如鍵盤、滑鼠、觸摸屏、搖桿或者是一些輸入裝置像體感輸入等等,Linux系統是如何管理如此之多的不同類型、不同原理、不同的輸入資訊的
輸入裝置的呢?其實就是通過input輸入子系統這套軟體體系來完成的。從整體上來說,input輸入子系統分為3層:上層(輸入事件驅動層)、中層(輸入核心層)、
下層(輸入裝置驅動層),如下圖所示:
聯系之前學過的驅動架構做對比,input輸入子系統其實就是input輸入裝置的驅動架構,與之前的學過的驅動架構不同的是,input輸入子系統分為3層:上、中、下,是以他的複雜度
要高于之前講的lcd、misc、fb等的驅動架構。
2、圖中Drivers對應的就是下層裝置驅動層,對應各種各樣不同的輸入裝置,Input Core對應的就是中層核心層,Handlers對應的就是上層輸入事件驅動層,最右邊的代表的是使用者空間。
(1)從圖中可以看出,系統中可以注冊多個輸入裝置,每個輸入裝置的可以是不同的,例如一台電腦上可以帶有滑鼠,鍵盤....。
(2)上層中的各個handler(Keyboard/Mouse/Joystick/Event)是屬于平行關系,他們都是屬于上層。不同的handler下對應的輸入裝置在應用層中的接口命名方式不一樣,例如
Mouse下的輸入裝置在應用層的接口是 /dev/input/mousen (n代表0、1、2...),Joystick下的輸入裝置在應用層的接口是 /dev/input/jsn(n代表0、1、2...),
Event下的輸入裝置在應用層的接口是 /dev/input/eventn(n代表0、1、2...),這個是在input輸入子系統中實作的,下面會分析其中的原由。
(3)輸入核心層其實是負責協調上層和下層,使得上層和下層之間能夠完成資料傳遞。當下層發生輸入事件的時候,整個系統就被激活了,事件就會通過核心層傳遞到上層對應的一個/多個
handler中,最終會傳遞到應用空間。
3、輸入子系統解決了什麼問題?
(1)在GUI界面中,使用者的自由度太大了,可以做的事情太多了,可以響應不同的輸入類裝置,而且還能夠對不同的輸入類裝置的輸入做出不同的動作。例如window中的一個軟
件既可以響應滑鼠輸入事件,也可以相應鍵盤輸入事件,而且這些事件都是預先不知道的。
(2)input子系統解決了不同的輸入類裝置的輸入事件與應用層之間的資料傳輸,使得應用層能夠擷取到各種不同的輸入裝置的輸入事件,input輸入子系統能夠囊括所有的不同種
類的輸入裝置,在應用層都能夠感覺到所有發生的輸入事件。
4、input輸入子系統如何工作?
例如以一次滑鼠按下事件為例子來說明我們的input輸入子系統的工作過程:
當我們按下滑鼠左鍵的時候就會觸發中斷(中斷是早就注冊好的),就會去執行中斷所綁定的處理函數,在函數中就會去讀取硬體寄存器來判斷按下的是哪個按鍵和狀态 ---->
将按鍵資訊上報給input core層 ---> input core層處理好了之後就會上報給input event層,在這裡會将我們的輸入事件封裝成一個input_event結構體放入一個緩沖區中 --->
應用層read就會将緩沖區中的資料讀取出去。
5、相關的資料結構
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.6.35.7)
input輸入子系統中的所有源碼都放在 drivers\input 這個目錄中,input.c檔案就是核心層的源代碼檔案。在input目錄中還可以看到一些檔案夾,例如gameport、joystick
keyboard、misc、mouse....,這些檔案夾裡面存放的就是屬于這類的input輸入裝置的裝置驅動源代碼,可以了解為input輸入子系統的下層。
input目錄下的evdev.c、joydev.c、mousedev.c..分别對應上層的各個不同的handler的源代碼。
1、輸入核心層子產品注冊函數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 }
2、核心層提供給裝置驅動層的接口函數
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的連接配接函數進行連接配接(至于如何連接配接将會在後面說到)。
3、核心層提供給事件驅動層的接口函數
在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的資訊。這種運用方法将會在後面的分析中看到。
4、總結:
核心層(其實就是驅動架構)提供的服務有哪些:
(1)建立裝置類、注冊字元裝置
(2)向裝置驅動層提供注冊接口
(3)提供上層handler和下層device之間的比對函數
(4)向上層提供注冊handler的接口
二、輸入事件驅動層源碼分析
input輸入子系統的輸入事件驅動層(上層)其實是由各個handler構成的,各個handler之間是屬于平行關系,不存在互相調用的現象。目前用的最多是event,今天就以這個handler
為例分析他的源代碼,以便對handler的實作有一定的了解,前面說到過,input輸入子系統的源代碼都在 drivers\input\這個目錄下,其中 drivers\input\evdev.c就是event
的源代碼檔案。
從evdev.c檔案的末尾可以看到使用了module_init、module_exit這些宏,說明核心中将這部分實作為子產品的方式,這其實很好了解,因為input核心層都是實作為子產品的方式,而
上層是要依賴于核心層才能夠注冊、才能夠工作的,而核心層都已經實作為子產品了,那麼上層不更得需要這樣做嗎。好了,廢話不多說開始分析代碼。
1、子產品注冊函數:
evdev_handler變量就是本次分析的handler對應的結構體變量,變量中填充最重要的有3個:
evdev_event函數:
evdev_connect函數:
evdev_fops變量:
2、相關的資料結構
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 };
3、函數詳解
(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輸入子系統中還有裝置驅動層沒有說到,将會在下一篇博文中補充。。。。。。。。。。。