linux输入子系统下按键驱动编写

学习目的:

  • 熟悉输入子系统下按键驱动程序编写

在分析输入子系统框架时,我们已经知道内核对不同类型的输入设备已经抽象出了不同的handler进行处理,device层实现纯硬件的操作,我们可以根据所实现驱动的功能对device层进行设计,主要是内容是当检测有效输入发送,调用input_event函数向handler层上报结果即可。device部分驱动程序设计主要分为以下几个步骤:

① 初始化硬件,注册input_dev设备

② 有效按键按下,向handler上报按键值

现在我们开始使用内核的输入子系统框架,将自己编写驱动程序融入其中,编写更通用的按键驱动程序。我们编写的驱动程序实现将开发板上的4个按键分别映射成键盘上不同键值,代表键盘上l、s、shift、enter值。

1、驱动入口函数

static int button_drv_init(void)
{
    unsigned int i = 0;
    
    init_timer(&button_timer);-------------------------->①
    button_timer.expires = 0;
    button_timer.function = button_timer_handler;
    add_timer(&button_timer);

    /* 分配一个input_dev结构体 */
    input = input_allocate_device();-------------------->②
    
    /* 产生哪一类事件 */
    set_bit(EV_KEY, input->evbit);
    
    /* 产生该类事件的那些事件 */
    set_bit(KEY_L, input->keybit);
    set_bit(KEY_S, input->keybit);
    set_bit(KEY_ENTER, input->keybit);
    set_bit(KEY_LEFTSHIFT, input->keybit);
    
    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);------------->③
    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpfdat = gpfcon + 1;
    gpgdat = gpgcon + 1;
    
    *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
    *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
    
    /* 注册中断处理函数 */
    for(i = 0; i < BUTTON_NUMS; i++)---------------------->④
        request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]);
    
    /* 注册input_dev结构体 */
    input_register_device(input);------------------------->⑤
    
    return 0;
}

① 初始化定时器,用于按键消抖获取稳定按键值使用

② 分配一个input_dev结构体,设置当前device能产生按键类事件,以及产生该类事件的那些按键值

③ 硬件相关操作:芯片相关gpio寄存器地址映射,设置连接按键的GPIO引脚为输入模式

④ 注册中断服务程序,设置中断触发方式以及中断发送时的中断服务函数

⑤ 注册input_dev结构体,找到内核中所有匹配的handler,并建立dev和handler直接连接

2、中断服务程序

static irqreturn_t button_irq_handle(int irq, void *dev_id)
{    
    pdesc = (struct button_desc *)dev_id;
    
    mod_timer(&button_timer, jiffies+HZ/100);
    
    return IRQ_RETVAL(IRQ_HANDLED);
}

按键按下时,由于按键内部机械弹片抖动引起GIPO口电平跳变进入中断服务程序,此时读取按键值为非有效值。为按键消抖,设置消抖时间,消抖时间到时在定时器的超时处理函数中获取有效按键值

3、定时器超时处理程序

void button_timer_handler(unsigned long data)
{
    unsigned char pin_val;
    unsigned int  key_sts;
    
    if(pdesc != NULL)
    {
        pin_val = gpio_get_value(pdesc->pin);---------------------------->①
        if(pin_val == 1)
        {
            /* 1-按下 */
            key_sts = 1;
        }
        else
        {
            /* 0-松开 */
            key_sts = 0;
        }
        input_event(input, EV_KEY, pdesc->key_val, key_sts);------------>②
        input_sync(input);---------------------------------------------->③
    }
}

① 读取触发中断的按键的GPIO的状态,确定按键是按下还是松开

② 调用input_event上报事件,input_event最终会调用handler中的event函数,event函数中将有效输入传送到handler中的相应存储区域,并唤醒因无数据而读取进入休眠的应用程序

③ 当提交输入设备产生的输入事件之后,需要调用此函数来通知输入子系统,以处理设备产生的完整事件

4、驱动程序测试

加载驱动程序,/dev目录下多出event1设备节点,该节点是device和内核中的handler匹配成功,调用handler中的connect函数input_class类下创建的设备。udev机制会使用这些信息在dev目录中创建相关节点

执行exec 0</dev/tty1命令,重定向shell程序的标准输入来自于/dev/tty1,这样按键产生的ls命令就能在中断显示了

 完整驱动程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <plat/gpio-fns.h>
#include <mach/gpio-nrs.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/poll.h>
#include <linux/input.h>

#define BUTTON_NUMS    4
#define IRQT_BOTHEDGE IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING

struct button_desc
{
    int pin;
    int irq_type;
    unsigned long flags;
    char *name;
    int key_val;
};

static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpgcon = NULL;
static volatile unsigned long *gpfdat = NULL;
static volatile unsigned long *gpgdat = NULL;



static struct timer_list button_timer;

static struct input_dev *input;


struct button_desc *pdesc = NULL;

static struct button_desc btn_desc[BUTTON_NUMS] = {
    {S3C2410_GPF(0),  IRQ_EINT0,  IRQT_BOTHEDGE, "S2", KEY_L},
    {S3C2410_GPF(2),  IRQ_EINT2,  IRQT_BOTHEDGE, "S3", KEY_S},
    {S3C2410_GPG(3),  IRQ_EINT11, IRQT_BOTHEDGE, "S4", KEY_ENTER},
    {S3C2410_GPG(11), IRQ_EINT19, IRQT_BOTHEDGE, "S5", KEY_LEFTSHIFT},
};


void button_timer_handler(unsigned long data)
{
    unsigned char pin_val;
    unsigned int  key_sts;
    
    if(pdesc != NULL)
    {
        pin_val = gpio_get_value(pdesc->pin);    
        if(pin_val == 1)
        {
            /* 1-按下 */
            key_sts = 1;
        }
        else
        {
            /* 0-松开 */
            key_sts = 0;
        }
        input_event(input, EV_KEY, pdesc->key_val, key_sts);
        input_sync(input);
    }
}

static irqreturn_t button_irq_handle(int irq, void *dev_id)
{    
    pdesc = (struct button_desc *)dev_id;
    
    mod_timer(&button_timer, jiffies+HZ/100);
    
    return IRQ_RETVAL(IRQ_HANDLED);
}
    
static int button_drv_init(void)
{
    unsigned int i = 0;
    
    init_timer(&button_timer);
    button_timer.expires = 0;
    button_timer.function = button_timer_handler;
    add_timer(&button_timer);

    /* 分配一个input_dev结构体 */
    input = input_allocate_device();
    
    /* 产生哪一类事件 */
    set_bit(EV_KEY, input->evbit);
    
    /* 产生该类事件的那些事件 */
    set_bit(KEY_L, input->keybit);
    set_bit(KEY_S, input->keybit);
    set_bit(KEY_ENTER, input->keybit);
    set_bit(KEY_LEFTSHIFT, input->keybit);
    
    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpfdat = gpfcon + 1;
    gpgdat = gpgcon + 1;
    
    *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
    *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
    
    /* 注册中断处理函数 */
    for(i = 0; i < BUTTON_NUMS; i++)
        request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]);
    
    /* 注册input_dev结构体 */
    input_register_device(input);
    
    return 0;
}

static void button_drv_exit(void)
{
    unsigned int i = 0;
    
    del_timer(&button_timer);
    
    for(i = 0; i < BUTTON_NUMS; i++)
        free_irq(btn_desc[i].irq_type, &btn_desc[i]);
    
    input_unregister_device(input);
    input_free_device(input);
    
    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");
input_dev
原文地址:https://www.cnblogs.com/053179hu/p/13799390.html