linux驱动input子系统学习五(输入设备驱动层)

输入设备驱动层就是主要由驱动工程师根据具体设备来实现了,既可以实现的很复杂又各种read,write,open等等。也可以实现的很简单。我这里是学习就根据系统给的参考readme实现一份简单的按键值得上报驱动。

在documentation文件夹下找到input文件夹,里面有两个主要的文档input.txt和input_programming.txt

其中input.txt主要讲述来input子系统的一些主要成员,层次信息等。

input_programming.txt主要是来说明驱动工程师如何实现设备驱动层,给的模板和建议。

input_programming.txt

这部分比较简单,参考模版就行。模版对每个变量,每个函数都分析的很清楚了。我直接借用模版写两中断按键驱动,直接上代码。

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/irqreturn.h>        /* 中断函数的返回值用到 */
#include <mach/irqs.h>              /* 中断号用到 */
#include <linux/interrupt.h>        /* 中断req和free用到 */


#define BUTTON_LIFT_IRQ IRQ_EINT2     /* 我的板子是接到外部中断2了 */


static struct input_dev *button_dev;      

static irqreturn_t button_interrupt(int irq, void *dummy)
{
    /* 得到按键状态,并上报给响应的上层程序,下一小节我分析具体的上报流程 */
    input_report_key(button_dev, KEY_LEFT, !gpio_get_value(S5PV210_GPH0(2)));
    /* 上报一个同步包 */
    input_sync(button_dev);
    return IRQ_HANDLED;
}

static int __init button_init(void)
{
    int error;
    int ret;
    
    /* 申请gpio */
    ret = gpio_request(S5PV210_GPH0(2), "GPH0");
    if(ret)
    {
        printk("button-x210: request gpio GPH0(2) fail");
        error = -EIO;
        goto err_io;
   }
    /* 设置gpio模式 */
    s3c_gpio_setpull(S5PV210_GPH0(2), S3C_GPIO_PULL_UP);    //上拉
    s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0xf));    //外部中断



    /* 请求并注册一个irq中断,上升沿和下降沿触发 */
    if (request_irq(BUTTON_LIFT_IRQ, button_interrupt,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button_left", NULL)) {
            printk(KERN_ERR "button.c: Can't allocate irq %d\n", BUTTON_LIFT_IRQ);
            error = -EBUSY;
            goto err_req_irq;
    }

    /* 申请一个input_dev并初始化里面的通用变量 */
    button_dev = input_allocate_device();
    if (!button_dev) {
        printk(KERN_ERR "button.c: Not enough memory\n");
        error = -ENOMEM;
        goto err_free_irq;
    }

   /* 设置按键事件和键值 */
    button_dev->evbit[0] = BIT_MASK(EV_KEY);
    button_dev->keybit[BIT_WORD(KEY_LEFT)] = BIT_MASK(KEY_LEFT);

    /* 注册这个input_dev */
    error = input_register_device(button_dev);
    if (error) {
        printk(KERN_ERR "button.c: Failed to register device\n");
        goto err_free_dev;
    }

    return 0;

 err_free_dev:
    input_free_device(button_dev);
 err_free_irq:
    free_irq(BUTTON_LIFT_IRQ, button_interrupt);
 err_req_irq:
    gpio_free(S5PV210_GPH0(2));
 err_io:
    return error;
}

/* 卸载时要吧申请的资源释放掉 */
static void __exit button_exit(void)
{
    input_unregister_device(button_dev);
    free_irq(BUTTON_LIFT_IRQ, NULL);    /* 这里必须是NULL,参考模版有问题(应该是文档很久没更新了,具体原因我在后面章节分析) */
    gpio_free(S5PV210_GPH0(2));
}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");

其中上报按键值的函数流程和中断函数的编写即注意点我在后面的章节分析。

轮询方式的基本和上面的一致。只不过是采用定时器定时查询。参考我的另一篇博客很容易写出来

linux内核定时timer的使用

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81267109