Linux学习笔记(17.1)——基于platform设备驱动的按键驱动

  1. 字符设备驱动程序设计思路
    字符设备驱动程序编写思路(参考韦东山老师) 基于platform平台的设备驱动的关键在于,1)将驱动程序分为与具体硬件驱动分为上下两层,2)并将下层的具体硬件驱动分离为资源和相关的SOC驱动。本文为便于理解分层、分离的概念,将驱动 分成了3个C文件:button_drv.c、imx6_io_drv.c、atk_btn_dev.c以及2个头文件:button_drv.h、imx6_io_drv.h。下面分别列出代码
  2. 按键设备驱动(框架)文件
    button_drv.c文件实现与硬件有关的按键驱动,主要实现struct file_operations类型相关函数及其实例的注册/解除注册。其中的.read、.open、.release成员要调用下层驱动:
/**
 * 文件    : button_drv.c
 * 作者    : glen  
 * 描述    : button driver文件
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "button_drv.h"

struct button_drv btn_drv = {
    .hw_ops = NULL,
    .name = "gbtn",
    .major = 0,
};

ssize_t button_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
    int ret;
    int minor = iminor(filp->f_inode);
    struct btn_hw_ops *ops = (struct btn_hw_ops *)filp->private_data;
    //struct btn_hw_ops *ops = btn_drv.hw_ops;
    char kval;

    size = (size >= 1) ? 1 : 0;
    if (ops == NULL) {
        printk("Please register button hardware operations instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EIO;
    }

    if (minor >= ops->num) {
        printk("Reading button value error %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EINVAL;
    }

    if (ops->read(minor, &kval) == 0) {
        ret = copy_to_user(buf, &kval, size);
        printk("Read button value successfully:");
        return size;
    }
    return 0;
}

int button_drv_open (struct inode *nd, struct file *filp)
{
    int minor = iminor(nd);
    struct btn_hw_ops *ops = btn_drv.hw_ops;
    filp->private_data = btn_drv.hw_ops;

    if (ops == NULL) {
        printk("Please register button hardware operations instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EIO;
    }

    if (minor >= ops->num) {
        printk("Openning button driver error %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EINVAL;
    }

    if (ops->open(minor) == 0)
        printk("Open button driver successfully:");

    return 0;
}

int button_drv_release (struct inode *nd, struct file *filp)
{
    int minor = iminor(nd);
    struct btn_hw_ops *ops = (struct btn_hw_ops *)filp->private_data;

    if (ops == NULL) {
        printk("Please register button hardware operations instance %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EIO;
    }

    if (minor >= ops->num) {
        printk("Closing button driver error %s %s, %d\n", __FILE__, __FUNCTION__, __LINE__);
        return -EINVAL;
    }

    if (ops->close(minor) == 0) {
        printk("Close button driver successfully:");
        filp->private_data = NULL;
    }

    return 0;
}

unsigned int button_drv_poll (struct file *filp, struct poll_table_struct * wait)
{
    return 0;
}

static struct file_operations button_drv_ops = {
    .owner   = THIS_MODULE,
    .read    = button_drv_read,
    .open    = button_drv_open,
    .release = button_drv_release,
    .poll    = button_drv_poll,
};

static int __init button_drv_init(void)
{
    btn_drv.major = register_chrdev(btn_drv.major, btn_drv.name, &button_drv_ops);
    btn_drv.class = class_create(THIS_MODULE, btn_drv.name);
    if (IS_ERR(btn_drv.class)) {
        printk("Class create error!\n");
        unregister_chrdev(btn_drv.major, btn_drv.name);
    }
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}
module_init(button_drv_init);

static void __exit button_drv_exit(void)
{
    /* device_destroy */
    class_destroy(btn_drv.class);
    unregister_chrdev(btn_drv.major, btn_drv.name);
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}
module_exit(button_drv_exit);

int button_hw_ops_register(struct btn_hw_ops *ops)
{
    if (btn_drv.hw_ops)
        return -EIO;

    btn_drv.hw_ops = ops;
    printk("Register button hardware operations instance successfully\n");
    return 0;
}
EXPORT_SYMBOL(button_hw_ops_register);

void button_hw_ops_unregister(struct btn_hw_ops *ops)
{
    if (btn_drv.hw_ops)
        btn_drv.hw_ops = NULL;
    printk("Register button hardware operations instance successfully\n");
}
EXPORT_SYMBOL(button_hw_ops_unregister);

void button_device_create(int num)
{
    int i;
    for (i = 0; i < num; i++) 
        device_create(btn_drv.class, NULL, MKDEV(btn_drv.major, i), NULL, "gbtn%d", i);
}
EXPORT_SYMBOL(button_device_create);

void button_device_destroy(int num)
{
    int i;
    for (i = 0; i < num; i++)
        device_destroy(btn_drv.class, MKDEV(btn_drv.major, i));
}
EXPORT_SYMBOL(button_device_destroy);

