我们新项目硬件设计上使用gpio口做按键,所以我就需要搞定这个驱动,本来想自己写一个gpio口的按键驱动,然后看了下内核下面的代码,已经有现成的了。Linux内核下游很多很多的现成驱动,只要你想得到的,基本都是有现成的,当然了,不包括一些非正常的需求性问题,学会在Linux下找驱动,看驱动和内核代码,我觉得是一件享受和快乐的事情。

不过我还是在使用这个驱动上遇到了问题。

1. 先说ADC 按键

之前的文章有写过adc按键的实现,无非就是为了省点GPIO口。

RK 利用SARADC 来做多个按键

2. GPIO 按键硬件原理图

3. 驱动代码

kernel-4.4/drivers/input/keyboard/gpio_keys.c

完整代码可查看

https://gitee.com/weiqifa/gpio_key/blob/master/gpio_keys.c

驱动代码流程,从probe处开始

刚开始的时候,我连dts文件都不会写,因为之前没有接触过这个驱动。然后看了gpio_keys_get_devtree_pdata函数,之后又看了内核代码下其他项目其他平台的dts文件,才知道怎么写这个驱动的dts文件。

实话说,这个驱动完成了很多我们需要的功能,比如防抖,比如中断,比如按键label等等。

3.1 gpio_keys_get_devtree_pdata 函数解析dts文件

这个文件解析的dts 有两种方式,一种是直接传入irq的,一种是只传入gpio口的。

我们的这个项目,就只传入了gpio口

3.2 gpio_keys_setup_key 函数

这个函数用来设置gpio口的中断的,直接看代码会比较清楚。

下面这个函数,我还没有想清楚它的作用,看了回调函数里面的实现,是为了把开启的工作队列停止掉。但是我加了打印并没有打印,我猜测是为了防止误触发,就是按键按下的时间非常短的时候,才会调用这个。

/**
 * devm_add_action() - add a custom action to list of managed resources
 * @dev: Device that owns the action
 * @action: Function that should be called
 * @data: Pointer to data passed to @action implementation
 *
 * This adds a custom action to the list of managed resources so that
 * it gets executed as part of standard resource unwinding.
 */
int devm_add_action(struct device *dev, void (*action)(void *), void *data)
{
 struct action_devres *devres;

 devres = devres_alloc(devm_action_release,
         sizeof(struct action_devres), GFP_KERNEL);
 if (!devres)
  return -ENOMEM;

 devres->data = data;
 devres->action = action;

 devres_add(dev, devres);
 return 0;
}

3.3 驱动修改

驱动修改的代码如下

--- a/kernel-4.4/drivers/input/keyboard/gpio_keys.c
+++ b/kernel-4.4/drivers/input/keyboard/gpio_keys.c
@@ -32,6 +32,11 @@
 #include <linux/of_irq.h>
 #include <linux/spinlock.h>

+
+#define LOG_TAG "[BUTTON]: %s() line: %d "
+#define PRINTK_T(fmt, args...)  printk(KERN_INFO LOG_TAG fmt, __FUNCTION__, __LINE__,  ##args)
+
+
 struct gpio_button_data {
        const struct gpio_keys_button *button;
        struct input_dev *input;
@@ -462,9 +467,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
        spin_lock_init(&bdata->lock);

        if (gpio_is_valid(button->gpio)) {
-
-               error = devm_gpio_request_one(&pdev->dev, button->gpio,
-                                             GPIOF_IN, desc);
+               PRINTK_T("gpio:%d\n",button->gpio);
+               error = devm_gpio_request(&pdev->dev, button->gpio,desc);
                if (error < 0) {
                        dev_err(dev, "Failed to request GPIO %d, error %d\n",
                                button->gpio, error);
@@ -483,7 +487,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
                if (button->irq) {
                        bdata->irq = button->irq;
                } else {
+                       gpio_direction_input(button->gpio);
                        irq = gpio_to_irq(button->gpio);
+                       PRINTK_T("===weiqifa=== irq :%d\n",irq);
                        if (irq < 0) {
                                error = irq;
                                dev_err(dev,
@@ -540,8 +546,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
        if (!button->can_disable)
                irqflags |= IRQF_SHARED;

-       error = devm_request_any_context_irq(&pdev->dev, bdata->irq,
-                                            isr, irqflags, desc, bdata);
+       PRINTK_T("===weiqifa=== devm_request_threaded_irq()\n");
+
+       error = devm_request_threaded_irq(&pdev->dev, bdata->irq,NULL,
+                                            isr, irqflags| IRQF_ONESHOT, desc, bdata);
        if (error < 0) {
                dev_err(dev, "Unable to claim irq %d; error %d\n",
                        bdata->irq, error);
@@ -709,6 +717,8 @@ static int gpio_keys_probe(struct platform_device *pdev)
        int i, error;
        int wakeup = 0;

+       PRINTK_T("start.\n");
+
        if (!pdata) {
                pdata = gpio_keys_get_devtree_pdata(dev);
                if (IS_ERR(pdata))
@@ -779,6 +789,8 @@ static int gpio_keys_probe(struct platform_device *pdev)

        device_init_wakeup(&pdev->dev, wakeup);

+       PRINTK_T("end.\n");
+
        return 0;

 err_remove_group:

可以确定的是,如果不修改的话,肯定是会出错的。

你要知道,这个驱动是在2005年就完成编写了,中间经过了多少次的系统升级,而且很多厂商主推的还是ADC按键驱动,GPIO口驱动默认情况下是会被抛弃的,厂商释放的SDK根本就不会记得修改这个驱动代码,所以别以为你的手机运行正常里面就没有bug,bug无处不在,只是我们有了重启大法而已。

4. dts 代码

gpio-keys {
   compatible = "gpio-keys";
   #address-cells = <1>;
   #size-cells = <0>;
   autorepeat;

   //pinctrl-names = "default";
   //pinctrl-0 = <&pwrbtn>;

   button@0 {
     gpios = <&pio 49 IRQ_TYPE_EDGE_BOTH>;
     linux,code = <KEY_F13>;
     label = "GPIO F13 Power";
     linux,input-type = <1>;
     gpio-key,wakeup = <1>;
     debounce-interval = <100>;
   };

   button@1 {
     gpios = <&pio 48 IRQ_TYPE_EDGE_BOTH>;
     linux,code = <KEY_F14>;
     label = "GPIO F14 Power";
     linux,input-type = <1>;
     gpio-key,wakeup = <1>;
     debounce-interval = <100>;
   };

   button@2 {
     gpios = <&pio 51 IRQ_TYPE_EDGE_BOTH>;
     linux,code = <KEY_F15>;
     label = "GPIO F15 Power";
     linux,input-type = <1>;
     gpio-key,wakeup = <1>;
     debounce-interval = <100>;
   };
 };

5. 测试驱动

烧录后按下按键,可以看到键值上报.


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

嵌入式Linux

微信扫描二维码,关注我的公众号

Logo

更多推荐