- register_chrdev 和 unregister_chrdev 这两个函数是老版本驱动使用的函数,新字符设备驱动使用了cdev
- image.png
- unregister_chrdev = cdev_del + unregister_chrdev_region :
- 在 Linux 中使用 cdev 结构体表示一个字符设备,cdev 结构体在 include/linux/cdev.h 文件中的定义如下:
- image.png
- 注册字符设备驱动
- 字符设备注册 static int __init led_init(void)
- 0、创建设备结构体及私有化
-
/* newchrled设备结构体 */ struct newchrled_dev{ dev_t devid; /* 设备号 */ struct cdev cdev; /* cdev */ struct class *class; /* 类 */ struct device *device; /* 设备 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ }; struct newchrled_dev newchrled; /* led设备 */
- led_open()中把led设备结构体设置为私有数据
-
//编写驱动 open 函数的时候将设备结构体作为私有数据添加到设备文件中 static int led_open(struct inode *inode, struct file *filp) { filp->private_data = &newchrled; /* 设置私有数据 */ return 0; }
- 在 open 函数里面设置好私有数据以后,在 write、read、close 等函数中直接读取 private_data即可得到设备结构体。
-
static int led_release(struct inode *inode, struct file *filp) { struct newchrled_dev *dev = filp->private_data; printk("newcheled major=%d,minor=%d\r\n",&dev->major, &dev->minor); return 0; }
- 1、创建设备号
-
/* 1、创建设备号 */ if (newchrled.major) { /* 定义了设备号 */ newchrled.devid = MKDEV(newchrled.major, 0); register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME); } else { /* 没有定义设备号 */ alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */ newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */ newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */ } printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor);
- 2、初始化cdev
-
/* 2、初始化cdev */ newchrled.cdev.owner = THIS_MODULE; cdev_init(&newchrled.cdev, &newchrled_fops); //参数 cdev 就是要初始化的 cdev 结构体变量,参数 fops 就是字符设备文件操作函数集合
- 3、添加一个cdev
-
/* 3、添加一个cdev */ cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
- 自动创建设备节点
- 4、创建类
-
/* 4、创建类 */ newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME); if (IS_ERR(newchrled.class)) { return PTR_ERR(newchrled.class); }
- 5、创建设备
-
/* 5、创建设备 */ newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME); if (IS_ERR(newchrled.device)) { return PTR_ERR(newchrled.device); }
- 初始化字符设备
- 初始化字符设备 也在static int __init led_init(void)
- 0、物理地址映射到虚拟地址
-
/* 寄存器物理地址 */ #define CCM_CCGR1_BASE (0X020C406C) #define SW_MUX_GPIO1_IO03_BASE (0X020E0068) #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) #define GPIO1_DR_BASE (0X0209C000) #define GPIO1_GDIR_BASE (0X0209C004) /* 映射后的寄存器虚拟地址指针 */ static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR;
-
u32 val = 0; /* 初始化LED */ /* 1、寄存器地址映射 */ IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);//4个byte,为IMX6U_CCM_CCGR1分配32位虚拟地址 SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4); GPIO1_DR = ioremap(GPIO1_DR_BASE, 4); GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
- 1、使能GPIO1时钟
-
/* 2、使能GPIO1时钟 */ val = readl(IMX6U_CCM_CCGR1); val &= ~(3 << 26); /* 清楚以前的设置 */ val |= (3 << 26); /* 设置新值 */ writel(val, IMX6U_CCM_CCGR1);
- 2、设置GPIO1_IO03的复用功能,将其复用为GPIO1_IO03,并设置IO属性
-
/* 3、设置GPIO1_IO03的复用功能,将其复用为 * GPIO1_IO03,最后设置IO属性。 */ writel(5, SW_MUX_GPIO1_IO03);
-
/*寄存器SW_PAD_GPIO1_IO03设置IO属性 *bit 16:0 HYS关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper功能 *bit [12]: 1 pull/keeper使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度100Mhz *bit [5:3]: 110 R0/6驱动能力 *bit [0]: 0 低转换率 */ writel(0x10B0, SW_PAD_GPIO1_IO03);
- 3、设置GPIO1_IO03为输出功能
-
val = readl(GPIO1_GDIR); val &= ~(1 << 3); /* 清除以前的设置 */ val |= (1 << 3); /* 设置为输出 */ writel(val, GPIO1_GDIR);
- 4、关闭LED
-
val = readl(GPIO1_DR); val |= (1 << 3); writel(val, GPIO1_DR);
- 映射函数ioremap和取消映射函数iounmap
- 注销字符设备 也在static void __exit led_exit(void)
-
static void __exit led_exit(void) { /* 取消映射 */ iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); /* 注销字符设备驱动 */ cdev_del(&newchrled.cdev);/* 删除cdev */ unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */ device_destroy(newchrled.class, newchrled.devid); class_destroy(newchrled.class); }
- 物理地址就是,机器内主存的地址,包括RAM和ROM(寄存器)
虚拟地址就是,cpu支持的内存空间远远大于机器主存的大小,这些多出来的空间对于程序来说是可以用的,这个时候的所有地址都称为虚拟地址
- 参考CSDN:linux--物理地址到虚拟地址映射,ioremap()函数_64位linux io空间物理地址与虚拟地址转换函数-CSDN博客