/* insert author information for module */
MODULE_AUTHOR("glen");
/* insert license for module */
MODULE_LICENSE("GPL");
其中包含的button_drv.h文件提供了与下层soc驱动共用的结构体,注册/销毁soc驱动等接口函数,以便在button_drv.c文件中调用——驱动上层和下层都依懒于接口。
/**
 * 文件    : button_drv.h
 * 作者    : glen  
 * 描述    : button driver头文件
 */
#ifndef __BUTTON_DRV_H__
#define __BUTTON_DRV_H__

struct btn_hw_ops {
    u32 num;
    int (* read) (int idx, char *kval);
    int (* open) (int idx);
    int (* close) (int idx);
};

struct button_drv {
    struct btn_hw_ops *hw_ops;
    struct class      *class;
    char  *name;
    int major;
};

int button_hw_ops_register(struct btn_hw_ops *ops);
void button_hw_ops_unregister(struct btn_hw_ops *ops);
void button_device_create(int num);
void button_device_destroy(int num);

#endif // !__BUTTON_DRV_H__
  1. SOC相关的IO驱动文件
    imx6_io_drv.c获取IO资源(这里获取的是按键的IO信息),并将资源用于实现按键的open、read、close等操作:
/**
 * 文件    : imx6_io_drv.c
 * 作者    : glen  
 * 描述    : imx6ull io driver文件
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "button_drv.h"
#include "imx6_io_drv.h"

static struct btn_dev_res __btn_dev_res[16];

static int imx6_io_read  (int idx, char *kval);
static int imx6_io_open  (int idx);
static int imx6_io_close (int idx);

static struct btn_hw_ops btn_hw_oper = {
    .open  = imx6_io_open,
    .read  = imx6_io_read,
    .close = imx6_io_close,
};

static int imx6_io_read  (int idx, char *kval)
{
    int val = 0;
    struct btn_dev_res *pbtn;

    if (idx >= btn_hw_oper.num) 
        return -EINVAL;
    pbtn = &__btn_dev_res[idx];
    val = readl(pbtn->vir_addr.gpio_dr);
    if  (pbtn->active_level) {
        if (val & (1 << pbtn->pin_num))
            *kval = 1;
        else 
            *kval = 0;
    } else {
        if (val & (1 << pbtn->pin_num))
            *kval = 0;
        else 
            *kval = 1;
    }
    return 0;
}

static int imx6_io_open  (int idx)
{
    int val = 0;
    struct btn_dev_res *pbtn;

    if (idx >= btn_hw_oper.num) 
        return -EINVAL;
    pbtn = &__btn_dev_res[idx];

    pbtn->vir_addr.ccm_ccgr = ioremap((phys_addr_t)(pbtn->phy_addr.ccm_ccgr), 4);
    pbtn->vir_addr.sw_mux   = ioremap((phys_addr_t)(pbtn->phy_addr.sw_mux  ), 4);
    pbtn->vir_addr.sw_pad   = ioremap((phys_addr_t)(pbtn->phy_addr.sw_pad  ), 4);
    pbtn->vir_addr.gpio_dr  = ioremap((phys_addr_t)(pbtn->phy_addr.gpio_dr ), 4);
    pbtn->vir_addr.gpio_dir = ioremap((phys_addr_t)(pbtn->phy_addr.gpio_dir), 4);

    val = readl(pbtn->vir_addr.ccm_ccgr);
    val |= (3 << pbtn->clk_bits);
    writel(val, pbtn->vir_addr.ccm_ccgr);

    val = readl(pbtn->vir_addr.sw_mux);
    val &= ~(0x0f);
    val |= 5;
    writel(val, pbtn->vir_addr.sw_mux);

    writel(0x10B0, pbtn->vir_addr.sw_pad);

    val = readl(pbtn->vir_addr.gpio_dir);
    val &= ~(1 << pbtn->pin_num);
    writel(val, pbtn->vir_addr.gpio_dir);

    printk("pbtn->phy_addr.ccm_ccgr = %x \n", (phys_addr_t)(pbtn->phy_addr.ccm_ccgr));

    return 0;
}

static int imx6_io_close (int idx)
{
    struct btn_dev_res *pbtn;

    if (idx >= btn_hw_oper.num) 
        return -EINVAL;
    pbtn = &__btn_dev_res[idx];

    iounmap(pbtn->vir_addr.ccm_ccgr);
    iounmap(pbtn->vir_addr.sw_mux  );
    iounmap(pbtn->vir_addr.sw_pad  );
    iounmap(pbtn->vir_addr.gpio_dr );
    iounmap(pbtn->vir_addr.gpio_dir);

    return 0;
}

int btn_hw_drv_probe (struct platform_device *pdev)
{
    int i = 0;
    struct resource *res;

    /* 获取资源 */
    while (1) {
        res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
        if (res == NULL) 
            break;

        memcpy(&__btn_dev_res[i], (const void *)res->start, sizeof(struct btn_dev_res));
        i++;
    }

    if (i == 0) {
        dev_err(&pdev->dev, "Cannot find IO resource\n");
        return -ENOENT;
    }

    btn_hw_oper.num = i;
    button_hw_ops_register(&btn_hw_oper);
    button_device_create(btn_hw_oper.num);
    
    return 0;
}

