keyboard-input-platform2:input輸入子系統
網上關於input輸入子系統的文章,非常多,先盜兩張圖,就對它的分層結構大致瞭解:
Input子系統分為三層,從下至上分別是裝置驅動層,輸入核心層以及事件處理層,即input device Driver -> InputCore -> Eventhandler -> userspace。輸入裝置主要的工作過程都是 動作產生(按鍵,觸屏……)-->產生中斷-->讀取數值(鍵值,座標……)-->將數值傳遞給應用程式。一個大致的工作流程就是,input device向上層報告-->input core接收報告,並根據在註冊input device時建立好的連線選擇哪一類handler來處理事件-->通過handler將資料存放在相應的dev(evdev,mousedev…)例項的緩衝區中,等待應用程式來讀取。這三層中的輸入核心層和事件處理層都是核心已經完成了的,因此需要我們完成的只有裝置驅動層(見上篇文章,即是這樣處理)。但核心完成的東西,我們也需要簡單分析一下,先直接上程式碼吧:
開始分析gpio_keys_probe函數了,這個感覺有點難。late_initcall(gpio_keys_init); //驅動程式(也許是所有程式碼)當然從init函式開始,late_initcall 類似module_init,在include/linux/init.h有定義, //initcall.init區段又分成7個子區段不同的區段,呼叫的順序不一樣,數字越小的優先順序越高 static int __init gpio_keys_init(void) { return platform_driver_register(&gpio_keys_device_driver);//註冊平臺匯流排驅動 } static struct platform_driver gpio_keys_device_driver = { .probe =<span style="color:#FF0000;"> gpio_keys_probe</span>,//探測函式,需要重點關注 .remove = __devexit_p(gpio_keys_remove), .driver = { .name = "gpio-keys", .owner = THIS_MODULE, .pm = &gpio_keys_pm_ops, .of_match_table = gpio_keys_of_match,//匹配函式 } };
probe()函式最核心的功能是在一個迴圈中完成了各個按鍵的註冊,也就是根據nbuttons遍歷gpio_keys_platform_data結構,對其中的每個按鍵進行事件的註冊處理。static int __devinit gpio_keys_probe(struct platform_device *pdev) ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +pdata->nbuttons * sizeof(struct gpio_button_data),GFP_KERNEL); //kzalloc其實等於kmalloc+memset。類似於malloc,kmalloc對應於核心空間的記憶體分配函式。 //第二行程式碼的意思:在核心空間開闢一段大小為sizeof(struct input_dev)+**大小的記憶體區,並把它初始化為0。 //GFP_KERNEL為一個常規的記憶體分配標誌。類似的還有GFP_DMA,表示分配的記憶體能供dma使用。GFP_ATOMIC分配記憶體時,不允許睡眠,如用於中斷。 //分配到記憶體後,用ddata指向這段記憶體 input = input_allocate_device();//申請註冊一個input_dev,再對其作些簡單的初始化,返回的是1個input_dev的結構體,此結構體用於表徵1個輸入裝置 platform_set_drvdata(pdev, ddata);//用於儲存區域性變數,將chip儲存成平臺匯流排裝置的私有資料,對應的用platform_get_drvdata來獲取data __set_bit(EV_REP, input->evbit);//設定裝置所產生的事件以及上報的按鍵值 error = <span style="color:#FF6666;">gpio_keys_setup_key</span>(pdev, input, bdata, button); //通過一個for迴圈,呼叫gpio_keys_setup_key和input_set_capability,設定列出的三個IO口的中斷函式以及按鍵功能,探測函式的關鍵點,接下來繼續分析。 //該函式使用了帶定時器延時的中斷機制,用於按鍵去抖動。即中斷的執行在定時器到來之後執行,這樣能夠有效的去除按鍵抖動。同時,中斷使用工作佇列的機制。 error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); //建立了基於sys系統的檔案屬性組,具體可以在sys/devices/platform/gpio-keys目錄下找到gpio_keys_attr_group結構體中attrs組對應的gpio_keys_attrs結構體中的幾個屬性檔案 error = input_register_device(input);//向輸入子系統註冊input_dev,結束探測函式初始化 gpio_keys_gpio_report_event(bdata);//根據按鍵報告事件 input_sync(input);//用於事件同步,告知事件的接收者,驅動已經發出一個完整的報告。 device_init_wakeup(&pdev->dev, wakeup);//設定是否喚醒
再來關注其中gpio_keys_setup_key這個比較重要的函式:
static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
const char *desc = button->desc ? button->desc : "gpio_keys";//取出按鍵描述符
error = gpio_request(button->gpio, desc);//請求使用GPIO
error = gpio_direction_input(button->gpio);//設定指定的GPIO為輸入模式
error = gpio_set_debounce(button->gpio,button->debounce_interval * 1000);
if (button->debounce_interval) { //去抖動函式
error = gpio_set_debounce(button->gpio,button->debounce_interval * 1000);
/* use timer if gpiolib doesn't provide debounce */
if (error < 0)
bdata->timer_debounce =
button->debounce_interval;
}
irq = gpio_to_irq(button->gpio);//獲得GPIO對應的中斷號
INIT_WORK(&bdata->work, <span style="color:#FF6666;">gpio_keys_gpio_work_func</span>);//初始化工作列隊
setup_timer(&bdata->timer,gpio_keys_gpio_timer, (unsigned long)bdata);//傳入引數: 過期時間,回撥函式,上下文.當計時器過期時,回撥函式gpio_keys_timer將得到執行
//該函式使用了帶定時器延時的中斷機制,用於按鍵去抖動。即中斷的執行在定時器到來之後執行,這樣能夠有效的去除按鍵抖動。同時,中斷使用工作佇列的機制。
isr = <span style="color:#FF0000;">gpio_keys_gpio_isr</span>;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;//註冊中斷函式,並設定標記
input_set_capability(input, button->type ?: EV_KEY, button->code);//設定該input dev的能力記錄本裝置對那些事件感興趣
上面紅色字型的函式如下:static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
if (bdata->timer_debounce)//如果有去抖間隔則修改定時器
mod_timer(&bdata->timer,jiffies + msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);//如果沒有,排程其中的佇列,直接執行work
static void gpio_keys_gpio_work_func(struct work_struct *work)
gpio_keys_gpio_report_event(bdata);//用input子系統,向上層報事件
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
type = button->type ?: EV_KEY;
state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low;
input_event(input, type, button->code, !!state);//報告事件
input_sync(input);//同步事件
首先,上面函式中setup_timer函式初始化定時器,第二個實參為一個名為gpio_keys_timer的函式,一旦計時器過期,該函式將得到執行。setup_timer函式需和mod_timer函式配合使用,在按鍵中斷的頂半部,即gpio_keys_isr函式,會判斷debounce_interval是否為0,
若為非0,則呼叫mod_timer函式延時debounce_interval ms,再觸發定時器中斷,即gpio_keys_timer函式得到執行。否則,直接排程工作佇列,執行中斷底半部。
回到gpio_keys_setup_key函式,在初始化完計時器後,再呼叫INIT_WORK函式初始化工作佇列,初始化函式有一個實參函式gpio_keys_work_func,即一旦排程相應佇列名,該函式將得到執行
有其他任務了,輸入子系統就暫時不研究了,以後再來補充。
相關文件以後再分析了:
http://www.linuxidc.com/Linux/2011-09/43766p2.htm
http://blog.chinaunix.net/uid-20937170-id-3052290.html
http://blog.csdn.net/shui1025701856/article/details/7577858
http://blog.csdn.net/ylyuanlu/article/details/6704744
http://blog.csdn.net/wangpengqi/article/details/8471144
http://blog.chinaunix.net/uid-25909619-id-3372031.html
http://blog.csdn.net/lbmygf/article/details/7360084
關於Kobjiect:
http://bbs.csdn.net/topics/370156324
http://www.cnblogs.com/image-eye/archive/2012/01/29/2330838.html
fcntl:http://www.cnblogs.com/andtt/articles/2178875.html