自动创建设备节点代码的实现

发布时间:2024年01月02日

一. 简介

上一篇文章简单学习了自动创建设备节点时,需要调用的函数。文章地址如下:

自动创建设备节点的方法说明-CSDN博客

这里在 前面所实现的 新字符设备驱动框架代码中,再添加自动创建设备节点的代码。

自动创建设备节点的效果:在驱动中实现自动创建设备节点的功能以后,使用 modprobe 命令加载驱动模块后,就会自动在 /dev 目录下 创建对应的设备文件。 而不需要我们自己手动输入命令创建设备节点。

二. 自动创建设备节点代码的实现

这里在 3_newchrled工程代码中,添加自动创建设备节点的代码。

注意:自动创建设备节点代码的实现,其实可以参考 Linux内核源码中一些驱动模块代码。

例如,可以在 NXP官方提供的 Linux内核源码中,搜索 class_create()函数的调用,这里我参考 Linux内核源码中 pg.c文件的代码。

自动创建设备节点的工作是在驱动模块入口函数中(一般是 xxx_init()函数中)完成的,一般在 cdev_add() 函数后面添加自动创建设备节点相关代码。

1.? 首先,添加 头文件与 class结构体,device结构体成员。

添加的头文件如下:

#include <linux/device.h>

添加的结构体如下:

因为创建类class_create()函数的返回值 为 class的结构体指针类型。而创建设备 device_create()函数返回值为 device的结构体指针类型。在之前 led灯结构体中添加:

//Led设备结构体
struct newchrled_dev{
    struct cdev led_cdev; 
    struct class * led_class;   //类(自动创建设备节点用)
    struct device * led_device; //设备(自动创建设备节点用) 
    dev_t dev_id;  //设备号
    int major;     //主设备号
    int minor;     //次设备号 
};

2.? 添加 创建类,创建设备代码,与摧毁类,摧毁设备的代码。

添加 创建类,创建设备的代码

/*驱动模块入口函数 */
static int __init newchrled_init(void)
{
.................................
/* 3.自动创建设备节点 */
    //创建类
    newchrled.led_class = class_create(THIS_MODULE, NEWCHRLED); 
    if (IS_ERR(newchrled.led_class)) {
		ret = PTR_ERR(newchrled.led_class);
		return ret;
	}   
    //创建设备
    newchrled.led_device = device_create(newchrled.led_class, NULL, newchrled.devid, NULL, NEWCHRLED);
    if (IS_ERR(newchrled.led_device)) {
		ret = PTR_ERR(newchrled.led_device);
		return ret;
	}  

    return 0;
}

添加摧毁类,摧毁设备的代码

/*驱动模块出口函数 */
static void __exit newchrled_exit(void)
{
......................

/*摧毁类与设备(自动创建设备节点时用) */
    //3. 摧毁设备
    device_destroy(newchrled.led_class, newchrled.devid);
    //4. 摧毁类
    class_destroy(newchrled.led_class);
}

三.? 代码实现

3_newchrled工程中,就是在 newchrled.c文件中添加 自动创建设备节点的代码。newchrled.c文件 添加后如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>

//寄存器的物理地址
#define  CCM_CCGR1_BASE          (0X020C406C)
#define  SW_MUX_GPIO1_IO03_BASE  (0X020E0068)
#define  SW_PAD_GPIO1_IO03_BASE  (0X020E02F4)
#define  GPIO1_GDIR_BASE         (0X0209C004)
#define  GPIO1_DR_BASE           (0X0209C000)

//地址映射后寄存器的虚拟地址指针
static void __iomem *  IMX6ULL_CCM_CCGR1;
static void __iomem *  IMX6ULL_SW_MUX_GPIO1_IO03;
static void __iomem *  IMX6ULL_SW_PAD_GPIO01_IO03;
static void __iomem *  IMX6ULL_GDIR;
static void __iomem *  IMX6ULL_DR;


#define  NEWCHRLED         "newchrled" //设备名
#define  NEWCHRLED_COUNT   1    //设备个数
#define  LED_OFF           0 //关闭Led灯
#define  LED_ON            1  //打开Led灯

//Led设备结构体
struct newchrled_dev{
    struct cdev led_cdev; 
    struct class * led_class;   //类(自动创建设备节点用)
    struct device * led_device; //设备(自动创建设备节点用) 
    dev_t devid;  //设备号
    int major;     //主设备号
    int minor;     //次设备号 
};

struct newchrled_dev newchrled;

void led_on_off(unsigned int status)
{
    unsigned int reg_value = 0;
    if(LED_ON == status) 
    {
        reg_value = readl(IMX6ULL_DR);
        reg_value &= ~(1 << 3);   //bit3清零,低电平,打开Led
        writel(reg_value, IMX6ULL_DR);
    }
    else if(LED_OFF == status)
    {
        reg_value = readl(IMX6ULL_DR);
        reg_value |= (1 << 3);  //bit3置1,高电平,关闭Led
        writel(reg_value, IMX6ULL_DR);
    }
}

