zoukankan      html  css  js  c++  java
  • 驱动程序实例(四):按键驱动程序(platform + input子系统 + 外部中断方式)

    结合之前对Linux内核的platform总线与input子系统的分析 ,本文将编写基于platform总线和input子系统的Button设备的实例代码并对其进行分析。

    platform总线的分析,详见Linux platform驱动模型

    input子系统的分析,详见Linux字符设备驱动框架(四):Linux内核的input子系统

    硬件接口:

      CPU:s5pv210;

      Button的GPIO:GPIO_H0_2,EINT2;

      LED的工作方式:按键弹起,低电平;按键按下,高电平。

    1. device

    在/kernel/arch/arm/mach-s5pv210/include/mach目录下,建立一个buttons_gpio.h文件,并填充如下内容。

    #ifndef __ASM_ARCH_BUTTONSGPIO_H
    #define __ASM_ARCH_BUTTONSGPIO_H "buttons-gpio.h"
    
    //定义一个Button设备的数据结构
    struct s5pv210_button_platdata 
    {
        char            *name;
        unsigned int    gpio;
        unsigned int    irqnum;
        unsigned int    flags;
    };
    
    #endif

    在/kernel/arch/arm/mach-s5pv210/mach-x210.c下,添加如下内容,并添加对buttons_gpio.h的包含。

    /*Buttons*/
    
    static struct s5pv210_button_platdata s5pv210_button_pdata = 
    { .name
    = "button1", .gpio = S5PV210_GPH0(2), .irqnum = IRQ_EINT2,//中断号 .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//上升沿触发+下降沿触发 }; static struct platform_device s5pv210_button = { .name = "s5pv210_button", .id = 1, .dev = { .platform_data = &s5pv210_button_pdata, }, };

    将LED设备信息集成至smdkc110_devices,内核初始化时smdkc110_devices中的设备将被注册进内核。

    static struct platform_device *smdkc110_devices[] __initdata = 
    {
        ......
        &s5pv210_button,
    };

    2. driver

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/platform_device.h>
    #include <linux/gpio.h>
    #include <linux/slab.h>
    #include <linux/input.h> 
    
    #include <mach/buttons_gpio.h>
    #include <mach/hardware.h>
    #include <mach/regs-gpio.h>
    #include <mach/irqs.h>
    #include <linux/interrupt.h>
    
    static struct s5pv210_button_platdata *pdata;
    static struct input_dev *button_dev = NULL;
    
    static irqreturn_t button_interrupt(int irq, void *dummy) 
    { 
        int flag;
    
        s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0)); //设置GPIO为input模式
        flag = gpio_get_value(pdata->gpio);              //读取GPIO的值
        s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0f));//设置GPIO为eint2模式
    
        input_report_key(button_dev, KEY_LEFT, !flag);   //上报事件
        input_sync(button_dev);                          //同步事件
        return IRQ_HANDLED; 
    }
    
    
    static int s5pv210_button_remove(struct platform_device *dev)
    {
        input_free_device(button_dev);            //释放button_dev内存
        free_irq(pdata->irqnum, button_interrupt);//释放中断资源
        gpio_free(pdata->gpio);                   //释放GPIO
        
        return 0;
    }
    
    static int s5pv210_button_probe(struct platform_device *dev)
    {
        int ret;
        
        pdata = dev->dev.platform_data;
        
        /*****************************申请资源******************************/
        //申请GPIO
        ret = gpio_request(pdata->gpio, pdata->name);
        if (ret) 
        {
            printk(KERN_ERR "gpio_request failed, ret = %d.
    ", ret);
            return -EBUSY;
        } 
        
        //申请IRQ
        if (request_irq(pdata->irqnum, button_interrupt, pdata->flags, pdata->name, NULL)) 
        { 
            printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d
    ", pdata->irqnum);
            ret = -EBUSY;
            goto ERR_STER0;
        }
        
        
        /************************初始化GPIO资源*************************/
        s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_UP); //设置GPIO为上拉模式
        s3c_gpio_cfgpin(pdata->gpio, S3C_GPIO_SFN(0x0f));//设置GPIO为eint模式
        
        
        /*******************************创建接口*******************************/
        
        //申请button_dev内存空间
        button_dev = input_allocate_device();
        if(!button_dev)
        {
            ret = -ENOMEM;
            goto ERR_STER1;
        }
        
        //初始化button_dev
        set_bit(EV_KEY, button_dev->evbit);    //支持EV_KEY事件
        set_bit(KEY_LEFT, button_dev->keybit); //支持KEY_LEFT子事件
        
        //注册button_dev
        if(input_register_device(button_dev) != 0)
        {
            printk("s5pv210-button input register device fail!!
    ");
    
            ret = -ENODEV;
            goto ERR_STER2;
        }
    
        return 0;
        
        /****************************倒映式错误处理****************************/
    ERR_STER2:
        input_free_device(button_dev);
    
    ERR_STER1:
        free_irq(pdata->irqnum, button_interrupt);
        
    ERR_STER0:
        gpio_free(pdata->gpio);
        
        return ret;
    }
    
    //定义并初始化驱动信息
    static struct platform_driver s5pv210_button_driver = 
    {
        .probe        = s5pv210_button_probe,
        .remove        = s5pv210_button_remove,
        .driver        = 
        {
            .name    = "s5pv210_button",
            .owner    = THIS_MODULE,
        },
    };
    
    //注册驱动
    static int __init s5pv210_button_init(void)
    {
        return platform_driver_register(&s5pv210_button_driver);
    }
    
    //注销驱动
    static void __exit s5pv210_button_exit(void)
    {
        platform_driver_unregister(&s5pv210_button_driver);
    }
    
    module_init(s5pv210_button_init);
    module_exit(s5pv210_button_exit);
    
    MODULE_AUTHOR("Lin");
    MODULE_DESCRIPTION("S5PV210 BUTTON driver");
    MODULE_LICENSE("GPL");
    MODULE_ALIAS("platform:s5pv210_button");

    3. 测试

    编写一个简易的应用程序,来测试上述驱动程序。

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <linux/input.h>
    #include <string.h>
    
    #define X210_KEY            "/dev/input/event1"
    
    int main(void)
    {
        int fd = -1, ret = -1;
        struct input_event ev;
        
        //打开设备文件
        fd = open(X210_KEY, O_RDONLY);
        if (fd < 0)
        {
            perror("open");
            return -1;
        }
        
        while (1)
        {
            //读取一个event事件包
            memset(&ev, 0, sizeof(struct input_event));
            ret = read(fd, &ev, sizeof(struct input_event));
            if (ret != sizeof(struct input_event))
            {
                perror("read");
                close(fd);
                return -1;
            }
            
            //解析event包
            printf("-------------------------
    ");
            printf("type: %hd
    ", ev.type);
            printf("code: %hd
    ", ev.code);
            printf("value: %d
    ", ev.value);
            printf("
    ");
        }
        
        //关闭设备
        close(fd);
        
        return 0;
    }

    装载驱动模块后,在Linux终端运行该应用程序。在一次按键按下并弹起的过程中,终端中将打印出如下的信息,表明驱动程序工作正常。

    -------------------------//按键按下
    type: 1
    code: 105
    value: 1
    
    -------------------------//同步事件
    type: 0
    code: 0
    value: 0
    
    -------------------------//按键弹起
    type: 1
    code: 105
    value: 0
    
    -------------------------//同步事件
    type: 0
    code: 0
    value: 0
  • 相关阅读:
    linux-nginx
    mysql数据库的多实例与主从同步。
    MySQL的命令
    Mysql的管理
    linux之mariadb的安装
    Linux进程基础
    linux网络基础
    解锁HMC8及HMC9的root用户
    RHEL8.0-beta-1.ISO
    RHEL6误安装RHEL7的包导致glibc被升级后系统崩溃处理方法
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9468018.html
Copyright © 2011-2022 走看看