Linux 输入 driver 工作不正常
Linux input driver not working properly
我写了一个小的 linux 输入 driver 来读取 gpio 的状态。
driver 注册成功,中断也被触发,但事件并不总是显示。
Driver 运行s on Beagleboneblack with Android and kernel version 3.8.13
为了测试它,我做了:
cat /dev/input/event2
或 运行 我编写的用户 space 应用程序。
该应用程序能够从其他输入设备正确检索事件。
提到 "event2" 是 /dev/input 中的正确事件,已经检查过了。
driver代码:
#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");
我删除了一些线条,使其更小。它与我在其中定义 GPIO 编号的 DTS 中的条目相匹配。 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 可能会重叠,这会影响输入流..?
将尝试更改代码以强制每 10 毫秒报告一次密钥,只是为了看看它是否有任何不同。
我没有别的想法。
非常感谢任何帮助。
更新:所以,我将代码更改为每 10 毫秒报告 1 次,每秒报告 1 次,没有区别,一定是其他原因。
有什么想法吗?
谢谢,
丹尼尔
看来我发现了问题。
设置中断时,应该在两个边沿都触发,因为linux输入系统只报告事件值的变化,因此当同一个边沿触发IRQ时,大多数时候事件是相同的,在我的例子中是 "low"。这意味着开关已释放,但在实际代码中,开关从未被视为已按下(除非该 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 结构。
我写了一个小的 linux 输入 driver 来读取 gpio 的状态。 driver 注册成功,中断也被触发,但事件并不总是显示。 Driver 运行s on Beagleboneblack with Android and kernel version 3.8.13 为了测试它,我做了:
cat /dev/input/event2
或 运行 我编写的用户 space 应用程序。 该应用程序能够从其他输入设备正确检索事件。 提到 "event2" 是 /dev/input 中的正确事件,已经检查过了。
driver代码:
#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");
我删除了一些线条,使其更小。它与我在其中定义 GPIO 编号的 DTS 中的条目相匹配。 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 可能会重叠,这会影响输入流..?
将尝试更改代码以强制每 10 毫秒报告一次密钥,只是为了看看它是否有任何不同。 我没有别的想法。 非常感谢任何帮助。
更新:所以,我将代码更改为每 10 毫秒报告 1 次,每秒报告 1 次,没有区别,一定是其他原因。
有什么想法吗?
谢谢, 丹尼尔
看来我发现了问题。 设置中断时,应该在两个边沿都触发,因为linux输入系统只报告事件值的变化,因此当同一个边沿触发IRQ时,大多数时候事件是相同的,在我的例子中是 "low"。这意味着开关已释放,但在实际代码中,开关从未被视为已按下(除非该 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 结构。