static int newchrled_open(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t newchrled_write(struct file * inode, const char __user * buf, size_t count, loff_t * ppos)
{
    int ret = 0;
    unsigned char data_buf[1] = {0};
    ret = copy_from_user(data_buf, buf, count);
    if(ret < 0)
    {
        printk("led_write failed!\r\n");
        return -EFAULT;
    }

    led_on_off(data_buf[0]);
    return 0;
}

int newchrled_release(struct inode * inode, struct file * file)
{
    printk("led_release\r\n");
    return 0;
}

//字符设备的函数集
const struct file_operations fops = {  
    .owner = THIS_MODULE,
    .open = newchrled_open,
    .write = newchrled_write,
    .release = newchrled_release,
};

/*驱动模块入口函数 */
static int __init newchrled_init(void)
{
    int ret = 0;
    unsigned int value = 0;
    printk("newchrled_init!\r\n");

/* 1. Led灯的IO初始化 */
    //地址映射
    IMX6ULL_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    IMX6ULL_SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    IMX6ULL_SW_PAD_GPIO01_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    IMX6ULL_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
    IMX6ULL_DR = ioremap(GPIO1_DR_BASE, 4);

    //使能Led时钟
    value = readl(IMX6ULL_CCM_CCGR1);
    value &= ~(3 << 26);
    value |= (3 << 26); //bit26~27置1
    writel(value, IMX6ULL_CCM_CCGR1);
    //复用为GPIO功能
    writel(0X05, IMX6ULL_SW_MUX_GPIO1_IO03);
    //配置电气属性
    writel(0X10B0, IMX6ULL_SW_PAD_GPIO01_IO03);
    //设置为输出功能
    value = readl(IMX6ULL_GDIR);
    value |= (1 << 3);
    writel(value, IMX6ULL_GDIR);

    //关闭Led灯(设置为高电平)
    value = readl(IMX6ULL_DR);
    value |= (1 << 3);
    writel(value, IMX6ULL_DR);

/* 2. 注册字符设备 */
    //设备号的分配
    if(newchrled.major) //给定主设备号
    {
        newchrled.devid = MKDEV(newchrled.major, 0);
        ret = register_chrdev_region(newchrled.devid, NEWCHRLED_COUNT, NEWCHRLED);
    }
    else{ //没有给定主设备号
        ret = alloc_chrdev_region(&(newchrled.devid), 0, NEWCHRLED_COUNT, NEWCHRLED);	
        newchrled.major = MAJOR(newchrled.devid);
        newchrled.minor = MINOR(newchrled.devid);
    }
	if (ret < 0) {
		printk("register-chrdev failed!\n");
		return -1;
	}
    //初始化设备
    cdev_init(&newchrled.led_cdev, &fops);
    
    //注册设备
    cdev_add(&newchrled.led_cdev, newchrled.devid, NEWCHRLED_COUNT);

/* 3.自动创建设备节点 */
    //创建类
    newchrled.led_class = class_create(THIS_MODULE, NEWCHRLED); 
    if (IS_ERR(newchrled.led_class)) {
		ret = PTR_ERR(newchrled.led_class);
		return ret;
	}   
    //创建设备
    newchrled.led_device = device_create(newchrled.led_class, NULL, newchrled.devid, NULL, NEWCHRLED);
    if (IS_ERR(newchrled.led_device)) {
		ret = PTR_ERR(newchrled.led_device);
		return ret;
	}  

    return 0;
}

/*驱动模块出口函数 */
static void __exit newchrled_exit(void)
{
    printk("newchrled_eixt!\r\n");
/*注销字符设备*/
    //1. 删除设备
    cdev_del(&newchrled.led_cdev);
    //2. 注销设备号
    unregister_chrdev_region(newchrled.devid, NEWCHRLED_COUNT);

/*摧毁类与设备(自动创建设备节点时用) */
    //3. 摧毁设备
    device_destroy(newchrled.led_class, newchrled.devid);
    //4. 摧毁类
    class_destroy(newchrled.led_class);
}

/* 注册与卸载驱动设备 */
module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXueWu");

经过编译,可以生成 newchrled.ko 驱动文件。可以拷贝到开发板的根文件系统下进行测试。

下一篇测试该驱动程序,是否在加载 驱动模块后,就在 /dev/目录下自动生成相应的设备节点。

当卸载驱动程序后,是否随着卸载驱动模块的执行,也可以卸载 /dev/目录下的节点。

文章来源:https://blog.csdn.net/wojiaxiaohuang2014/article/details/135341000
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。