歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android系統移植之按鍵驅動篇

Android系統移植之按鍵驅動篇

日期:2017/3/1 11:18:04   编辑:Linux編程

平台:MX53_QSB開發板

MX53_QSB開發板上一起有四個按鍵,分別為RESET,POWER,USER1,USER2。其中RESET為純硬件復位按鍵,無須軟件控制。POWER,USER1,USER2三個按鍵均需要程序控制。默認BSP包中將三個按鈕全設置為上升和下降沿觸發,當系統起來後,按下POWER鍵,進入睡眠狀態,這時再按下POWER鍵喚醒時,系統系統被喚醒,但是一旦手松下,又觸發了POWER鍵的中斷,系統又睡下去了。在進入睡眠狀態後,只有按USER1和USER2這兩個鍵,才能正常喚醒。因此,這裡有BUG需修復。

按鍵驅動有兩個,一個為矩陣鍵盤驅動,路徑為:

\drivers\input\keyboard\mxc_keyb.c

一個為GPIO接口的鍵盤驅動,路徑為:

\drivers\input\keyboard\gpio_keys.c

前者用於多按鍵的情況,如果按鍵比較少,後者就可以了,一般情況下,Android系統只需幾個按鍵就可以了,所以大多數情況下,都是使用的gpio_keys.c。下面我們將詳細分析該驅動的工作流程。

在module_init函數中,在總線上注冊名為gpio-keys的驅動,這時將夫在總線上查找是否存在同名的設備。系統初始化時,mx53_loco.c中,mxc_board_init函數已調用了按鍵初始化函數loco_add_device_buttons(),同時pdev的數據結構體中定義了按鍵的相關信息如下:

#define GPIO_BUTTON(gpio_num, ev_code, act_low,descr, wake) \

{ \

.gpio = gpio_num, \

.type = EV_KEY, \

.code = ev_code, \

.active_low = act_low, \

.desc = "btn " descr, \

.wakeup = wake, \

}

static struct gpio_keys_button loco_buttons[] = {

GPIO_BUTTON(MX53_nONKEY,KEY_POWER, 1, "power", 0),

GPIO_BUTTON(USER_UI1,KEY_BACK, 1, "back", 0),

GPIO_BUTTON(USER_UI2,KEY_HOME, 1, "home", 0),

};

static struct gpio_keys_platform_dataloco_button_data = {

.buttons = loco_buttons,

.nbuttons = ARRAY_SIZE(loco_buttons),

};

可見,結構體定義了三個GPIO,分別為power,back以及home。注意GPIO_BUTTON函數中的實參,第一個為對應的GPIO,純硬件特性,第二個為按鍵的鍵值,在linux/input.h中定義:

#defineKEY_POWER 116

#defineKEY_HOME 102

#defineKEY_BACK 158

第三個參數為1,表明按下去為1,抬起為0;第四個參數為按鍵名稱描述,無關緊要;第5個參數為wakeup,看名稱好像與休眠喚醒有關,實際測試修改為1後,沒有發現有什麼異常。這幾個參數是後續添加新的按鍵,或者更改按鍵功能的關鍵。

再回到gpio-keys.c中,找到同名設備後,探測函數gpio_keys_probe得到執行,調用input_allocate_device函數創建一個input設備,再通過一個for循環,調用gpio_keys_setup_key和input_set_capability函數,設置上表中列出的三個IO口的中斷函數以及按鍵功能。後面用到了sysfs_create_group函數創建了基於sys系統的文件屬性組,具體可以在?sys/devices/platform/gpio-keys目錄下找到gpio_keys_attr_group結構體中attrs組對應的gpio_keys_attrs結構體中的幾個屬性文件,gpio_keys_attrs結構體描述如下:

static struct attribute *gpio_keys_attrs[] = {

&dev_attr_keys.attr,

&dev_attr_switches.attr,

&dev_attr_disabled_keys.attr,

&dev_attr_disabled_switches.attr,

NULL,

};

dev_attr_disabled_keys和dev_attr_disabled_switches在前面做了如下聲明:

static DEVICE_ATTR(disabled_keys, S_IWUSR |S_IRUGO,

gpio_keys_show_disabled_keys,

gpio_keys_store_disabled_keys);

static DEVICE_ATTR(disabled_switches, S_IWUSR |S_IRUGO,

gpio_keys_show_disabled_switches,

gpio_keys_store_disabled_switches);

在android系統終端,我們可以進入該路徑查看是否存在,以及他們的文件屬性如下:


注意上面四個文件的讀寫屬性。

可見,這裡留有在系統中操作按鍵的後門,具體以後再分析。接下來,調用input_register_device函數向輸入子系統注冊input_dev,結束探測函數初始化。

探測函數的關鍵點在gpio_keys_setup_key函數中,相關代碼如下:

static int __devinit gpio_keys_setup_key(structplatform_device *pdev,

struct gpio_button_data *bdata,

struct gpio_keys_button *button)

{

char *desc= button->desc ? button->desc : "gpio_keys";//從loco_buttons數組中獲得按鍵的描述名稱

structdevice *dev = &pdev->dev;

unsignedlong irqflags;

intirq, error;

//傳入參數: 過期時間,回調函數,上下文

//當計時器過期時,回調函數gpio_keys_timer將得到運行

setup_timer(&bdata->timer,gpio_keys_timer, (unsigned long)bdata);//初始化計時器

INIT_WORK(&bdata->work,gpio_keys_work_func);

error= gpio_request(button->gpio, desc);//請求使用GPIO

if(error < 0)

{

dev_err(dev,"failed to request GPIO %d, error %d\n",button->gpio, error);

gotofail2;

}

error= gpio_direction_input(button->gpio);//設置指定的GPIO為輸入模式

if(error < 0)

{

dev_err(dev,"failed to configure direction for GPIO %d, error %d\n",

button->gpio,error);

gotofail3;

}

irq =gpio_to_irq(button->gpio);//獲得GPIO對應的中斷號

if (irq< 0)

{

error= irq;

dev_err(dev,"Unable to get irq number for GPIO %d, error %d\n",button->gpio,error);

gotofail3;

}

irqflags= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;

//irqflags= IRQF_TRIGGER_FALLING;//lqm changed.

/*

* If platform has specified that the buttoncan be disabled,

* we don't want it to share the interruptline.

*/

if(!button->can_disable)

irqflags|= IRQF_SHARED;

error= request_irq(irq, gpio_keys_isr, irqflags, desc, bdata);//請求按鍵中斷

……

}

該函數使用了帶定時器延時的中斷機制,用於按鍵去抖動。即中斷的執行在定時器到來之後執行,這樣能夠有效的去除按鍵抖動。同時,中斷使用工作隊列的機制。

整個按鍵中斷的工作調用有點復雜,下面逐步解析:

首先,上面函數中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,即一旦調度相應隊列名,該函數將得到執行。

Copyright © Linux教程網 All Rights Reserved