Linux 內核為了處理各種不同類型的的輸入設備 , 比如說鼠標 , 鍵盤 , 操縱桿 , 觸摸屏 , 設計並實現了一個對上層應用統一的試圖的抽象層 , 即是Linux 輸入子系統 .
輸入子系統的層次結構體如下
從底層到上層 , input 子系統由 設備驅動層 , 核心層 , 以及事件處理層3個部分組成
當一個鼠標移動, 一個按鍵按下或彈起 , 它都需要從底層設備驅動-->核心層-->事件處理層 -->用戶空間 , 層層上報 , 一直到運用程序.
應用這個input 子系統有如下優點 :
1. 統一了各種各樣的輸入設備的處理方法.
2. 提供了用於分布給用戶使用的簡單接口
3 . 提煉了輸入驅動程序的通用部分 , 簡化了驅動程序的開發和移植工作.
首先為大家上一個假設有個按鍵的驅動源 , 通過這個源碼一層一層分析它是怎么事件上報的.
1 #include "../bus.h" 2 //這里面包含必要的頭文件 3 4 #define GPX3CON 0x11000C60 5 6 7 static struct input_dev *device = NULL; 8 static volatile unsigned int __iomem *gpxconf = NULL; 9 10 static int irqs[] = { 11 IRQ_EINT(26), 12 IRQ_EINT(27), 13 IRQ_EINT(28), 14 IRQ_EINT(29), 15 }; 16 17 void HardWare_init(void) 18 { 19 gpxconf = ioremap(GPX3CON , SZ_4K); 20 } 21 void HardWare_exit(void) 22 { 23 iounmap(gpxconf); 24 } 25 26 27 irqreturn_t do_irq(int irqno , void *data) 28 { 29 //事件上報 30 //算出第幾個按鍵 31 int keynum = irqno - IRQ_EINT(26) ; //0 - 3 32 unsigned int val ; 33 34 val = ioread32(gpxconf + 1); 35 val = val >> 2 ; 36 37 //printk("keynum:%d val:%#x \n" , keynum , val); 38 switch(keynum) 39 { //事件上報函數. 40 case 0: input_report_key(device,KEY_A, !(val & (1 << keynum)) ); break; 41 case 1: input_report_key(device,KEY_B, !(val & (1 << keynum)) ); break; 42 case 2: input_report_key(device,KEY_C, !(val & (1 << keynum)) ); break; 43 case 3: input_report_key(device,KEY_D, !(val & (1 << keynum)) ); break; 44 } 45 46 //同步 47 input_sync(device); 48 49 return IRQ_HANDLED ; 50 } 51 52 53 //************************************************* 54 //模塊入口出口 55 56 static int __init test_init(void) 57 { 58 int ret ; 59 int i ; 60 61 for(i = 0 ; i < ARRAY_SIZE(irqs) ; i++) 62 { //請求中斷 63 ret = request_irq(irqs[i] , do_irq , IRQF_TRIGGER_FALLING| IRQF_TRIGGER_RISING | IRQF_SHARED , "myirq",(void *)88); 64 if(ret < 0) 65 { goto Err ; 66 } 67 } 68 //分配一個事件上報設備結構體 69 device = input_allocate_device(); 70 if(IS_ERR(device)) 71 { 72 ret = PTR_ERR(device); 73 goto Err1 ; 74 } 75 76 //#define BITS_PER_LONG 32 77 //#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) 78 //#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) 79 80 device->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); //使能按鍵事件類型 81 device->keybit[BIT_WORD(KEY_A)] |= BIT_MASK(KEY_A) ; //使能按鍵值為A的事件 82 device->keybit[BIT_WORD(KEY_B)] |= BIT_MASK(KEY_B) ; //使能按鍵值為A的事件 83 device->keybit[BIT_WORD(KEY_C)] |= BIT_MASK(KEY_C) ; //使能按鍵值為A的事件 84 device->keybit[BIT_WORD(KEY_D)] |= BIT_MASK(KEY_D) ; //使能按鍵值為A的事件 85 86 87 //注冊一個input設備 上報類型 上報鍵值 88 ret = input_register_device(device); 89 if(ret < 0) 90 { 91 goto Err2 ; 92 } 93 94 //硬件初始化 95 HardWare_init(); 96 97 return ret ; 98 99 Err2: 100 input_free_device(device); 101 Err1: 102 Err: 103 while(--i) 104 { 105 free_irq(irqs[i] , (void *)88); 106 } 107 108 return ret ; 109 } 110 111 static void __exit test_exit(void) 112 { 113 int i ; 114 115 input_unregister_device(device); 116 117 input_free_device(device); 118 119 for(i = 0 ; i < ARRAY_SIZE(irqs) ; i++) 120 { 121 free_irq(irqs[i] , (void *)88); 122 } 123 124 HardWare_exit(); 125 } 126 127 module_init(test_init); 128 module_exit(test_exit); 129 130 MODULE_LICENSE("GPL");
先對他的一個主體流程進行分析一下
代碼分析:
模塊入口函數為test_init
它首先去申請了4個按鍵的中斷 , 雙邊緣觸發 .
而后 , 它分配一個事件上報結構體的空間 , 並對其進行賦值 :
1 static struct input_dev *device = NULL;
1 //分配一個事件上報設備結構體 2 device = input_allocate_device();
1 device->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); //使能按鍵事件類型 2 device->keybit[BIT_WORD(KEY_A)] |= BIT_MASK(KEY_A) ; //使能按鍵值為A的事件 3 device->keybit[BIT_WORD(KEY_B)] |= BIT_MASK(KEY_B) ; //使能按鍵值為A的事件 4 device->keybit[BIT_WORD(KEY_C)] |= BIT_MASK(KEY_C) ; //使能按鍵值為A的事件 5 device->keybit[BIT_WORD(KEY_D)] |= BIT_MASK(KEY_D) ; //使能按鍵值為A的事件
這個結構體的原型是這樣的:
1 struct input_dev { 2 const char *name; //設備名字 3 const char *phys; //設備在系統中的物理路徑 4 const char *uniq; //設備的唯一標識碼 5 struct input_id id; //設備id,包含總線id ,廠商id , 與input_handler匹配的時會用到 6 7 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; //設備性質 8 9 unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//設備支持的事件類型 10 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//支持的具體的按鍵。按鈕事件 11 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//支持的具體的相對坐標事件 12 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//支持的具體的絕對坐標事件 13 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//支持的具體的混雜事件 14 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//支持的具體的led指示燈事件 15 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//支持的具體的音效事件 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//支持的具體的力反饋事件 16 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//支持的具體的開關事件 17 18 unsigned int hint_events_per_packet; 19 20 unsigned int keycodemax; // 鍵盤碼表的大小 21 unsigned int keycodesize; //鍵盤碼中的元素個數 22 void *keycode; //設備的鍵盤碼表 23 /* 兩個可選方法 , 用於配置和獲取鍵盤碼表 */ 24 int (*setkeycode)(struct input_dev *dev, 25 const struct input_keymap_entry *ke, 26 unsigned int *old_keycode); 27 int (*getkeycode)(struct input_dev *dev, 28 struct input_keymap_entry *ke); 29 30 struct ff_device *ff; /* 如果設備支持力反饋,則該成員將指向力反饋的設備描述 31 結構 */ 32 33 unsigned int repeat_key; /*保存上一個鍵值,實現軟件自動重復按鍵 */ 34 struct timer_list timer; /*用於軟件自動重復按鍵的定時器 */ 35 36 int rep[REP_CNT]; 37 38 struct input_mt_slot *mt; 39 int mtsize; 40 int slot; 41 int trkid; 42 43 struct input_absinfo *absinfo; 44 45 unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反映設備按鍵按鈕的當前狀態 46 unsigned long led[BITS_TO_LONGS(LED_CNT)];//反映設備led燈的當前狀態 47 unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映設備音效的當前狀態 48 unsigned long sw[BITS_TO_LONGS(SW_CNT)];//反映設備開關的當前狀態 49 50 /* 提供以下4個設備驅動層的操作接口,根據具體的設備需求實現他們 */ 51 int (*open)(struct input_dev *dev); //將在input_open_device()中調用 52 void (*close)(struct input_dev *dev);//將在input_close_device()中調用 53 int (*flush)(struct input_dev *dev, struct file *file); 55 /*(event)用於處理送到設備驅動層來的事件,很多事件在事件處理層被處理,但有很多事件仍需 56 要送到設備驅動中,如led指示燈事件和音效事件,因為這些操作通常需要設備驅動執行*/ 57 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); 58 59 struct input_handle __rcu *grab; 60 61 spinlock_t event_lock; 62 struct mutex mutex; 63 64 /* 記錄輸入事件處理程序(input handlers)調用設備open()方法的次數,保證設備open()是在第一個調用input_open_device()中被調用, 設備close()方法是在最后一次調用input_close_device()中被調用 */ 65 unsigned int users; 66 bool going_away; 67 68 bool sync; //上次同步事件(EV_SYNC)發生后沒有新事件產生,則被設置為1*/ 69 70 struct device dev; //內嵌設備device結構 71 struct list_head h_list; //與該設備相關的輸入句柄(input handles)列表 72 struct list_head node;//通過該成員,系統中的所有的input_dev對象被管理 73 };
上面是我做的一些注釋 .
而且在上面的按鍵設備的驅動中 , 我還對evbit , 和keybit 兩個數值進行賦值.
它那個宏定義是這樣的:
1 //#define BITS_PER_LONG 32 2 //#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) 3 //#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
這里邊所支持的事件類型和支持的具體的按鍵按鈕類型是這樣的:
1 #define EV_SYN 0x00 2 #define EV_KEY 0x01 3 #define EV_REL 0x02 4 #define EV_ABS 0x03 5 #define EV_MSC 0x04 6 #define EV_SW 0x05 7 #define EV_LED 0x11 8 #define EV_SND 0x12
9 #define EV_REP 0x14 9 #define EV_FF 0x15 10 #define EV_PWR 0x16 11 #define EV_FF_STATUS 0x17
12 #define EV_MAX 0x1f 12 #define EV_CNT (EV_MAX+1)
所支持的按鍵按鈕類型就比較多了
1 2 #define KEY_RESERVED 0 3 #define KEY_ESC 1 4 #define KEY_1 2 5 #define KEY_2 3 6 #define KEY_3 4 7 #define KEY_4 5 8 #define KEY_5 6 9 #define KEY_6 7 10 #define KEY_7 8 11 #define KEY_8 9 12 #define KEY_9 10 13 #define KEY_0 11 14 ......
這些相對應的類型都是用一些宏定義來定義 , 到上報個核心層再上報到事件處理層的時候以一個結構體的形式的數據體現給用戶, 后面會講到這個結構體
回到源代碼:
然后就是注冊一個 input 設備 把剛才賦值的事件上報類型傳進去:
1 //注冊一個input設備 上報類型 上報鍵值 2 ret = input_register_device(device); 3 if(ret < 0) 4 { 5 goto Err2 ; 6 }
后面的硬件初始化就是對gpio口的配置而已 , 這里涉及到硬件原理圖上的東西 ,其實就是做了一個寄存器地址的映射
在中斷函數內
1 irqreturn_t do_irq(int irqno , void *data) 2 { 3 //事件上報 4 //算出第幾個按鍵 5 int keynum = irqno - IRQ_EINT(26) ; //0 - 3 6 unsigned int val ; 7 8 val = ioread32(gpxconf + 1); 9 val = val >> 2 ; 10 11 //printk("keynum:%d val:%#x \n" , keynum , val); 12 switch(keynum) 13 { 14 case 0: input_report_key(device,KEY_A, !(val & (1 << keynum)) ); break; 15 case 1: input_report_key(device,KEY_B, !(val & (1 << keynum)) ); break; 16 case 2: input_report_key(device,KEY_C, !(val & (1 << keynum)) ); break; 17 case 3: input_report_key(device,KEY_D, !(val & (1 << keynum)) ); break; 18 } 19 20 //同步 21 input_sync(device); 22 23 return IRQ_HANDLED ; 24 } 25
其實總的來說我們做了兩個事情 , 第一個就是觸發中斷后 ,我們上報了是第幾個按鍵觸發的中斷 , 並進行同步處理.
現在 , 我已經把這個最簡單的一個按鍵事件上報驅動講解了一遍 , 其實我們有很多疑問 :
第一個就是: 我們怎么分配的一個事件上報結構體
第二個問題 , 我們是怎么將事件層層上報 , 將input driver , input core , input handler 這三層聯系起來的 .
第三個問題 , 我們給上層提供的是一個怎么樣的借口 , 給上層上報的是一個怎么的數據.
其實第一個問題比較簡單 , 我只是為了拿出來緩解一下當前比較緊張的氣氛 , 透露一下 , 第二個問題才是關鍵!
第一個問題它只是做了一個分配空間並對其初始化值的操作:
1 *///申請一個新的input device 2 struct input_dev *input_allocate_device(void) 3 { 4 struct input_dev *dev; 5 6 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 7 if (dev) { 8 dev->dev.type = &input_dev_type; 9 dev->dev.class = &input_class; 10 device_initialize(&dev->dev); 11 mutex_init(&dev->mutex); 12 spin_lock_init(&dev->event_lock); 13 INIT_LIST_HEAD(&dev->h_list); 14 INIT_LIST_HEAD(&dev->node); 15 16 __module_get(THIS_MODULE); 17 } 18 19 return dev; 20 } 21 EXPORT_SYMBOL(input_allocate_device);
是不是覺得自己竟然能兩眼就能看懂這些代碼 , 別驚訝 , 其實你自己挺強的 , 就連我都要花喵一眼的時間去看才能看懂它
下面才是重頭戲: input core 代碼.
在前面的驅動當中 , 我們在分配完input_dev 結構體后 , 我們直接在沒有產生並觸發按鍵中斷之前 , 搶先注冊了一個input 設備
1 //注冊一個input設備 上報類型 上報鍵值 2 ret = input_register_device(device);
其實如果按照標准的寫法 , 我們注冊一個input 設備應該寫在請求中斷函數之前
就是這個input_register_device , 他就是帶我們進入input core的引路人:
我們跟進去: 在include/linux/input.h 里面定義
1 //輸入子系統的注冊函數 2 int __must_check input_register_device(struct input_dev *); 3 void input_unregister_device(struct input_dev *); 4 //輸入子系統的注銷函數
它的源碼是在 drivers/input/input.c 里面
1 /** 2 * input_register_device - register device with input core 3 * @dev: device to be registered 4 * 5 * This function registers device with input core. The device must be 6 * allocated with input_allocate_device() and all it's capabilities 7 * set up before registering. 8 * If function fails the device must be freed with input_free_device(). 9 * Once device has been successfully registered it can be unregistered 10 * with input_unregister_device(); input_free_device() should not be 11 * called in this case. 12 *///輸入設備注冊函數 13 int input_register_device(struct input_dev *dev) 14 { 15 static atomic_t input_no = ATOMIC_INIT(0); 16 struct input_handler *handler; 17 const char *path; 18 int error; 19 20 /* Every input device generates EV_SYN/SYN_REPORT events. */ 21 __set_bit(EV_SYN, dev->evbit); 22 /* 任何一個設備都支持同步事件 */ 23 24 /* KEY_RESERVED is not supposed to be transmitted to userspace. */ 25 __clear_bit(KEY_RESERVED, dev->keybit); 26 27 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ 28 input_cleanse_bitmasks(dev); 29 30 if (!dev->hint_events_per_packet) 31 dev->hint_events_per_packet = 32 input_estimate_events_per_packet(dev); 33 34 35 /* 36 * If delay and period are pre-set by the driver, then autorepeating 37 * is handled by the driver itself and we don't do it in input.c. 38 */ //rep 主要時為了處理重復按鍵,如果沒有定義dev->rep[REP_DELAY]和 39 // dev->rep[REP_PERIOD]則將其值設為默認 40 init_timer(&dev->timer); 41 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 42 dev->timer.data = (long) dev; 43 dev->timer.function = input_repeat_key; 44 dev->rep[REP_DELAY] = 250; //這個是指第一次按下去多久算一次 45 dev->rep[REP_PERIOD] = 33;//這個是指按鍵多久沒有被抬起,則33ms算一次 46 } 47 /* 如果dev沒有定義getkeycode 和setkeycode 則設為默認值 */ 48 if (!dev->getkeycode) 49 dev->getkeycode = input_default_getkeycode; 50 51 if (!dev->setkeycode) 52 dev->setkeycode = input_default_setkeycode; 53 54 dev_set_name(&dev->dev, "input%ld", 55 (unsigned long) atomic_inc_return(&input_no) - 1); 56 /* 將input_dev內嵌的dev注冊到sysfs */ 57 error = device_add(&dev->dev); 58 if (error) 59 return error; 60 61 path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); 62 pr_info("%s as %s\n", 63 dev->name ? dev->name : "Unspecified device", 64 path ? path : "N/A"); 65 kfree(path); 66 67 error = mutex_lock_interruptible(&input_mutex); 68 if (error) { 69 device_del(&dev->dev); 70 return error; 71 } 72 /* 將input_dev 掛在input_dev_list上 */ 73 list_add_tail(&dev->node, &input_dev_list); 74 /* 遍歷所有的事件的處理驅動,找到匹配的就將該設備依附上去 */ 75 list_for_each_entry(handler, &input_handler_list, node) 76 input_attach_handler(dev, handler); //進去看看 77 78 input_wakeup_procfs_readers(); 79 80 mutex_unlock(&input_mutex); 81 82 return 0; 83 } 84 EXPORT_SYMBOL(input_register_device);
它的注釋講的很明白 , 注冊一個設備 , 在使用這個函數之前必須調用input_allocate_device() 並對input_dev 結構體進行必要的賦值
而且這個函數必須作返回值檢查 , 如果出錯必須對input_dev進行free
先來看一下這里邊出現的input_handler 結構體
1 struct input_handler { 2 3 void *private; //由具體的事件處理程序指定私有數據 4 5 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //用於處理送到事件處理層的事件,該方法由核心層調用,調用時已禁止中斷 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); //這是匹配handler 和設備的函數 8 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//事件處理程序關聯到一個輸出設備時調用 9 void (*disconnect)(struct input_handle *handle);//事件處理程序脫離與之有關的 10 輸入設備時調用 11 void (*start)(struct input_handle *handle); 12 //開啟對給定句柄的處理程序,該函數由核心層在connect()方法調用之后,或者在獨占句柄釋放它占有的輸入設備時調用 13 14 const struct file_operations *fops; //該事件處理驅動的文件操作集合 15 int minor; //該驅動能夠提供的32個連續次設備號中的第一個 16 const char *name; //該驅動處理程序的名稱,可以在/proc/bus/input/handlers 中看到 17 18 const struct input_device_id *id_table;//輸入設備id表, 事件處理驅動通過該成員來匹配他能處理的設備 19 20 struct list_head h_list; //該事件處理程序相關聯的輸入句柄列表 21 //所有事件處理驅動都會通過該成員連接到input_handler_list列表上 22 struct list_head node; 23 };
上面有我花了很多(就那么幾分鍾)的時間去作的注釋 , 大家可以看看
其實這就是一個很多個操作接口的結構體 , 要處理什么事情的時候就會調用這里邊的某個函數 , 事件處理的時候調用里邊的event 函數 或者調用filter函數 ,
匹配input_handler 和 input_dev 的時候調用match函數 還有就是一些必要的變量還有就是一些文件操作的集合 , 還有一個id_table的表
我們回到input_register_device()函數
在變量設定之后 , kernel 直接設置
1 /* Every input device generates EV_SYN/SYN_REPORT events. */ 2 __set_bit(EV_SYN, dev->evbit); 3 /* 任何一個設備都支持同步事件 */ 4 5 /* KEY_RESERVED is not supposed to be transmitted to userspace. */ 6 __clear_bit(KEY_RESERVED, dev->keybit); 7 8 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ 9 input_cleanse_bitmasks(dev);
這里時設置了一個重復按鍵的功能 , 就是長按了一個鍵后的那個按能設置.
1 /* 2 * If delay and period are pre-set by the driver, then autorepeating 3 * is handled by the driver itself and we don't do it in input.c. 4 */ //rep 主要時為了處理重復按鍵,如果沒有定義dev->rep[REP_DELAY]和 5 // dev->rep[REP_PERIOD]則將其值設為默認 6 init_timer(&dev->timer); 7 if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 8 dev->timer.data = (long) dev; 9 dev->timer.function = input_repeat_key; 10 dev->rep[REP_DELAY] = 250; //這個是指第一次按下去多久算一次 11 dev->rep[REP_PERIOD] = 33;//這個是指按鍵多久沒有被抬起,則33ms算一次 12 }
1 /* 如果dev沒有定義getkeycode 和setkeycode 則設為默認值 */ 2 if (!dev->getkeycode) 3 dev->getkeycode = input_default_getkeycode; 4 5 if (!dev->setkeycode) 6 dev->setkeycode = input_default_setkeycode;
執行這段代碼后 ,我們將會在文件系統中找到/sys/class/input/input* 設備
1 dev_set_name(&dev->dev, "input%ld", 2 (unsigned long) atomic_inc_return(&input_no) - 1);
1 /* 將input_dev內嵌的dev注冊到sysfs */ 2 error = device_add(&dev->dev);
這里它做了兩件事 , 一件是將dev的node 掛在input_dev_list 上 , 以后可以通過這一塊肉找到整個dev結構體 , 內核里邊全是這種方法 (container of())的方法
第二件事件就是遍歷所有的input_handler_list , 匹配設備和設備事件處理方法
1 /* 將input_dev 掛在input_dev_list上, 把dev->node 加到input_dev_list */ 2 list_add_tail(&dev->node, &input_dev_list); /* 遍歷所有的事件的處理驅動(handler),找到匹配的就將該設備依附上去 */ 3 list_for_each_entry(handler, &input_handler_list, node) 4 input_attach_handler(dev, handler); //進去看看
我只分析里邊的input_attach_handler(dev , handler) , 前面兩個相對簡單易懂
1 //找到匹配的就將該設備依附上去 2 static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) 3 { 4 const struct input_device_id *id; 5 int error; 6 /* 調用匹配id 的函數, 如果沒有匹配上 , 則推出依附過程*/ 7 id = input_match_device(handler, dev); 8 if (!id) 9 return -ENODEV; 10 /* 如果匹配上了, 就調用事件處理驅動的connect()接口 */ 11 error = handler->connect(handler, dev, id); 12 if (error && error != -ENODEV) 13 pr_err("failed to attach handler %s to device %s, error: %d\n", 14 handler->name, kobject_name(&dev->dev.kobj), error); 15 16 return error; 17 }
在這里邊 , 得到一個正確的id 的方法時比較嚴格的 , 這就跟外面的相親的過程一樣 , 要算八字(是不是同一個版本,是不是同一個總線類型 , 是不是同一個生產商 , 然后還要比較各種位 , 最后還要確定handler->match為空或者是這是匹配handler 和 dev
最后通過層層考驗 , 才返回一個id , 沒辦法, 這講究一個門當戶對! 源代碼如下:
1 static const struct input_device_id *input_match_device(struct input_handler *handler, 2 struct input_dev *dev) 3 { 4 const struct input_device_id *id; 5 int i; 6 7 for (id = handler->id_table; id->flags || id->driver_info; id++) { 8 9 if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) 10 if (id->bustype != dev->id.bustype) 11 continue; 12 13 if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) 14 if (id->vendor != dev->id.vendor) 15 continue; 16 17 if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) 18 if (id->product != dev->id.product) 19 continue; 20 21 if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) 22 if (id->version != dev->id.version) 23 continue; 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 if (!handler->match || handler->match(handler, dev)) 36 return id; 37 } 38 39 return NULL; 40 }
回到上面的input_attach-handler 函數 , 當兩個人( handler , dev) 相親成功了 , 我們就調用handler->connect這個連接的方法
其實這里有一個毀我人生價值觀的問題 , 就是一個handler 有可能不僅僅只有一個device 跟它對應 , 相對的 , 一個device 有可能也有多個設備驅動事件處理函數跟它相對應
我只是打一個簡單的比方 , 大家別糾結上面的問題 , 他們關系時 1 對多的關系.
其實上面那段代碼就已經涉及了input 核心層的東西了
input 核心層 , 其實在一開始的時候
init/main.c ---> start_kernel --->rest_init() ---> kernel_thread(kernel_init , NULL , CLONE_FS | CLONE_SIGHAND) ;
---> kernel_init ---> do_basic_setup() ---> do_initcalls() --- >
1 static void __init do_initcalls(void) 2 { 3 initcall_t *fn; 4 5 for (fn = __early_initcall_end; fn < __initcall_end; fn++) 6 do_one_initcall(*fn); 7 }
這里邊它就是會調用subsys_initcall(input_init); , 他是調用了所有的sub_initcall 里面的函數
調用之后 , 便有如下代碼
1 static int __init input_init(void) 2 { 3 int err; 4 5 err = class_register(&input_class); /* 創建一個類input 的class */ 6 if (err) { 7 pr_err("unable to register input_dev class\n"); 8 return err; 9 } 10 11 err = input_proc_init(); /* 在proc 文件系統下創建入口項 */ 12 if (err) 13 goto fail1; 14 15 err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 16 /* 注冊設備號INPUT_MAJOR(13)的字符設備, 文件操作集合為input_fops */ 17 if (err) { 18 pr_err("unable to register char major %d", INPUT_MAJOR); 19 goto fail2; 20 } 21 22 return 0; 23 24 fail2: input_proc_exit(); 25 fail1: class_unregister(&input_class); 26 return err; 27 }
其實它就是為了給我們創建了一個設備類並提供了文件系統的操作接口.
這個文件中 , 還有一個借口 是我們必須要了解的
那就是input_report_key
在前面我給的那個驅動程序中 , 如果按鍵按下或彈起 , 都會產生一個中斷 , 雙邊緣觸發中斷 , 然后在那個中斷函數里面 , 就會有一個input_report_key的函數被調用,
其實在內核中 , 他是調用了input_event 這個函數
源代碼也在 input.c 這個文件中:
1 static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) 2 { input_event(dev, EV_KEY, code, !!value); 3 } 4 static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value) 5 { 6 input_event(dev, EV_REL, code, value); 7 }
其實input_report_rel 還有幾個函數都是調用了input_event , 只是給的標志位不相同 , 所以功能上由一點點區別
源碼如下:
1 void input_event(struct input_dev *dev, 2 unsigned int type, unsigned int code, int value) 3 { 4 unsigned long flags; 5 /* 首先判斷設備是否支持該事件類型*/ 6 if (is_event_supported(type, dev->evbit, EV_MAX)) { 7 8 spin_lock_irqsave(&dev->event_lock, flags); 9 add_input_randomness(type, code, value);//利用輸入值調整隨機數產生器 10 input_handle_event(dev, type, code, value);//進這里,實際調用了這個 11 spin_unlock_irqrestore(&dev->event_lock, flags); 12 } 13 } 14 EXPORT_SYMBOL(input_event);
這里邊做了設備檢查 , 判斷設備是否支持該事件類型
如果支持 , 進入input_handle_event這里邊:
1 static void input_handle_event(struct input_dev *dev, 2 unsigned int type, unsigned int code, int value) 3 { 4 int disposition = INPUT_IGNORE_EVENT; 5 6 switch (type) { 7 8 case EV_SYN: 9 switch (code) { 10 case SYN_CONFIG: 11 disposition = INPUT_PASS_TO_ALL; 12 break; 13 14 case SYN_REPORT: 15 if (!dev->sync) { 16 /* 將同步標志置1 */ 17 dev->sync = true; 18 /* 表示將該事件送往事件處理驅動處理 */ 19 disposition = INPUT_PASS_TO_HANDLERS; 20 } 21 break; 22 case SYN_MT_REPORT: 23 dev->sync = false; 24 disposition = INPUT_PASS_TO_HANDLERS; 25 break; 26 } 27 break; 28 29 /* 30 * 如果設備支持該事件碼且本次上報值同dev->key中記錄的上次上報值不同, 31 * 則將事件的派送位置設置為送往事件處理驅動,並且如果判斷按鍵時按下的, 32 * 則啟動自動重復按鍵 33 * */ 34 case EV_KEY: 35 if (is_event_supported(code, dev->keybit, KEY_MAX) && 36 !!test_bit(code, dev->key) != value) { 37 38 if (value != 2) { 39 __change_bit(code, dev->key); 40 if (value) 41 input_start_autorepeat(dev, code); 42 else 43 input_stop_autorepeat(dev); 44 } 45 46 disposition = INPUT_PASS_TO_HANDLERS; 47 } 48 break; 49 50 case EV_SW: 51 if (is_event_supported(code, dev->swbit, SW_MAX) && 52 !!test_bit(code, dev->sw) != value) { 53 54 __change_bit(code, dev->sw); 55 disposition = INPUT_PASS_TO_HANDLERS; 56 } 57 break; 58 59 case EV_SW: 60 if (is_event_supported(code, dev->swbit, SW_MAX) && 61 !!test_bit(code, dev->sw) != value) { 62 63 __change_bit(code, dev->sw); 64 disposition = INPUT_PASS_TO_HANDLERS; 65 } 66 break; 67 68 case EV_ABS: 69 if (is_event_supported(code, dev->absbit, ABS_MAX)) 70 disposition = input_handle_abs_event(dev, code, &value); 71 72 break; 73 74 case EV_REL: 75 if (is_event_supported(code, dev->relbit, REL_MAX) && value) 76 disposition = INPUT_PASS_TO_HANDLERS; 77 78 break; 79 80 case EV_MSC: 81 if (is_event_supported(code, dev->mscbit, MSC_MAX)) 82 disposition = INPUT_PASS_TO_ALL; 83 84 break; 85 86 case EV_LED: 87 if (is_event_supported(code, dev->ledbit, LED_MAX) && 88 !!test_bit(code, dev->led) != value) { 89 90 __change_bit(code, dev->led); 91 disposition = INPUT_PASS_TO_ALL; 92 } 93 break; 94 95 case EV_SND: 96 if (is_event_supported(code, dev->sndbit, SND_MAX)) { 97 98 if (!!test_bit(code, dev->snd) != !!value) 99 __change_bit(code, dev->snd); 100 disposition = INPUT_PASS_TO_ALL; 101 } 102 break; 103 104 case EV_REP: 105 if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) { 106 dev->rep[code] = value; 107 disposition = INPUT_PASS_TO_ALL; 108 } 109 break; 110 111 case EV_FF: 112 if (value >= 0) 113 disposition = INPUT_PASS_TO_ALL; 114 break; 115 116 case EV_PWR: 117 disposition = INPUT_PASS_TO_ALL; 118 break; 119 } 120 /* 只有上報了同步事件並且事件碼是SYN_REPORT, 同步標志dev->sync才會置1 */ 121 if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) 122 dev->sync = false; 123 /* 如果設置了送往設備的標志位,那么將調用設備的event()方法 */ 124 if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) 125 dev->event(dev, type, code, value); 126 /* 如果設置了表示送往事件處理驅動的標志位, 127 * 那么將調用input_pass_event的方法*/ 128 if (disposition & INPUT_PASS_TO_HANDLERS) 129 input_pass_event(dev, type, code, value); 130 }
在驗證disposition 和INPUT_PASS_TO_HANDLERS后
進入input_pass_event
1 static void input_pass_event(struct input_dev *dev, 2 unsigned int type, unsigned int code, int value) 3 { 4 struct input_handler *handler; 5 struct input_handle *handle; 6 7 rcu_read_lock(); 8 9 handle = rcu_dereference(dev->grab); 10 if (handle) 11 handle->handler->event(handle, type, code, value); 12 else { 13 bool filtered = false; 14 15 list_for_each_entry_rcu(handle, &dev->h_list, d_node) { 16 if (!handle->open) 17 continue; 18 19 handler = handle->handler; 20 if (!handler->filter) { 21 if (filtered) 22 break; 23 24 handler->event(handle, type, code, value); 25 26 } else if (handler->filter(handle, type, code, value)) 27 filtered = true; 28 } 29 } 30 31 rcu_read_unlock(); 32 }
最終還是要調用event函數
input 核心層代碼先到這里 , 接下來就到事件上報處理層 ,
input 輸入子系統最重要的部分在於對於上報事件的處理. 內核實現了多個事件處理驅動 , 如處理通用設備的evdev , 處理鼠標類的輸入設備的mousedev 和處理游戲桿的joydev
下面已最基礎的通用事件處理驅動為例子 , 分析輸入子系統處理上報事件的方法:
通用事件處理驅動的實現文件 evdev.c 中注冊一個名為evdev_handler 和 input_handler的對象.
1 static int __init evdev_init(void) 2 { 3 return input_register_handler(&evdev_handler); 4 }
evdev_handler :
1 2 static struct input_handler evdev_handler = { 3 .event = evdev_event, 4 .match = evdev_match, /* Added by EETI*/ 5 .connect = evdev_connect, 6 .disconnect = evdev_disconnect, 7 .fops = &evdev_fops, 8 .minor = EVDEV_MINOR_BASE, //64 9 .name = "evdev", 10 .id_table = evdev_ids, 11 };
這里邊由 event , match , connect , fops , id_table 是不是覺得很多謎團好像都要在這里都要解開了
這里邊 , 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; 5 int minor; 6 int error; 7 8 /* 通用事件處理驅動的設備是通過evdev結構描述的,該事件的驅動最多能處理32個這>樣的設備,這些設備被存放在evdev_table數組中,下面的for循環將從evdev_table數組中找>到一個未使用的項 */ 9 for (minor = 0; minor < EVDEV_MINORS; minor++) 10 if (!evdev_table[minor]) 11 break; 12 13 if (minor == EVDEV_MINORS) { 14 pr_err("no more free evdev devices\n"); 15 return -ENFILE; 16 } 17 /* 下面的代碼為每個匹配的設備分配一個evdev結構體,並對成員進行初始化 */ 18 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); 19 if (!evdev) 20 return -ENOMEM; 21 22 INIT_LIST_HEAD(&evdev->client_list); 23 spin_lock_init(&evdev->client_lock); 24 mutex_init(&evdev->mutex); 25 init_waitqueue_head(&evdev->wait); 26 27 dev_set_name(&evdev->dev, "event%d", minor); 28 evdev->exist = true; 29 evdev->minor = minor; 30 31 evdev->handle.dev = input_get_device(dev); 32 evdev->handle.name = dev_name(&evdev->dev); 33 evdev->handle.handler = handler; 34 evdev->handle.private = evdev; 35 36 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); 37 evdev->dev.class = &input_class; 38 evdev->dev.parent = &dev->dev; 39 evdev->dev.release = evdev_free; 40 device_initialize(&evdev->dev); 41 /* input_register() 將&evdev->handle分別掛在對應的設備和事件處理驅動的句柄列 42 表中 */ 43 error = input_register_handle(&evdev->handle); 44 if (error) 45 goto err_free_evdev; 46 /* evdev_install_chrdev()會將evdev放入evdev_table數組的evdev->minor上*/ 47 error = evdev_install_chrdev(evdev); 48 if (error) 49 goto err_unregister_handle; 50 /* 由於指定了設備號和設備名稱,如果用戶空間配置了mdev或udev,dev_add() 的調用>將導致/dev 目錄下產生對應的設備文件eventX , 其中 X = 0,1,2... */ 51 error = device_add(&evdev->dev); 52 if (error) 53 goto err_cleanup_evdev; 54 55 return 0; 56 57 err_cleanup_evdev: 58 evdev_cleanup(evdev); 59 err_unregister_handle: 60 input_unregister_handle(&evdev->handle); 61 err_free_evdev: 62 put_device(&evdev->dev); 63 return error; 64 }
上面代碼 給我們上層的用戶提供了一個操作接口 , 它是在/dev/event* 這些都是通用的事件處理給上層用戶提供的文件接口 ,我們可以根據實際情況對這些文件 進行文件操作
其實文件操作接口也在這里:
1 static const struct file_operations evdev_fops = { 2 .owner = THIS_MODULE, 3 .read = evdev_read, 4 .write = evdev_write, 5 .poll = evdev_poll, 6 .open = evdev_open, 7 .release = evdev_release, 8 .unlocked_ioctl = evdev_ioctl, 9 #ifdef CONFIG_COMPAT 10 .compat_ioctl = evdev_ioctl_compat, 11 #endif 12 .fasync = evdev_fasync, 13 .flush = evdev_flush, 14 .llseek = no_llseek, 15 };
學內核的人是不是頓時覺得很熟悉, 沒錯, 真的很熟悉
我們以read函數為例:
1 static ssize_t evdev_read(struct file *file, char __user *buffer, 2 size_t count, loff_t *ppos) 3 { 4 struct evdev_client *client = file->private_data; 5 struct evdev *evdev = client->evdev; 6 struct input_event event; 7 int retval = 0; 8 /* 用戶必須請求數據不小於input_event結構的大小 */ 9 if (count < input_event_size()) 10 return -EINVAL; 11 /* 事件環形緩沖區為空, evdev存在且文件以非阻塞方式打開,則返回-EAGAIN*/ 12 if (!(file->f_flags & O_NONBLOCK)) { 13 retval = wait_event_interruptible(evdev->wait, 14 client->packet_head != client->tail || !evdev->exist); 15 if (retval) 16 return retval; 17 } 18 19 if (!evdev->exist) 20 return -ENODEV; 21 /* retval 記錄了讀取的事件總大小 */ 22 while (retval + input_event_size() <= count && 23 evdev_fetch_next_event(client, &event)) { 24 25 if (input_event_to_user(buffer + retval, &event)) 26 return -EFAULT; 27 28 retval += input_event_size(); 29 } 30 31 if (retval == 0 && file->f_flags & O_NONBLOCK) 32 retval = -EAGAIN; 33 return retval; 34 }
event 是為了處理從底層過來的數據 , 在通用事件處理里邊是有如下代碼:
1 static void evdev_event(struct input_handle *handle, 2 unsigned int type, unsigned int code, int value) 3 { 4 struct evdev *evdev = handle->private; 5 struct evdev_client *client; 6 struct input_event event; 7 struct timespec ts; 8 9 ktime_get_ts(&ts); 10 event.time.tv_sec = ts.tv_sec; 11 event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; 12 event.type = type; 13 event.code = code; 14 event.value = value; 15 16 rcu_read_lock(); 17 18 client = rcu_dereference(evdev->grab); 19 if (client) 20 evdev_pass_event(client, &event); 21 else 22 list_for_each_entry_rcu(client, &evdev->client_list, node) 23 evdev_pass_event(client, &event); 24 25 rcu_read_unlock(); 26 27 if (type == EV_SYN && code == SYN_REPORT) 28 wake_up_interruptible(&evdev->wait); 29 }
其實主要事情就是給struct input_event 結構體賦值:
struct input_event { struct timeval time; /* 事件發生的時間 */ __u16 type; /* 事件類型 */ __u16 code; /* 事件碼 */ __s32 value; /* 事件值 */ };
回到通用事件處理初始化;
1 static int __init evdev_init(void) 2 { 3 return input_register_handler(&evdev_handler); 4 }
它調用的是input_register_handler函數
這個函數也是drivers/input/input.c 函數里邊
1 int input_register_handler(struct input_handler *handler) 2 { 3 struct input_dev *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); 11 12 if (handler->fops != NULL) { 13 if (input_table[handler->minor >> 5]) { 14 retval = -EBUSY; 15 goto out; 16 } 17 input_table[handler->minor >> 5] = handler; 18 } 19 20 list_add_tail(&handler->node, &input_handler_list); 21 22 list_for_each_entry(dev, &input_dev_list, node) 23 input_attach_handler(dev, handler); 24 25 input_wakeup_procfs_readers(); 26 27 out: 28 mutex_unlock(&input_mutex); 29 return retval; 30 } 31 EXPORT_SYMBOL(input_register_handler); 32
這里邊他也是調用了input_attach_handler(dev , handler) 函數
這里邊也是一個依附配對的過程, 時一個一對多的一個配對, 一個設備配對多個事件上報處理
剛才也把第三個問題, 給上層提供一個怎么樣的接口的問題一筆帶過了
到這里 , 就剩下一些細節上的東西還沒有完善
其實inpu 子系統 從底層往上層的一個流程時這樣的
1. 底層驅動硬件初始化 , 並指定要上報的事件的類型 以及值
2. 通過input.c 配對
3 . 配對到相應的事件上報處理層 , 然后給上層發布接口
其實這樣的一個input 模型有幾個好處
1. 寫驅動相對沒有那么麻煩 , 只要硬件初始化 , 指定要上報的事件的類型以及數值
2 . 應用層操作的接口比較統一 , 上層的人不用那么辛苦去看各種各樣的操作接口 , 可能有幾個ioctl可能要看一下.