代码之家  ›  专栏  ›  技术社区  ›  mdaniel

Linux输入驱动程序工作不正常

  •  0
  • mdaniel  · 技术社区  · 9 年前

    我写了一个小的linux输入驱动程序,它读取gpio的状态。 驱动程序注册OK,中断也被触发,但事件并不总是显示。 驱动程序在Beagleboneblack上运行,Android和内核版本为3.8.13 为了测试它,我可以:

    cat/dev/input/event2

    或者运行我编写的用户空间应用程序。 该应用程序能够从其他输入设备正确检索事件。 要提到的是,“event2”是/dev/input中的正确事件,已选中。

    驾驶员代码:

    #include <linux/module.h>                                                                                                                                                                                
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    ...
    
    struct button_drv {
    
    ·   int··   ·   ·   ·   btn_irq;
    ·   int ·   ·   ·   ·   btn_gpio;
    ·   struct device·  ·   *dev;
    ·   struct input_dev·   *btn_input;
    };
    
    static irqreturn_t btn_irq(int irq_nb, void *data) {
    
    ·   struct button_drv *btn_drv = (struct button_drv*)data;
    ·   struct input_dev *btn_input = btn_drv->btn_input;
    ·   int btn_gpio = btn_drv->btn_gpio;
    
    ·   dev_info(btn_drv->dev,"Inside button IRQ!\n");
    ·   input_report_key(btn_input,BTN_0,gpio_get_value(btn_gpio));
    ·   input_sync(btn_input);
    
    ·   return IRQ_HANDLED;
    }
    
    static int btn_probe(struct platform_device *pdev) {
    
    ·   int ret,btn_irq,btn_gpio;
    ·   struct button_drv *btn_drv;
    ·   struct input_dev *btn_input;
    ·   struct device_node *node = pdev->dev.of_node;
    ·   
    ·   dev_info(&pdev->dev,"Inside btn probe driver!\n");
    ·   btn_drv = devm_kzalloc(&pdev->dev,sizeof(struct button_drv),GFP_KERNEL);
    ·   if(!btn_drv)
    ·   {
    ·   ·   dev_err(&pdev->dev,"Failed to allocate memory for btn device!\n");
    ·   ·   return -ENOMEM;
    ·   }
    
    ·   platform_set_drvdata(pdev,btn_drv);
    ·   btn_gpio = of_get_named_gpio(node,"btn-gpios",0);
    ·   
    ·   ret = devm_gpio_request_one(&pdev->dev,btn_gpio,GPIOF_DIR_IN,"btn");
    ·   if(IS_ERR_VALUE(ret))
        {
      ·   dev_err(&pdev->dev,"Failed to allocate btn gpio!\n");
            ret = -ENODEV;
    ·   ·   goto err_mem;
       }
    ·   
    ·   btn_irq = gpio_to_irq(btn_gpio);
    ·   if(IS_ERR_VALUE(btn_irq))
    ·   {
       ·   dev_err(&pdev->dev,"Failed to get corresponding btn IRQ!\n");
       ·   ret = -ENODEV;
       ·   goto err_out;
    ·   }
    
    ·   ret = devm_request_irq(&pdev->dev,btn_irq,btn_irq,
    ·   ·   ·   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,pdev->name, btn_drv);
    ·   if(IS_ERR_VALUE(ret))
    ·   {
    ·   ·   dev_err(&pdev->dev,"Failed to map btn IRQ!\n");
    ·   ·   ret = -EINVAL;
    ·   ·   goto err_out;
    ·   }
    
    ·   btn_input = input_allocate_device();
    ·   if(IS_ERR(btn_input))
    ·   {
    ·   ·   dev_err(&pdev->dev,"No memory for btn input device!");
    ·   ·   ret = -ENODEV;                                                                                                                                                                                   
    ·   ·   goto err_out;
    ·   }
    ·   btn_input->evbit[0] = BIT_MASK(EV_KEY);
    ·   btn_input->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
    ·   btn_input->name = "button1";
        btn_input->phys = "button1/input0";
    ·   ret = input_register_device(btn_input);
    ·   if(IS_ERR_VALUE(ret))
    ·   {
    ·   ·   dev_err(&pdev->dev,"Failed to register btn input device!\n");
    ·   ·   ret = -ENODEV;
    ·   ·   goto err_out;
    ·   }
    
    ·   btn_drv->btn_gpio · = btn_gpio;
    ·   btn_drv->btn_irq ·  = btn_irq;
    ·   btn_drv->btn_input ·= btn_input;
    ·   btn_drv->dev·   ·   = &pdev->dev;
    
    ·   dev_info(&pdev->dev,"Registered btn input device!");
    ·   
    ·   return 0;
    
    err_out:
    ·   devm_gpio_free(&pdev->dev,btn_gpio);
    
    err_mem:
    ·   platform_set_drvdata(pdev,NULL);
    ·   devm_kfree(&pdev->dev, btn_drv);
    ·   return ret;
    }
    
    static int btn_remove(struct platform_device *pdev) {
    
    ·.......
    
    ·   return 0;
    }                                                      
    
    
    static struct platform_driver btn_drv = {
    ·   .probe· ·   = btn_probe,
    ·   .remove··   = btn_remove,
    ...
    };
    
    
    static int __init btn_init(void) {
    
    ·   return platform_driver_register(&btn_drv);
    }
    module_init(btn_init);
    
    static void __exit btn_exit(void) {
    
    ·   platform_driver_unregister(&btn_drv);
    }
    module_exit(btn_exit);
    
    
    MODULE_DESCRIPTION("Button driver");
    MODULE_AUTHOR("Test");
    MODULE_LICENSE("GPL");        
    

    我去掉了一些线条,使其更小。它与DTS中定义GPIO编号的条目相匹配。这里删除了OF_相关的内容。

    应用程序代码:

    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <linux/input.h>
    #include <string.h>
    #include <stdio.h>
    #include <sys/select.h>
    
    int main(int argc, char * argv[]) {
    
    ·   char* device;
    ·   int dev_fd,ret;
    ·   fd_set readfds;
    
    ·   if(argc < 2)
    ·   {
    ·   ·   printf("No input device specified!\n");
    ·   ·   exit(1);                                                                                                                                                                                         
    ·   }
    
    ·   device = argv[1];
    ·   dev_fd = open(device,O_RDONLY);
    ·   if(dev_fd<0)
    ·   {
    ·   ·   printf("Failed to oped device: %s with error: %s\n",device,strerror(errno));
    ·   ·   exit(2);
    ·   }
    ·   
    ·   do {
    ·   ·   FD_ZERO(&readfds);
    ·   ·   FD_SET(dev_fd,&readfds);
    
    ·   ·   ret = select(dev_fd+1,&readfds,NULL,NULL,NULL);
    ·   ·   if(ret < 0)
    ·   ·   {
    ·   ·   ·   printf("Select returned error: %s\n",strerror(errno));
    ·   ·   ·   exit(3);
    ·   ·   }
       ·    
            if(FD_ISSET(dev_fd,&readfds))
    ·   ·   {
    ·   ·   ·   int nb;
    ·   ·   ·   struct input_event ev;
    
    ·   ·   ·   nb = read(dev_fd,&ev,sizeof(ev));
    ·   ·   ·   if(nb < 0)
    ·   ·   ·   {
    ·   ·   ·   ·   printf("Failed to read from input device, err: %s\n",strerror(errno));
    ·   ·   ·   ·   exit(4);
    ·   ·   ·   }
    
    ·   ·   ·   printf("Read for sizeof-ev:%lu returned nb:%d, Type:%d, Code:%d, Value:%d\n",
    ·   ·   ·   ·   ·   sizeof(ev), nb, ev.type, ev.code, ev.value);
    ·   ·   }
    ·   }while(1);
    
    ·   close(dev_fd);
        exit(0);
    }
    

    在执行时 cat /dev/input/event2 有时我会得到一些角色,大多数时候我不会。这与读取输入设备时我的应用程序的输出相匹配:

    shell@beagleboneblack:/data/user/bbone/01.BtnInput # ./input_read /dev/input/event2
    [  158.419312] btn-drv btn.9: Inside  button IRQ!
    [  158.425230] btn-drv btn.9: Inside  button IRQ!
    [  159.125057] btn-drv btn.9: Inside  button IRQ!
    [  159.130966] btn-drv btn.9: Inside  button IRQ!
    Read for sizeof-ev:16 returned nb:16, Type:1, Code:256, Value:1
    Read for sizeof-ev:16 returned nb:16, Type:0, Code:0, Value:0
    Read for sizeof-ev:16 returned nb:16, Type:1, Code:256, Value:0
    Read for sizeof-ev:16 returned nb:16, Type:0, Code:0, Value:0
    [  161.441807] btn-drv btn.9: Inside  button IRQ!
    [  161.447731] btn-drv btn.9: Inside  button IRQ!
    [  161.453645] btn-drv btn.9: Inside  button IRQ!
    [  161.571151] btn-drv btn.9: Inside  button IRQ!
    [  161.593890] btn-drv btn.9: Inside  button IRQ!
    ......
    [  164.519528] btn-drv btn.9: Inside  button IRQ!
    [  164.525427] btn-drv btn.9: Inside  button IRQ!
    [  165.023885] btn-drv btn.9: Inside  button IRQ!
    [  165.029780] btn-drv btn.9: Inside  button IRQ!
    [  165.416927] btn-drv btn.9: Inside  button IRQ!
    [  165.444170] btn-drv btn.9: Inside  button IRQ!
    [  165.450079] btn-drv btn.9: Inside  button IRQ!
    Read for sizeof-ev:16 returned nb:16, Type:1, Code:256, Value:1
    Read for sizeof-ev:16 returned nb:16, Type:0, Code:0, Value:0
    [  166.075658] btn-drv btn.9: Inside  button IRQ!
    [  166.081639] btn-drv btn.9: Inside  button IRQ!
    Read for sizeof-ev:16 returned nb:16, Type:1, Code:256, Value:0
    Read for sizeof-ev:16 returned nb:16, Type:0, Code:0, Value:0
    [  166.272010] btn-drv btn.9: Inside  button IRQ!
    [  166.277938] btn-drv btn.9: Inside  button IRQ!
    [  166.547894] btn-drv btn.9: Inside  button IRQ!
    

    我放置了一个 dev_info 在我的irq例行程序中 input_report_key 被调用。 有时我收到更多的事件,有时收到更多的IRQ,有时我只收到IRQ消息。

    所以我没有错过任何IRQ,这似乎与关键报告事件有关。我在想,如果按下按钮时IRQ彼此靠近,可能会重叠,这会影响输入流。。?

    将尝试更改代码,以强制每10毫秒报告一次密钥,以查看是否有任何不同。 我没有别的主意。 非常感谢任何帮助。

    更新:所以,我将代码更改为每10ms报告一次,每秒钟报告一次。

    有什么想法吗?

    非常感谢。 丹尼尔

    1 回复  |  直到 9 年前
        1
  •  0
  •   mdaniel    9 年前

    看来我发现了问题。 当设置中断时,它应该在两个边缘触发,因为linux输入系统只报告事件值的变化,因此当同一个边缘触发IRQ时,大多数时间事件都是相同的,在我的情况下是“低”。这意味着开关被释放,但在实际代码中,开关从未被视为被按下(除非gpio上的电平尚未稳定,可以读取高电平而不是低电平)。似乎这是我得到事件报告时唯一的案例。

    通过以下更改,驱动程序按预期工作:

    在btn_probe中替换:

    ret = devm_request_irq(&pdev->dev,btn_irq,btn_irq,
            IRQF_TRIGGER_FALLING | IRQF_ONESHOT,pdev->name, btn_drv);
    

    具有

    ret = devm_request_irq(&pdev->dev,btn_irq,btn_irq,
            IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,pdev->name, btn_drv);
    

    并且IRQ被重写以避免在短时间内出现多个IRQ:

    static irqreturn_t btn_irq(int irq_nb, void *data) {
    
    ·   struct button_drv *btn_drv = (struct button_drv*)data;
        struct input_dev *btn_input = btn_drv->btn_input;
    ·   int btn_gpio = btn_drv.btn_gpio;
    ·   int val;
    
    ·   if(time_before(jiffies,btn_drv.new_time))
    ·   {
    ·   ·   btn_drv.scan_disc++;
    ·   ·   return IRQ_HANDLED;
    ·   }
    
    ·   if(!gpio_get_value(btn_gpio))
    ·   {
    ·   ·   pr_info("btn-drv: switch pressed!\n");
    ·   ·   val = 1;
    ·   }
    ·   else
    ·   {
    ·   ·   pr_info("btn-drv: switch released!\n");
    ·   ·   val = 0;
    ·   }
    
    ·   input_report_key(btn_input,BTN_0,val);
    ·   input_sync(btn_input);
    
    ·   btn_drv.new_time = jiffies + SCAN_DELTA;
    
    ·   return IRQ_HANDLED;
    }
    

    使用:

    #define SCAN_DELTA∙ msecs_to_jiffies(100)∙ //msecs

    new_time scan_disc btn_drv结构中的新条目。