字符设备驱动开发模版

/*
* @Author: peter
* @Date:   2024-03-06 19:04:37
* @Last Modified by:   peter
* @Last Modified time: 2024-03-07 15:03:52
*/


#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 <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/device.h>

#define DTSGPIO_CNT          1            /* 设备号个数 */
#define DTSGPIO_NAME         "dtsgpio"     /* 名字 */

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *data_addr;
static void __iomem *dirm_addr;
static void __iomem *outen_addr;
static void __iomem *intdis_addr;
static void __iomem *aper_clk_ctrl_addr;

/* dtsgpio设备结构体 */
typedef struct Dtsgpio_dev {
    dev_t devid;            /* 设备号 */
    struct cdev cdev;       /* cdev */
    struct class *class;    /* 类 */
    struct device *device;  /* 设备 */
    int major;              /* 主设备号 */
    int minor;              /* 次设备号 */
    struct device_node *nd; /* 设备节点 */
}TDtsgpio_dev,*PTDtsgpio_dev;

TDtsgpio_dev  g_tDtsgpioDev;

static inline void gpio_ioremap(void)
{
    data_addr = of_iomap(g_tDtsgpioDev.nd, 0);
    dirm_addr = of_iomap(g_tDtsgpioDev.nd, 1);
    outen_addr = of_iomap(g_tDtsgpioDev.nd, 2);
    intdis_addr = of_iomap(g_tDtsgpioDev.nd, 3);
    aper_clk_ctrl_addr = of_iomap(g_tDtsgpioDev.nd, 4);
}

static inline void gpio_iounmap(void)
{
    iounmap(data_addr);
    iounmap(dirm_addr);
    iounmap(outen_addr);
    iounmap(intdis_addr);
    iounmap(aper_clk_ctrl_addr);
}


/*
 * @description         : 打开设备
 * @param – inode       : 传递给驱动的inode
 * @param - filp        : 设备文件,file结构体有个叫做private_data的成员变量
 *                        一般在open的时候将private_data指向设备结构体。
 * @return              : 0 成功;其他 失败
 */
static int gpio_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &g_tDtsgpioDev;   /* 设置私有数据 */
    return 0;
}

/*
 * @description         : 从设备读取数据 
 * @param - filp        : 要打开的设备文件(文件描述符)
 * @param - buf         : 返回给用户空间的数据缓冲区
 * @param - cnt         : 要读取的数据长度
 * @param - offt        : 相对于文件首地址的偏移
 * @return              : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t gpio_read(struct file *filp, char __user *buf,
            size_t cnt, loff_t *offt)
{
    return 0;
}

/*
 * @description         : 向设备写数据 
 * @param - filp        : 设备文件,表示打开的文件描述符
 * @param - buf         : 要写给设备写入的数据
 * @param - cnt         : 要写入的数据长度
 * @param - offt        : 相对于文件首地址的偏移
 * @return              : 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t gpio_write(struct file *filp, const char __user *buf,
            size_t cnt, loff_t *offt)
{
    int ret;
    int val;
    char kern_buf[1];

    ret = copy_from_user(kern_buf, buf, cnt);       // 得到应用层传递过来的数据
    if(0 > ret) {
        printk(KERN_ERR "kernel write failed!\r\n");
        return -EFAULT;
    }

    val = readl(data_addr);
    if (0 == kern_buf[0])
        val &= ~(0x1U << 12);            // 如果传递过来的数据是0则关闭led
    else if (1 == kern_buf[0])
        val |= (0x1U << 12);                     // 如果传递过来的数据是1则点亮led

    writel(val, data_addr);
    return 0;
}

/*
 * @description         : 关闭/释放设备
 * @param – filp        : 要关闭的设备文件(文件描述符)
 * @return              : 0 成功;其他 失败
 */
static int gpio_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations gpio_fops={
     .owner =THIS_MODULE,
     .open  =gpio_open,
     .write =gpio_write,
     .read  =gpio_read,
     .release =gpio_release,
};


