Linux输入驱动程序无法正常工作

时间:2016-01-26 15:06:33

标签: linux input kernel driver device

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

  

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!

我在我的irq例程中放置了dev_info以查看调用input_report_key的时间。 有时我会收到更多事件,有时会有更多的IRQ,有时我只会收到IRQ消息。

所以我没有错过任何IRQ,似乎与关键报告事件有关。我想也许IRQ是重叠的,如果按下按钮时它们会相互接近,这会影响输入流量。

尝试更改代码以强制每10ms报告一次密钥,只是为了看看它是否有任何区别。 我没有其他想法。 任何帮助非常感谢。

更新:所以,我将代码更改为每10分钟报告1次,每秒1次,没有区别,必须是别的。

有什么想法吗?

谢谢你, 丹尼尔

1 个答案:

答案 0 :(得分:0)

似乎我发现了这个问题。 设置中断时,它应该在两个边缘都被触发,因为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

btn_drv struct中的new_timescan_disc个新条目。