2016-01-26 2 views
0

Я написал небольшой драйвер ввода linux, который считывает состояние gpio. Драйвер регистрируется в порядке, а также прерывается, но события не всегда отображаются. Драйвер работает на Beagleboneblack с Android и версии ядра 3.8.13 Чтобы проверить это, я сделать что-либо:Драйвер ввода Linux не работает должным образом

кошка/DEV/вход/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 иногда я получаю некоторые символы, большую часть времени я не знаю. Это совпадает с выходом моего приложения при чтении устройства ввода:

[email protected]:/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 мс, просто чтобы узнать, не имеет значения. У меня нет другой идеи. Любая помощь очень ценится.

Обновление: поэтому я изменил код, чтобы сообщать 1 раз каждые 10 мс и 1 раз в секунду, без разницы, должно быть что-то еще.

Любая идея?

Спасибо, Daniel

ответ

0

Кажется, я нашел проблему. При настройке прерывания он должен запускаться на обоих краях, так как система ввода linux сообщает только об изменении значения события, поэтому, когда тот же самый край запускает IRQ, большую часть времени событие будет таким же, что в мое дело было «низким». Это означает, что коммутатор вышел, но с фактическим кодом переключатель никогда не рассматривался как нажатый (если уровень на этом gpio еще не был стабильным и мог читать максимум, а не низкий). Кажется, это были единственные случаи, когда я получал сообщение о событии.

С следующие изменения работы драйвера, как ожидалось:

In 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 переписан, чтобы избежать многочисленных IRQs в течение короткого периода времени:

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 структуры.