static int __init gpio_init(void)
{
    const char *str;
    u32 val;
    int ret;

    g_tDtsgpioDev.nd = of_find_node_by_path("/gpio");
    if(NULL == g_tDtsgpioDev.nd) {
        printk(KERN_ERR "gpio node can not found!\r\n");
        return -EINVAL;
    }

    /* 2.读取status属性 */
    ret = of_property_read_string(g_tDtsgpioDev.nd, "status", &str);
    if(!ret) {
        if (strcmp(str, "okay"))
        return -EINVAL;
    }

    /* 3、获取compatible属性值并进行匹配 */
    ret = of_property_read_string(g_tDtsgpioDev.nd, "compatible", &str);
    if(0 > ret)
        return -EINVAL;

    if (strcmp(str, "xykj,gpio"))
        return -EINVAL;

    printk(KERN_ERR "gpio device matching successful!\r\n");

    /*4.寄存器内存映射*/
    gpio_ioremap();

    /* 5.使能GPIO时钟 */
    val = readl(aper_clk_ctrl_addr);
    val |= (0x1U << 24);
    writel(val, aper_clk_ctrl_addr);

    /* 6.关闭中断功能 */
    val |= (0x1U << 0);
    writel(val, intdis_addr);

    /* 7.设置GPIO为输出功能 */
    val = readl(dirm_addr);
    val |= (0x1U << 0);
    writel(val, dirm_addr);

    /* 8.使能GPIO输出功能 */
    val = readl(outen_addr);
    val |= (0x1U << 0);
    writel(val, outen_addr);

    /* 9.初始化LED的默认状态 */
    val = readl(data_addr);
    ret = of_property_read_string(g_tDtsgpioDev.nd, "default-state", &str);
    if(!ret) {
        if (!strcmp(str, "on"))
            val |= (0x1U << 0);
        else
            val &= ~(0x1U << 0);
    } else
        val &= ~(0x1U << 0);

    writel(val, data_addr);

    if (g_tDtsgpioDev.major)
    {
        g_tDtsgpioDev.devid=MKDEV(g_tDtsgpioDev.major,0);
        ret=register_chrdev_region(g_tDtsgpioDev.devid,DTSGPIO_CNT,DTSGPIO_NAME);
        if(ret)
            goto out1;
    }
    else
    {
        ret=alloc_chrdev_region( &g_tDtsgpioDev.devid,0,DTSGPIO_CNT,DTSGPIO_NAME);
        if(ret)
            goto out2;

        g_tDtsgpioDev.major = MAJOR(g_tDtsgpioDev.devid);
        g_tDtsgpioDev.minor = MINOR(g_tDtsgpioDev.devid);
    }
    
    printk("dtsgpio major=%d,minor=%d\r\n",g_tDtsgpioDev.major, g_tDtsgpioDev.minor);

     /* 初始化cdev */
    g_tDtsgpioDev.cdev.owner=THIS_MODULE;
    cdev_init(&g_tDtsgpioDev.cdev,&gpio_fops);

    /*添加一个cdev*/
    ret=cdev_add(&g_tDtsgpioDev.cdev,g_tDtsgpioDev.devid,DTSGPIO_CNT);
    if (ret)
        goto out2;


    /*创建类*/
    g_tDtsgpioDev.class=class_create(THIS_MODULE,DTSGPIO_NAME);
     if (IS_ERR(g_tDtsgpioDev.class)) {
        ret = PTR_ERR(g_tDtsgpioDev.class);
        goto out3;
    }   

    /*创建设备*/
    g_tDtsgpioDev.device = device_create(g_tDtsgpioDev.class, NULL,
                g_tDtsgpioDev.devid, NULL, DTSGPIO_NAME);
    if (IS_ERR(g_tDtsgpioDev.device)) {
        ret = PTR_ERR(g_tDtsgpioDev.device);
        goto out4;
    }

    printk("gpio_init successful!\r\n");
    return 0;

out4:
    class_destroy(g_tDtsgpioDev.class);

out3:
    cdev_del(&g_tDtsgpioDev.cdev);

out2:
    unregister_chrdev_region(g_tDtsgpioDev.devid, DTSGPIO_CNT);

out1:
    gpio_iounmap();

    return ret;
}

static void __exit gpio_exit(void)
{
    /* 注销设备 */
    device_destroy(g_tDtsgpioDev.class, g_tDtsgpioDev.devid);

    /* 注销类 */
    class_destroy(g_tDtsgpioDev.class);

    /* 删除cdev */
    cdev_del(&g_tDtsgpioDev.cdev);

    /* 注销设备号 */
    unregister_chrdev_region(g_tDtsgpioDev.devid, DTSGPIO_CNT);

    /* 取消地址映射 */
    gpio_iounmap();    
}

module_init(gpio_init);
module_exit(gpio_exit);

MODULE_AUTHOR("peter 554253547@qq.com");
MODULE_DESCRIPTION("xykj gpio drv!");
MODULE_LICENSE("GPL");

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值