對輸入子系統分析總結

 原文鏈接   http://100ask.net/forum/showtopic-3567.aspx

在drivers/input/input.c中:   

 進入模塊入口函數input_init :    
對輸入子系統分析總結

  1.     err= register_chrdev(INPUT_MAJOR, "input",&input_fops);
複製代碼
    而input_fops只有open和llseek函數:    
  1.  static const struct file_operations input_fops ={
  2.     .owner= THIS_MODULE,
  3.     .open= input_open_file,
  4.     .llseek= noop_llseek,
  5.     };
複製代碼
     那麼沒有read函數的話該怎麼讀數據呢?    進入 input_open_file函數:        
  1.   struct input_handler*handler;//定義一個input_handler指針
  2.         handler= input_table[iminor(inode) >>5];//根據傳入文件的次設備號從
  3.         //input_table中提取出一個input_handler
  4.         if(handler)
  5.         new_fops=fops_get(handler->fops);//從input_handler中提取出new_fops
  6.         file->f_op= new_fops;//將new_fops賦值給當前文件的file_operations
  7.         err= new_fops->open(inode, file);
複製代碼
   經過這些操作後,當app再來調用read,write,open等函數時,最終都會調用到file->f_op 
中的read,write,open等函數。
  那麼input_open_file函數中的input_table 又是從何而來的呢?    
搜索代碼可知,    
在input.c的input_register_handler函數中構造了input_table:       
  1.   if(handler->fops != NULL) {
  2.             if(input_table[handler->minor>> 5]) {
  3.                 retval= -EBUSY;
  4.                 gotoout;
  5.             }
  6.             //給input_table中的第handler->minor >>5項賦值
  7.             input_table[handler->minor>> 5] = handler;
  8.         }
複製代碼
     又是誰在調用input_register_handler呢?    搜索內核可知:    evdev.c,tsdev.c,joydev.c,
keyboard.c,mousedev.c等文件調用了    input_register_handler,我們以evdev.c爲例 
    在drivers/input/evdev.c中:
     進入模塊入口函數evdev_init:    
  1.      returninput_register_handler(&evdev_handler);
  2.     evdev_handler的定義如下:
  3.         staticstruct input_handler evdev_handler = {
  4.         .event        =evdev_event,
  5.         .connect    =evdev_connect,
  6.         .disconnect    =evdev_disconnect,
  7.         .fops        =&evdev_fops,//前面所用到的 new_fops指的就是這裏定義的的fops
  8.         .minor        =EVDEV_MINOR_BASE,
  9.         .name        ="evdev",
  10.         .id_table    =evdev_ids,
  11.         };
複製代碼
     這裏需要插入的講解一下輸入子系統的架構!如圖:    通過input_register_device註冊一個
input_dev設備或者通過  input_register_handler註冊一個input_handler時,input_dev與
input_handler    會進行匹配,如何匹配?   
input_handler中有一個id_table,裏面包含的就是這個input_handler能處理的input_dev,
當input_handler與input_dev匹配成功的話,則會調用    input_handler裏    的connect函數。  
     
 下面我們來看下input_register_device和input_register_handler分別做了什麼:  
  1.    input_register_device
  2.         //將剛註冊的input_dev放入input_dev_list鏈表(1)
  3.         list_add_tail(&dev->node,&input_dev_list);
  4.         
  5.             
  6.         list_for_each_entry(handler,&input_handler_list,node)
  7.         input_attach_handler(dev,handler);
  8.         
  9.     input_register_handler
  10.         //將剛註冊的input_handler放入input_table中(1)
  11.         input_table[handler->minor>> 5] = handler;
  12.     
  13.         //將剛註冊的input_handler放入input_handler_list鏈表中 (2)
  14.         list_add_tail(&handler->node,&input_handler_list);

  15.         
  16.         list_for_each_entry(dev,&input_dev_list,node)
  17.         input_attach_handler(dev,handler);
複製代碼
     由此可以看出,無論是先註冊input_handler還是先註冊input_dev最終都會調用來
input_attach_handler(dev,handler);
來進行兩兩匹配。現在我們來看看input_attach_handler是如何將input_handler和input_dev進行
匹配的。    
在input_attach_handler函數中:       
id = input_match_device(handler, dev);          
        
 error = handler->connect(handler,dev, id);        
我們以evdev.c中的 input_handler結構中的connect函數爲例,分析connect函數做了些什麼。    
evdev_connect函數中:
  1.  
  2.         
  3.         evdev= kzalloc(sizeof(struct evdev), GFP_KERNEL);
  4.         
  5.         
  6.         evdev->handle.dev= input_get_device(dev);
  7.         evdev->handle.name= dev_name(&evdev->dev);
  8.         evdev->handle.handler= handler;
  9.         evdev->handle.private= evdev;
  10.       
  11.         
  12.         error=input_register_handle(&evdev->handle);
  13.      
複製代碼
            下面我們來看註冊input_handle做了些什麼:       
  1.       
  2.             
  3.        list_add_tail_rcu(&handle->d_node,&dev->h_list);
  4.             
  5.             
  6.             list_add_tail_rcu(&handle->h_node,&handler->h_list);
複製代碼
     對於輸入設備,我們知道一般需要讀取其數據,那麼經過上述的一些列動作後又是如何讀取到數據的呢?
我們來看下evdev.c中的input_handler中的fops中的read函數:   
 在evdev_read函數中:       
 //如果沒有數據且nonblock的話,則EAGAIN
  1.        if( client->head == client->tail&& evdev->exist&&
  2.         (file->f_flags & O_NONBLOCK))
  3.         return-EAGAIN;
  4.         //否則,睡眠
  5.         retval=wait_event_interruptible(evdev->wait,client->head!= client->tail ||!        evdev->exist);
複製代碼
     既然有睡眠,那麼何時被喚醒,搜索代碼。    
evdev_event函數中:       
 //喚醒  wake_up_interruptible(&evdev->wait);    
evdev_event是input_handler中的成員,當有數據可讀
(如觸摸屏被按下,按鍵    被按下)時,event函數會被調用。
而event函數是怎麼被調用到的?這就得看設備層了,
設備層的驅動做了如下工作:   
<1>在中斷函數中確定判斷髮生了什麼事件,並且相應的值是多少   
<2>通過input_event()函數上報事件    //input.c  input_report_key會調用 input_event
<3>通過input_sync()函數表明上報結束   

分析input_event函數我們就可以知道input_handler中的event函數是如何被調用到的了。   
input_event中,調用了input_handle_event函數,在input_handle_event函數中調用了
input_pass_event函數; 
   
在input_pass_event函數中:
input_report_key ——> input_event——> input_handle_event ——> input_pass_event ——>
handler->event


  1.  
  2.         structinput_handler *handler;
  3.         
  4.         list_for_each_entry_rcu(handle,&dev->h_list, d_node) {
  5.             if (!handle->open)
  6.                 continue;
  7.             //如果該input_handle被打開,則該input_handle->input_handler即爲可            //處理該input_dev的handler
  8.             handler= handle->handler;
  9.             if(!handler->filter) {
  10.                 if(filtered)
  11.                     break;
  12.                 
  13.                 handler->event(handle,type, code, value);
  14.             }else if (handler->filter(handle, type, code,value))
  15.                 filtered= true;
  16.         }
複製代碼
 最後,迴歸到如何寫符合輸入子系統框架的驅動程序,四個步驟:
 <1> 分配一個input_dev結構體
 <2> 設置input_dev
 <3>    註冊input_dev        
 <4>  在中斷函數中上報事件   
     
發佈了6 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章