内核如何获取音量键按下的状态

本文详细解析了高通平台如何通过音量下键实现设备重启的机制。介绍了QPnP power-on驱动支持的功能,包括电源开关原因、按键检测等,并深入分析了设备树配置及gpio-keys驱动如何解析配置,最后解释了内核模块如何获取按键状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.tjtech.me/how-to-get-volume-key-pressed.html

高通平台一般会把音量下键绑在PMIC RESIN上,这样除了长按电源键重启外,还可以按音量下键重启。

先从内核文档(Kernel3.18)了解下RESIN:

Qualcomm QPNP power-on

The qpnp-power-on is a driver which supports the power-on(PON)
peripheral on Qualcomm PMICs. The supported functionality includes
power on/off reason, key press/release detection, PMIC reset configurations
and other PON specifc features. The PON module supports multiple physical
power-on (KPDPWR_N, CBLPWR) and reset (KPDPWR_N, RESIN, KPDPWR+RESIN) sources.

dts配置:

               pm8909_pon: qcom,power-on@800 {
                        compatible = "qcom,qpnp-power-on";
                        reg = <0x800 0x100>;
                        interrupts = <0x0 0x8 0x0>,
                                     <0x0 0x8 0x1>;
                        interrupt-names = "kpdpwr", "resin";
                        qcom,pon-dbc-delay = <15625>;
                        qcom,system-reset;
                        qcom,clear-warm-reset;
                        qcom,store-hard-reset-reason;

                        qcom,pon_1 {
                                qcom,pon-type = <0>;
                                qcom,support-reset = <1>;
                                qcom,pull-up = <1>;
                                qcom,s1-timer = <10256>;
                                qcom,s2-timer = <2000>;
                                qcom,s2-type = <1>;
                                linux,code = <116>;
                        };  

                        qcom,pon_2 {
                                qcom,pon-type = <1>;
                                qcom,pull-up = <1>;
                                linux,code = <114>;
                        };  
                };  

具体node/prop含义都在下面这个文件里:
devicetree/bindings/platform/msm/qpnp-power-on.txt
pon-type就是复位源,linux,code就是对应的按键,116是电源键,114是音量下键,具体上报是在中断里派遣:

static int
qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
{
        int rc;
        struct qpnp_pon_config *cfg = NULL;
        u8 pon_rt_sts = 0, pon_rt_bit = 0; 
        u32 key_status;
        u64 elapsed_us;
...
        pr_debug("PMIC input: code=%d, sts=0x%hhx\n",
                                        cfg->key_code, pon_rt_sts);
        key_status = pon_rt_sts & pon_rt_bit;
...
static irqreturn_t qpnp_kpdpwr_irq(int irq, void *_pon)
{
        int rc;
        struct qpnp_pon *pon = _pon;

        rc = qpnp_pon_input_dispatch(pon, PON_KPDPWR);

注意看文档描述,还支持bark中断,这样可以添加一些自定义的处理了。

那音量下键没有接到PMIC RESIN,而是接到了GPIO上,内核是怎么获取按键状态的呢。

看下gpio的配置:

gpio_keys {
                compatible = "gpio-keys";
                input-name = "gpio-keys";
                pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
                pinctrl-0 = <&gpio_key_active>;
                pinctrl-1 = <&gpio_key_suspend>;

                vol_up {
                        label = "volume_up";
                        gpios = <&msm_gpio 90 0x1>;
                        linux,input-type = <1>;
                        linux,code = <115>;
                        gpio-key,wakeup;
                        debounce-interval = <15>;
                };  

                vol_down {
                        label = "volume_down";
                        gpios = <&msm_gpio 91 0x1>;
                        linux,input-type = <1>;
                        linux,code = <114>;
                        gpio-key,wakeup;
                        debounce-interval = <15>;
                };  

驱动解析这个设备树配置的是drivers/input/keyboard/gpio_keys.c,具体是在gpio_keys_get_devtree_pdata里:

#ifdef CONFIG_OF
/*
 * Translate OpenFirmware node properties into platform_data
 */
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
...
       for_each_child_of_node(node, pp) {
                int gpio;
                enum of_gpio_flags flags;

                if (!of_find_property(pp, "gpios", NULL)) {
                        pdata->nbuttons--;
                        dev_warn(dev, "Found button without gpios\n");
                        continue;
                }

                gpio = of_get_gpio_flags(pp, 0, &flags);
                if (gpio < 0) {
                        error = gpio;
                        if (error != -EPROBE_DEFER)
                                dev_err(dev,
                                        "Failed to get gpio flags, error: %d\n",
                                        error);
                        return ERR_PTR(error);
                }

                button = &pdata->buttons[i++];

                button->gpio = gpio;
                button->active_low = flags & OF_GPIO_ACTIVE_LOW;

                if (of_property_read_u32(pp, "linux,code", &button->code)) {
                        dev_err(dev, "Button without keycode: 0x%x\n",
                                button->gpio);
                        return ERR_PTR(-EINVAL);
                }

                button->desc = of_get_property(pp, "label", NULL);

                if (of_property_read_u32(pp, "linux,input-type", &button->type))
                        button->type = EV_KEY;

                button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

                if (of_property_read_u32(pp, "debounce-interval",
                                        &button->debounce_interval))
                        button->debounce_interval = 5;
        }
...

状态上报是在:

static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
        const struct gpio_keys_button *button = bdata->button;
        struct input_dev *input = bdata->input;
        unsigned int type = button->type ?: EV_KEY;
        int state;

        state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; //here

        if (type == EV_ABS) {
                if (state)
                        input_event(input, type, button->code, button->value);
        } else {
                input_event(input, type, button->code, !!state);
        }    
        input_sync(input);
}

什么时候上报的了?中断isr处理:

static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
        struct gpio_button_data *bdata = dev_id;

        BUG_ON(irq != bdata->irq);

        if (bdata->button->wakeup)
                pm_stay_awake(bdata->input->dev.parent);
        if (bdata->timer_debounce)
                mod_timer(&bdata->timer,
                        jiffies + msecs_to_jiffies(bdata->timer_debounce));
        else
                schedule_work(&bdata->work);

        return IRQ_HANDLED;
}
static int gpio_keys_setup_key(struct platform_device *pdev,
                                struct input_dev *input,
                                struct gpio_button_data *bdata,
                                const struct gpio_keys_button *button)
{
...
        if (gpio_is_valid(button->gpio)) {
                INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
                setup_timer(&bdata->timer,
                            gpio_keys_gpio_timer, (unsigned long)bdata);

                isr = gpio_keys_gpio_isr;
                irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
        }

那内核其他模块如何获取按键状态? 我想可以参考gpio_keys_gpio_report_event把state取出应该就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值