1. 程式人生 > >[RK3288][Android6.0] 系統按鍵驅動流程分析

[RK3288][Android6.0] 系統按鍵驅動流程分析

Rockchip的按鍵驅動位於 kernel/drivers/input/keyboard/rk_keys.c

預設支援的keys在dts中定義:

這裡寫圖片描述

其中power key作為普通gpio,具有喚醒功能。而其他按鍵比如,volume up/down 可以通過adc精確讀取到gpio的電壓值,原理圖如下:

這裡寫圖片描述
這裡寫圖片描述

和一般的按鍵一樣,驅動是通過核心input子系統來將keys註冊供使用者空間使用

static int keys_probe(struct platform_device *pdev)
{
    input = devm_input_allocate_device(dev);
    input->
name = "rk29-keypad"; /* pdev->name; */ input->phys = "gpio-keys/input0"; input->dev.parent = dev; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; error = input_register_device(input); }

dts keys的解析通過rk_keys_parse_dt()實現。而每個key都會註冊一個定時器函式來處理狀態變化並通知使用者空間。

    for (i = 0; i < ddata->nbuttons; i++) {
        if (button->code) {
            setup_timer(&button->timer,
                    keys_timer, (unsigned long)button);
        }
    }

keys_timer():

static void keys_timer(unsigned long _data)
{
    //普通gpio直接讀取
    if (button->
type == TYPE_GPIO) state = !!((gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low); else //adc轉成bool狀態值 state = !!button->adc_state; //狀態變化上報事件 if (button->state != state) { button->state = state; input_event(input, EV_KEY, button->code, button->state); input_event(input, EV_KEY, button->code, button->state); input_sync(input); } //10ms後啟動定時器 if (state) mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES); }

定時器會處理普通gpio和adc兩種型別的按鍵,當狀態變化時,會向用戶空間上報當前事件、鍵值、狀態。預設開機時,定時器處理函式因為檢測不到狀態變化而關閉退出。

定時器的開啟有兩個地方會被呼叫:
1.系統開機會啟一個工作佇列,每100ms週期性呼叫一次檢測有沒有按鍵觸發

static void adc_key_poll(struct work_struct *work)
{
    if (!ddata->in_suspend) {
        //讀取adc電壓
        result = rk_key_adc_iio_read(ddata);
        for (i = 0; i < ddata->nbuttons; i++) {
            //允許值有一定範圍的漂移
            if (result < button->adc_value + DRIFT_ADVALUE &&
                result > button->adc_value - DRIFT_ADVALUE)
                button->adc_state = 1;
            else
                button->adc_state = 0;
            if (button->state != button->adc_state)
                mod_timer(&button->timer,
                      jiffies + DEBOUNCE_JIFFIES);
        }
    }
    //週期性呼叫。ADC_SAMPLE_JIFFIES為100ms
    schedule_delayed_work(&ddata->adc_poll_work, ADC_SAMPLE_JIFFIES);
}

2. power key喚醒時中斷處理會被觸發

static irqreturn_t keys_isr(int irq, void *dev_id)
{
    //上報power key事件
    if (button->wakeup && pdata->in_suspend) {
        button->state = 1;
        input_event(input, EV_KEY, button->code, button->state);
        input_sync(input);
    }
    if (button->wakeup)
        wake_lock_timeout(&pdata->wake_lock, WAKE_LOCK_JIFFIES);
    mod_timer(&button->timer, jiffies + DEBOUNCE_JIFFIES);

    return IRQ_HANDLED;
}