android 4.4 按鍵分析四--鍵盤新增
這部分簡單介紹Keypad的基本知識。(圖片上傳不了,後續再補)
對於輸入裝置,
一般支援的API功能如下,
分配/釋放一個輸入裝置:
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);
註冊/登出輸入裝置:
int __must_check input_register_device(struct input_dev *);
void input_unregister_device(struct input_dev *);
報告輸入事件:
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);/* 報告指定type、code的輸入事件 */
void input_report_key(struct input_dev *dev, unsigned int code, int value);/* 報告鍵值 */
void input_report_rel(struct input_dev *dev, unsigned int code, int value);/* 報告相對座標 */
void input_report_abs(struct input_dev *dev, unsigned int code, int value);/* 報告絕對座標 */
void input_sync(struct input_dev *dev);/* 報告同步事件 */
Input驅動編寫步驟
1.分配一個輸入裝置;
2.註冊一個輸入裝置;
3.驅動支援什麼事件;
Set_bit告訴inout子系統它支援哪些事件
Set_bit(EV_KEY,button_dev.evbit)
Struct input_dev中有兩個成員,一個是evbit;一個是keybit.分別用來表示裝置所支援的事件型別和按鍵型別。
4.驅動事件報告;
5.釋放和登出裝置;
對於input子系統的編譯處理,在\kernel\drivers的makefile裡面我們可以看到,它會編譯對應的input目錄下的makefile,
對應鍵盤,在\kernel\drivers\input\keyboard目錄下,定義了各種鍵盤實現,部分例項如下,
系統如何選擇合適的驅動檔案,在本目錄下的makefile裡面我們可以看到,如果系統配置了對應的鍵盤,則編譯就新增對應檔案,進行支援。
對於手機系統來講,一般是支援GPIO鍵盤的,如MTK 6572平臺的一款設計,硬體原理圖設計如下,使用了系統的KROW0-KROW1,KCOL0-KCOL1,模擬出4個按鍵,提供諸如側鍵等物理按鍵(本來晶片是支援9個按鍵的,KROW2和KCOL2被JTAG借用了)。
所以我們接著分析CONFIG_KEYBOARD_GPIO是如何開啟來支援GPIO鍵盤的。在\kernel\arch\arm\configs目錄下,定義了各種ARM核的相關配置,可以看到GPIO是在這裡配置是否支援的。
至此,就知道了一個支援的鍵盤檔案是如何參與編譯的,並延伸的瞭解到,如果要新增加一個新的鍵盤檔案,我們該如何處理。
和linux通用的驅動處理一樣,GPIO的驅動編寫也有固定的格式,簡單介紹如下,
1)編寫基本的驅動函式:
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.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,
}
};
根據平臺編寫格式,給每個裝置要編寫類似上面的驅動節點,並完成節點裡的函式實現。
2)向平臺註冊:
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);
}
static void __exit gpio_keys_exit(void)
{
platform_driver_unregister(&gpio_keys_device_driver);
}
late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);
在驅動編寫中,一般在檔案最後寫上類似下面的程式碼,完成驅動模組的平臺註冊。
module_init(gpio_keys_init);
module_exit(gpio_keys_exit);
當模組載入(insmod)或核心引導過程中,gpio_keys_init函式會被呼叫。首先做的工作是獲取能夠正確控制硬體裝置的硬體資源(例如記憶體、IO記憶體、中斷和DMA);在系統退出時,gpio_keys_exit會被呼叫。
對於gpio_keys_probe,它是一個關鍵的函式,用來檢測按鍵,說明如下,
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
struct device *dev = &pdev->dev;
struct gpio_keys_platform_data alt_pdata;
struct input_dev *input;
int i, error;
int wakeup = 0;
if (!pdata) {
error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
if (error)
return error;
pdata = &alt_pdata;
}
ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
pdata->nbuttons * sizeof(struct gpio_button_data),
GFP_KERNEL);
input = input_allocate_device(); //申請一個input裝置
if (!ddata || !input) {
dev_err(dev, "failed to allocate state\n");
error = -ENOMEM;
goto fail1;
}
ddata->input = input;
ddata->n_buttons = pdata->nbuttons;
ddata->enable = pdata->enable;
ddata->disable = pdata->disable;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
input_set_drvdata(input, ddata);
input->name = pdata->name ? : pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->open = gpio_keys_open;
input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
//描述input裝置節點屬性,最後新增eventHup裡的節點列表,可以通過log瞭解到實際相關設定如下,表明這個GPIO鍵盤裝置的節點名字為/dev/input/event4,在節點列表中id為2,並使用的Keylayout和KeyChars設定如下
New device: id=2, fd=121, path='/dev/input/event4', name='mtk-tpd-kpd', classes=0x1, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, usingSuspendBlockIoctl=true, usingClockIoctl=true
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
const struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
error = gpio_keys_setup_key(pdev, input, bdata, button);
if (error)
goto fail2;
if (button->wakeup)
wakeup = 1;
}
error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
if (error) {
dev_err(dev, "Unable to export keys/switches, error: %d\n",
error);
goto fail2;
}
error = input_register_device(input);//設備註冊,並和對應的handler處理函式掛鉤
if (error) {
dev_err(dev, "Unable to register input device, error: %d\n",
error);
goto fail3;
}
/* get current state of buttons that are connected to GPIOs */
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
if (gpio_is_valid(bdata->button->gpio))
gpio_keys_gpio_report_event(bdata);//這裡是中斷引起的事件報告,通過gpio_keys_gpio_report_event,傳遞給input_event,再到input_handle_event,經過check,再通過input_pass_event,獲取到對應的handler,用handler->event將事件傳遞給使用者空間。
}
input_sync(input);
device_init_wakeup(&pdev->dev, wakeup);
return 0;
fail3:
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
fail2:
while (--i >= 0)
gpio_remove_key(&ddata->data[i]);
platform_set_drvdata(pdev, NULL);
fail1:
input_free_device(input);
kfree(ddata);
/* If we have no platform_data, we allocated buttons dynamically. */
if (!pdev->dev.platform_data)
kfree(pdata->buttons);
return error;
}
實際上Keypad驅動的實現依附於Input輸入子系統,其完整流程比較複雜,這裡只簡單分析一下流程,主要是描述鍵盤按鍵是如何從驅動傳遞到介面的。