[RK3288][Android6.0] 系統按鍵驅動流程分析
阿新 • • 發佈:2019-01-01
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;
}