1. 程式人生 > >android 4.4 按鍵分析四--鍵盤新增

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輸入子系統,其完整流程比較複雜,這裡只簡單分析一下流程,主要是描述鍵盤按鍵是如何從驅動傳遞到介面的。