1. 程式人生 > >keyboard-input-platform2:input輸入子系統

keyboard-input-platform2:input輸入子系統

網上關於input輸入子系統的文章,非常多,先盜兩張圖,就對它的分層結構大致瞭解:

Input子系統分為三層,從下至上分別是裝置驅動層,輸入核心層以及事件處理層,即input device Driver -> InputCore -> Eventhandler -> userspace。輸入裝置主要的工作過程都是 動作產生(按鍵,觸屏……)-->產生中斷-->讀取數值(鍵值,座標……)-->將數值傳遞給應用程式。一個大致的工作流程就是,input device向上層報告-->input core接收報告,並根據在註冊input device時建立好的連線選擇哪一類handler來處理事件-->通過handler將資料存放在相應的dev(evdev,mousedev…)例項的緩衝區中,等待應用程式來讀取。這三層中的輸入核心層和事件處理層都是核心已經完成了的,因此需要我們完成的只有裝置驅動層(見上篇文章,即是這樣處理)。但核心完成的東西,我們也需要簡單分析一下,先直接上程式碼吧:

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,//匹配函式
	}
};
開始分析gpio_keys_probe函數了,這個感覺有點難。
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);//設定是否喚醒
probe()函式最核心的功能是在一個迴圈中完成了各個按鍵的註冊,也就是根據nbuttons遍歷gpio_keys_platform_data結構,對其中的每個按鍵進行事件的註冊處理。
再來關注其中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