高通平台一般会把音量下键绑在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 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 ; } ...
ok. 状态上报是在:
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; 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取出应该就可以了。
版权声明: 本站所有文章均采用 CC BY-NC-SA 4.0 CN 许可协议。转载请注明原文链接!