int btn_hw_drv_remove(struct platform_device *pdev)
{
    button_hw_ops_unregister(NULL);
    button_device_destroy(btn_hw_oper.num);
    btn_hw_oper.num = 0;

    return 0;
}

static struct platform_driver btn_hw_drv = {
    .driver = {
        .name = "gbtn",
    },
    .probe = btn_hw_drv_probe,
    .remove = btn_hw_drv_remove,
};

static int __init imx6_io_drv_init(void)
{
    int ret;
    ret = platform_driver_register(&btn_hw_drv);
    return ret;
}
module_init(imx6_io_drv_init);

static void __exit imx6_io_drv_exit(void)
{
    platform_driver_unregister(&btn_hw_drv);
}
module_exit(imx6_io_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("glen");
其中包含的头文件button_drv.h和imx6_io_drv.h,前者上面已说明不再赘述;后者为soc驱动和资源文件提供按键设备资源结构体:
/**
 * 文件    : imx6_io_drv.h
 * 作者    : glen  
 * 描述    : imx6ull io driver头文件
 */
#ifndef __IMX6_IO_DRV_H__
#define __IMX6_IO_DRV_H__

#include <linux/types.h>

struct io_reg {
    void *ccm_ccgr;
    void *sw_mux  ;
    void *sw_pad  ;
    void *gpio_dr ;
    void *gpio_dir;
};

/* 按键设备资源 */
struct btn_dev_res {
    struct io_reg phy_addr;
    struct io_reg vir_addr;
    u8 active_level;
    u8 clk_bits;
    u8 pin_num;
};

#endif // !__IMX6_IO_DRV_H__
  1. 按键相关资源文件
    atk_btn_dev.c提供了具体目标板的按键相关资源:
/**
 * 文件    : atk_btn_dev.c
 * 作者    : glen  
 * 描述    : button device文件
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/platform_device.h>

#include "imx6_io_drv.h"

static struct btn_dev_res atk_btn_res[] = {
    {
        .phy_addr = {
            .ccm_ccgr   = (0x020C406C),
            .sw_mux     = (0x020E008C),
            .sw_pad     = (0x020E0318),
            .gpio_dr    = (0x0209C000),
            .gpio_dir   = (0x0209C004),
        },
        .active_level = 0,
        .clk_bits = 26,
        .pin_num = 18,
    }, {
        .phy_addr = {
            .ccm_ccgr   = (0x020C406C),
            .sw_mux     = (0x020E0068),
            .sw_pad     = (0x020E02F4),
            .gpio_dr    = (0x0209C000),
            .gpio_dir   = (0x0209C004),
        },
        .active_level = 1,
        .clk_bits = 26,
        .pin_num = 3,
    },    
};

static struct resource btn_res[] = {
    {
        .name = "pin_res",
        .start = (resource_size_t)&atk_btn_res[0],
        .flags = IORESOURCE_IRQ,
    }, {
        .name = "pin_res",
        .start = (resource_size_t)&atk_btn_res[1],
        .flags = IORESOURCE_IRQ,
    }, 
};

static struct platform_device atk_btn_dev = {
    .name = "gbtn",
    .resource = btn_res,
    .num_resources = ARRAY_SIZE(btn_res),
};

static int __init atk_btn_dev_init(void)
{
    platform_device_register(&atk_btn_dev);
    return 0;
}
module_init(atk_btn_dev_init);

static void __exit atk_btn_dev_exit(void)
{
    platform_device_unregister(&atk_btn_dev);
}
module_exit(atk_btn_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("glen");
  1. 应用程序
/*
 * 文件名   :  button_drv_test.c
 * 作者     :  glen
 * 描述     :  button_drv应用程序
 */

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

/**
 * @brief   : main函数
 * @par     : argc  argv数组元素的个数
 *            argv  参数数组
 * @retval  : 0 成功    其它 失败
 */
int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    char kval;

    if (argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    while (1) {
        ret = read(fd, &kval, 1);
        if (ret <= 0) {
            printf("Read glen button value failed!\r\n");
            close(fd);
            return -1;
        } else {
            printf("The glen button value is: %d!\r\n", kval);
        }
        sleep(1);
    }

    /* 关闭文件 */
    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}
  1. 在alientek_linux_alpha开发板实测验证
/home/glen/linux/imx6ull/glen/3_1_button/button_drv.c button_drv_exit line 130
/drv_module # insmod button_drv.ko
 /home/glen/linux/imx6ull/glen/3_1_button/button_drv.c button_drv_init line 120
/drv_module # insmod atk_btn_dev.ko
/drv_module # insmod imx6_io_drv.ko
Register button hardware operations instance successfully
/drv_module # ./btn_drv_test /dev/gbtn0
pbtn->phy_addr.ccm_ccgr = 20c406c
Open button driver successfully:Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 0!
Read button value successfully:The glen button value is: 1!
Read button value successfully:The glen button value is: 1!
Read button value successfully:The glen button value is: 1!
Read button value successfully:The glen button value is: 1!
Read button value successfully:The glen button value is: 1!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值