原文鏈接 http://100ask.net/forum/showtopic-3567.aspx
在drivers/input/input.c中:
進入模塊入口函數input_init :
-
err= register_chrdev(INPUT_MAJOR, "input",&input_fops);
複製代碼
而input_fops只有open和llseek函數:
-
static const struct file_operations input_fops ={
-
.owner= THIS_MODULE,
-
.open= input_open_file,
-
.llseek= noop_llseek,
-
};
複製代碼
那麼沒有read函數的話該怎麼讀數據呢? 進入
input_open_file函數:
-
struct input_handler*handler;//定義一個input_handler指針
-
handler= input_table[iminor(inode) >>5];//根據傳入文件的次設備號從
-
//input_table中提取出一個input_handler
-
if(handler)
-
new_fops=fops_get(handler->fops);//從input_handler中提取出new_fops
-
file->f_op= new_fops;//將new_fops賦值給當前文件的file_operations
-
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:
-
if(handler->fops != NULL) {
-
if(input_table[handler->minor>> 5]) {
-
retval= -EBUSY;
-
gotoout;
-
}
-
//給input_table中的第handler->minor >>5項賦值
-
input_table[handler->minor>> 5] = handler;
-
}
複製代碼
又是誰在調用input_register_handler呢? 搜索內核可知:
evdev.c,tsdev.c,joydev.c,
keyboard.c,mousedev.c等文件調用了 input_register_handler,我們以evdev.c爲例
在drivers/input/evdev.c中:
進入模塊入口函數evdev_init:
-
returninput_register_handler(&evdev_handler);
-
evdev_handler的定義如下:
-
staticstruct input_handler evdev_handler = {
-
.event =evdev_event,
-
.connect =evdev_connect,
-
.disconnect =evdev_disconnect,
-
.fops =&evdev_fops,//前面所用到的 new_fops指的就是這裏定義的的fops
-
.minor =EVDEV_MINOR_BASE,
-
.name ="evdev",
-
.id_table =evdev_ids,
-
};
複製代碼
這裏需要插入的講解一下輸入子系統的架構!如圖:
當通過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分別做了什麼:
-
input_register_device:
-
//將剛註冊的input_dev放入input_dev_list鏈表(1)
-
list_add_tail(&dev->node,&input_dev_list);
-
-
-
list_for_each_entry(handler,&input_handler_list,node)
-
input_attach_handler(dev,handler);
-
-
input_register_handler:
-
//將剛註冊的input_handler放入input_table中(1)
-
input_table[handler->minor>> 5] = handler;
-
-
//將剛註冊的input_handler放入input_handler_list鏈表中 (2)
-
list_add_tail(&handler->node,&input_handler_list);
-
-
-
list_for_each_entry(dev,&input_dev_list,node)
-
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函數中:
-
-
-
evdev= kzalloc(sizeof(struct evdev), GFP_KERNEL);
-
-
-
evdev->handle.dev= input_get_device(dev);
-
evdev->handle.name= dev_name(&evdev->dev);
-
evdev->handle.handler= handler;
-
evdev->handle.private= evdev;
-
-
-
error=input_register_handle(&evdev->handle);
-
複製代碼
下面我們來看註冊
input_handle做了些什麼:
-
-
-
list_add_tail_rcu(&handle->d_node,&dev->h_list);
-
-
-
list_add_tail_rcu(&handle->h_node,&handler->h_list);
複製代碼
對於輸入設備,我們知道一般需要讀取其數據,那麼經過上述的一些列動作後又是如何讀取到數據的呢?
我們來看下evdev.c中的input_handler中的fops中的read函數:
在evdev_read函數中:
//如果沒有數據且nonblock的話,則EAGAIN
-
if( client->head == client->tail&& evdev->exist&&
-
(file->f_flags & O_NONBLOCK))
-
return-EAGAIN;
-
//否則,睡眠
-
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
-
-
structinput_handler *handler;
-
-
list_for_each_entry_rcu(handle,&dev->h_list, d_node) {
-
if (!handle->open)
-
continue;
-
//如果該input_handle被打開,則該input_handle->input_handler即爲可 //處理該input_dev的handler
-
handler= handle->handler;
-
if(!handler->filter) {
-
if(filtered)
-
break;
-
-
handler->event(handle,type, code, value);
-
}else if (handler->filter(handle, type, code,value))
-
filtered= true;
-
}
複製代碼
最後,迴歸到如何寫符合輸入子系統框架的驅動程序,四個步驟:
<1> 分配一個input_dev結構體
<2> 設置input_dev
<3> 註冊input_dev
<4> 在中斷函數中上報事件