[RK3288][Android6.0] 系统按键驱动流程分析

Posted 请给我倒杯茶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[RK3288][Android6.0] 系统按键驱动流程分析相关的知识,希望对你有一定的参考价值。

本文转载自:http://blog.csdn.net/kris_fei/article/details/77894406

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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;
}

以上是关于[RK3288][Android6.0] 系统按键驱动流程分析的主要内容,如果未能解决你的问题,请参考以下文章

[RK3288][Android6.0] 调试笔记 --- 系统识别不同硬件版本方法

[RK3288][Android6.0] 调试笔记 --- hwclock命令无法使用

[RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED

[RK3288][Android6.0] Display驱动初始化流程小结

[RK3288][Android6.0] 调试笔记 --- 普通串口的添加

[RK3288][Android6.0] 调试笔记 --- pmu(rk818)寄存器读写