Rk3568驱动开发_自动创建设备节点_8
- 电脑硬件
- 2025-09-20 10:36:02

1.设备节点
之前已经说过设备节点是驱动挂载后以文件形式显示在dev目录下,供给open函数打开,每次挂载后都要重新创建十分麻烦,于是替代方式是,驱动挂载后自动创建,驱动卸载后自动卸载
核心代码:
// 自动创建设备节点 newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME); if(IS_ERR(newchrled.class)) return PTR_ERR(newchrled.class); // 先创建类再创建设备 newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME); // 创建设备 if(IS_ERR(newchrled.device)) return PTR_ERR(newchrled.device); // 注释设备 device_destroy(newchrled.class, newchrled.devid); // 注销类 class_destroy(newchrled.class);先创建类,再创建设备
在 Linux 驱动开发中,通过 class_create 和 device_create 函数自动在 /dev 下创建设备节点的过程,本质上是利用了内核的 sysfs 文件系统 和 udev 用户空间守护进程 的协同机制。
调用 class_create 会为设备驱动创建一个类(Class),该类的信息会被注册到 /sys/class 目录下。例如,代码中 NEWCHRLED_NAME 对应的类会在 /sys/class 下生成一个同名目录(如 /sys/class/newchrled) 类为设备提供了一种逻辑分组方式,例如所有 LED 设备可以归入 leds 类。 类的信息会通过 sysfs 暴露给用户空间,udev 可以通过这些信息动态管理设备节点
device_create 会在已创建的类下生成一个设备实例,并在 /sys/class/newchrled 目录中创建对应的子目录(如 /sys/class/newchrled/newchrled),其中包含设备的主次设备号(dev 文件)、设备属性等信息 当 device_create 执行时,内核会向用户空间发送 uevent 事件,通知 udev 有新的设备被注册。udev 会监听这些事件,并根据 /sys/class 下的设备信息动态创建或删除 /dev 中的设备节点 dev 会根据 /sys/class/newchrled/newchrled 中的属性(如设备号、设备名)执行预定义的规则(位于 /etc/udev/rules.d),最终调用 mknod 在 /dev 下生成设备节点
完整代码:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/cdev.h> #include <linux/device.h> #define NEWCHRLED_NAME "newchrled" #define NEWCHRLED_COUNT 1 // 实际地址 #define PMU_GRF_BASE (0xFDC20000) #define PMU_GRF_GPIO0C_IOMUX_L (PMU_GRF_BASE + 0x0010) #define PMU_GRF_GPIO0C_DS_0 (PMU_GRF_BASE + 0X0090) #define GPIO0_BASE (0xFDD60000) #define GPIO0_SWPORT_DR_H (GPIO0_BASE + 0X0004) #define GPIO0_SWPORT_DDR_H (GPIO0_BASE + 0X000C) #define LEDOPEN 1 #define LEDCLOSE 0 /* 映射后的寄存器虚拟地址指针 */ static void __iomem *PMU_GRF_GPIO0C_IOMUX_L_PI; static void __iomem *PMU_GRF_GPIO0C_DS_0_PI; static void __iomem *GPIO0_SWPORT_DR_H_PI; static void __iomem *GPIO0_SWPORT_DDR_H_PI; // led gpio初始化操作 void gpio_init(void){ u32 val = 0; // 设置GPIO0_c0为GPIO功能 val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI); val &= ~(0x7 << 0); //最低三位置0 val |= ((0x7 << 16) | (0x0 << 0)); // 16 17 18位置1其他不变,bit2:0:0,用作GPIO0_C0 writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI); // 设置GPIO_C0驱动能力为level5 val = readl(PMU_GRF_GPIO0C_DS_0_PI); val &= ~(0x3f << 0); // 0 ~ 5置0 val |= ((0x3f << 16) | (0x3f << 0)); // 16 ~ 21置1,0~5置1同时用作GPIO0c0 writel(val, PMU_GRF_GPIO0C_DS_0_PI); // 设置GPIOO0_c0为输出 val = readl(GPIO0_SWPORT_DDR_H_PI); val &= ~(0x1 << 0); // 0置0 val |= ((0x1 << 16) | (0x1 << 0)); // 16置1,0置1 writel(val, GPIO0_SWPORT_DDR_H_PI); // 设置GPIO_c0为低电平,关闭LED val = readl(GPIO0_SWPORT_DR_H_PI); val &= ~(0x1 << 0); val |= ((0x1 << 16) | (0x0 << 0)); writel(val, GPIO0_SWPORT_DR_H_PI); } // 开关 void led_switch(int status){ u32 val = 0; if(status == LEDOPEN){ // 开灯 val = readl(GPIO0_SWPORT_DR_H_PI); val &= ~(0X1 << 0); /* bit0 清零*/ val |= ((0X1 << 16) | (0X1 << 0)); /* bit16 置1,允许写bit0, bit0,高电平*/ writel(val, GPIO0_SWPORT_DR_H_PI); }else if(status == LEDCLOSE){ // 关灯 val = readl(GPIO0_SWPORT_DR_H_PI); val &= ~(0X1 << 0); /* bit0 清零*/ val |= ((0X1 << 16) | (0X0 << 0)); /* bit16 置1,允许写bit0, bit0,低电平 */ writel(val, GPIO0_SWPORT_DR_H_PI); } } // 真实物理地址映射虚拟内存函数 void led_remap(void){ PMU_GRF_GPIO0C_IOMUX_L_PI = ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4); PMU_GRF_GPIO0C_DS_0_PI = ioremap(PMU_GRF_GPIO0C_DS_0, 4); GPIO0_SWPORT_DR_H_PI = ioremap(GPIO0_SWPORT_DR_H, 4); GPIO0_SWPORT_DDR_H_PI = ioremap(GPIO0_SWPORT_DDR_H, 4); } // 释放 void led_releaseMap(void){ // 取消地址映射 iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI); iounmap(PMU_GRF_GPIO0C_DS_0_PI); iounmap(GPIO0_SWPORT_DR_H_PI); iounmap(GPIO0_SWPORT_DDR_H_PI); } /*LED 设备结构体*/ struct newchrled_dev{ struct cdev cdev; // 字符设备 dev_t devid; // 设备号 struct class* class; // 类 struct device* device; //设备 int major; // 主设备号 int minor; // 次设备号 }; struct newchrled_dev newchrled; // led设备 static int newchrled_open(struct inode* inode, struct file* filp){ return 0; } static int newchrled_release(struct inode* inode, struct file* filp){ return 0; } static ssize_t newchrled_write(struct file* filp, const char __user* buf, size_t count, loff_t* ppos){ int ret; unsigned char databuf[1]; unsigned char state; ret = copy_from_user(databuf, buf, count); if(ret < 0){ printk("kernel write failed\r\n"); return -EFAULT; } state = databuf[0]; if(state == LEDOPEN){ led_switch(LEDOPEN); }else if(state == LEDCLOSE){ led_switch(LEDCLOSE); } return 0; } static const struct file_operations newchrled_fops = { .owner = THIS_MODULE, .write = newchrled_write, .open = newchrled_open, .release = newchrled_release, }; /*入口*/ static int __init newchrled_init(void){ int ret = 0; printk("new chrled init\r\n"); /*初始化虚拟内存,初始化gpio*/ led_remap(); gpio_init(); // led初始化 if(newchrled.major){ // 给定主设备号 newchrled.devid = MKDEV(newchrled.major, 0); // 次设备号从零开始 ret = register_chrdev_region(newchrled.devid, NEWCHRLED_COUNT, NEWCHRLED_NAME); // 注册一个 }else{ // 自己申请 ret = alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_COUNT, NEWCHRLED_NAME); //由0开始申请1个 newchrled.major = MAJOR(newchrled.devid); newchrled.minor = MINOR(newchrled.devid); } if(ret < 0){ printk("newchrled chrdev_region err\r\n"); return -1; } printk("newchrled major = %d, minor= %d\r\n", newchrled.major, newchrled.minor); newchrled.cdev.owner = THIS_MODULE; cdev_init(&newchrled.cdev, &newchrled_fops); // 初始化完 ret = cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_COUNT); // 自动创建设备节点 newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME); if(IS_ERR(newchrled.class)) return PTR_ERR(newchrled.class); // 先创建类再创建设备 newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME); // 创建设备 if(IS_ERR(newchrled.device)) return PTR_ERR(newchrled.device); return 0; } /*出口*/ static void __exit newchrled_exit(void){ printk("new chrled exit\r\n"); led_releaseMap(); // 删除字符设备 cdev_del(&newchrled.cdev); // 注销设备号 unregister_chrdev_region(newchrled.devid, NEWCHRLED_COUNT); // 注释设备 device_destroy(newchrled.class, newchrled.devid); // 注销类 class_destroy(newchrled.class); } /* 注册驱动和卸载驱动*/ module_init(newchrled_init); module_exit(newchrled_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("narnat"); 2.补充:不声明GPL,未声明许可证的内核模块会被内核视为“专有”(Proprietary),加载时触发警告并标记内核为“被污染”(Tainted Kernel)状态,有些东西可能会无法使用
Rk3568驱动开发_自动创建设备节点_8由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Rk3568驱动开发_自动创建设备节点_8”
上一篇
C#上位机--三元运算符