1. 程式人生 > 其它 >輸入子系統驅動

輸入子系統驅動

1.輸入子系統的驅動的框架結構體

​ 1.1核心層 :提供時間處理層和裝置驅動層的註冊、登出,匹配過程
​ 1.2事件處理層 :嚮應用層提供操作的介面如mouse.c evdev.c
​ 1.3裝置驅動層 :讀取事件,上報事件

2.輸入子系統的目錄結構體

input.c :核心層
evdev.c mousedev.c :事件處理層
gpio_keys.c gt818.c:裝置驅動層

3.input子系統的註冊和登出函式

​ 事件處理層

 	int input_register_handler(struct input_handler *handler) 
	void input_unregister_handler(struct input_handler *handler) 

​ 裝置驅動層

 	int input_register_device(struct input_dev *dev)
	void input_unregister_device(struct input_dev *dev) 

4.事件處理層註冊handler的過程 我們以linux-3.4/drivers/input/evdev.c為例

從上面的程式碼我們能夠分析看出來,這裡就是定義了一個input_handler結構體
將結構體的成員填充完成之後,通過input_register_handler函式註冊到核心中
接下來我們分析下注冊handler的過程。


input_register_handler(&evdev_handler);
	input_table[handler->minor >> 5] = handler; //將上面定義的結構體,填充到input_table陣列中
	list_add_tail(&handler->node, &input_handler_list); //將註冊的該節點放在input_handler_list這條連結串列中
		list_for_each_entry(dev, &input_dev_list, node)     //遍歷input_dev_list這條連結串列
			input_attach_handler(dev, handler);             //那handler在input_dev_list這條連結串列上進行匹配
				id = input_match_device(handler, dev);      //開始匹配
					for (id = handler->id_table; id->flags || id->driver_info; id++) {
						 if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
							  if (id->bustype != dev->id.bustype)
						     id->flags  //在前面沒有定義,它可以是匯流排型別,廠商,id等成員
							MATCH_BIT(evbit,  EV_MAX);                                                                                          
							MATCH_BIT(keybit, KEY_MAX);
							MATCH_BIT(relbit, REL_MAX);
							MATCH_BIT(absbit, ABS_MAX);

							#define MATCH_BIT(bit, max) \                                                                                               
								for (i = 0; i < BITS_TO_LONGS(max); i++) \
									if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
										break; \
									if (i != BITS_TO_LONGS(max)) \  
										continue;     //這裡的continue是作用到外層迴圈的
								//如果evbit,keybit,relbit等有一項不相等就接著匹配下一個input_dev,
								//如果上面的都匹配成功,就會執行handler中的match函式
								if (!handler->match || handler->match(handler, dev))
								//在evdev中並沒有對handler結構體中的match成員初始化,取反之後為真
								return id;返回匹配到的id成員。

					error = handler->connect(handler, dev, id);
					.connect    = evdev_connect,  
						struct evdev *evdev;
						evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
						dev_set_name(&evdev->dev, "event%d", minor);
						evdev->handle.dev = input_get_device(dev);  
						evdev->handle.handler = handler;          //將匹配到的dev和handler放到handle結構體中
						error = input_register_handle(&evdev->handle);
							list_add_tail_rcu(&handle->d_node, &dev->h_list);  //將d_node新增到dev的h_list中
							list_add_tail_rcu(&handle->h_node, &handler->h_list);//將h_node新增到handler的h_list中
						//這裡我們需要回顧一下input_handler和input_dev結構體關於連結串列的成員
							struct input_handler {
								struct list_head    h_list;
								struct list_head    node;
								...
							};  

							struct input_dev {
								struct list_head    h_list;
								struct list_head    node;
								...
							};

​ 我們可以看到這兩個結構體中都有h_list和node這兩條連結串列,這兩條連結串列都是儲存的什麼成員呢?
​ input_handler結構體的node成員會放入input_handler_list連結串列。input_dev結構體的node成員會放入input_dev_list連結串列。也就是 說node是為了儲存自己的。而匹配到之後首先會構建input_handle結構體,然後將這個結構體分別放在兩個結構體體的h_list的連結串列 中。

5.裝置驅動層註冊過程

​ 我們以fspad-733/lichee/linux-3.4/drivers/input/keyboard/gpio_keys.c為例子
​ 從這個驅動的probe函式開始分析gpio_keys_probe

struct input_dev *input;
		input = input_allocate_device();   //為input結構體分配空間,我們可以看下它的實現
			struct input_dev *input_allocate_device(void)
			{
				struct input_dev *dev;
				

​			dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); 
​			if (dev) {
​				dev->dev.type = &input_dev_type;   //建立裝置屬性檔案
​				dev->dev.class = &input_class;     //填充匯流排型別為input                                                               
​				device_initialize(&dev->dev);      //初始device結構體,包括kobject的資訊
​				INIT_LIST_HEAD(&dev->h_list);      //初始化前面分析的兩條連結串列
​				INIT_LIST_HEAD(&dev->node);

​	input->open = gpio_keys_open;    //填充open函式
​	input->close = gpio_keys_close;  //填充close函式
​	input->id.bustype = BUS_HOST;    //初始化匯流排型別,廠商,id,版本號
​	input->id.vendor = 0x0001;
​	input->id.product = 0x0001;
​	input->id.version = 0x0100;
​	#define EV_REP          0x14     0001 010  0   --->20
​	 __set_bit(EV_REP, input->evbit);

​			static inline void __set_bit(int nr, volatile unsigned long *addr)
​			{                                                                                                                            
​				unsigned long mask = BIT_MASK(nr);
​				unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
​				
​				*p  |= mask;
​			}   

​		#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG))
​		#define BIT_WORD(nr)        ((nr) / BITS_PER_LONG)    

​		((unsigned long *)addr) +  ((nr) / 32) |= (1UL << ((nr) % 32))
​	
​	通過__set_bit函式,來設定input->evbit變數的第20位為1,EV_REP命令碼代表重複。
​	當然還可以設定為下面的命令碼。evbit這一位代表設定不同的事件型別
​		EV_SYN 0x00 同步事件
​		EV_KEY 0x01 按鍵事件,如KEY_VOLUMEDOWN
​		EV_REL 0x02 相對座標, 如滑鼠上報的座標
​		EV_ABS 0x03 絕對座標,如觸控式螢幕上報的座標
​		EV_MSC 0x04 其它
​		EV_LED 0x11 LED
​		EV_SND 0x12 聲音
​		EV_REP 0x14 Repeat
​		EV_FF 0x15 力反饋 

​	 error = input_register_device(input);   //將input註冊到系統中
​		 list_add_tail(&dev->node, &input_dev_list);    //將input_dev節點新增到input_dev_list連結串列中
​		 list_for_each_entry(handler, &input_handler_list, node) //在input_handler_list連結串列中查詢
​			 input_attach_handler(dev, handler);  //完成匹配過程,此過程以及分析過了,這裡就不在分析了

6.set_bit和__set_bit的關係

 #define set_bit(nr,p)           ATOMIC_BITOP(set_bit,nr,p)    
 #define ATOMIC_BITOP(name,nr,p)     _##name(nr,p)   
__set_bit(nr,p)

7.輸入子系統驅動如何編寫

​ 1.定義結構體

struct input_dev  *input;

​ 2.分配空間

input = input_allocate_device(); 

​ 3.結構體成員的填充
​ 3.1什麼型別的事件

set_bit(EV_KEY,fs_input->evbit);

​ 3.2上報什麼值

set_bit(KEY_ENTER,fs_input->keybit);
			set_bit(KEY_L,fs_input->keybit);

​ 4.結構體的註冊和登出

input_register_device(input);
input_unregister_device(input);

8.如何上報事件

​ 事件會通過input_event函式進行資料的上報。

input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
         input_handle_event(dev, type, code, value);
			switch (type) {
				case EV_SYN:
				case EV_KEY:      //對disposition變數賦值
				...
			if (disposition & INPUT_PASS_TO_HANDLERS)
				input_pass_event(dev, type, code, value);     
					handle->handler->event(handle, type, code, value);    
						.event      = evdev_event,
							struct input_event event;
							 struct input_event {                                                                                                        
								 struct timeval time;    //時間
								 __u16 type;             //事件型別
								 __u16 code;             //編號
								 __s32 value;            //值
							 };
							 event.type = type;
							 event.code = code;
							 event.value = value;
								evdev_pass_event(client, &event, time_mono, time_real);
									client->buffer[client->head++] = *event;   //將要上報的input_event
									client->head &= client->bufsize - 1;       //放入緩衝區
									if (unlikely(client->head == client->tail)) {   //如果緩衝區存放滿之後,將最後一個設定為同步訊號
										client->tail = (client->head - 2) & (client->bufsize - 1);
										client->buffer[client->tail].time = event->time;
										client->buffer[client->tail].type = EV_SYN;
										client->buffer[client->tail].code = SYN_DROPPED;
										client->buffer[client->tail].value = 0;                                                                             
										client->packet_head = client->tail;
									}
							 if (event->type == EV_SYN && event->code == SYN_REPORT) {
								 client->packet_head = client->head;                                                                                
								 kill_fasync(&client->fasync, SIGIO, POLL_IN);
							 }
							//從上述的程式碼中,我們可以看到,EV_SYN通過訊號的發出可以有兩種情況,
							第一,input_client中的緩衝區存滿,第二使用者發出了EV_SYN同步訊號。當
							同步訊號發出時,驅動中就會發送kill_fasync訊號,告訴應用程式有資料要
							上報了。kill_fasync和fasync_helper實現非同步通知。
							 if (type == EV_SYN && code == SYN_REPORT)
							     wake_up_interruptible(&evdev->wait);  //喚醒等待佇列

​ 此時應用程式可以通過三種方式來讀取,第一種就是在應用程式中註冊signal訊號處理函式,在訊號處理函式中讀取資料,第二種就是直接呼叫read函式讀資料就行了。第三種就是使用select,poll,epoll函式來讀取資料。開啟的裝置節點為/dev/input/event%d

​ 當然事件的上報還可以是input_report_key,input_report_rel,input_report_abs,input_sync等,你會發現這些函式最終都是呼叫input_event函式來實現的。只是暫時指定了他們的事件型別