新版的字符设备驱动

  • register_chrdev 和 unregister_chrdev 这两个函数是老版本驱动使用的函数,新字符设备驱动使用了cdev
  • image.png
  • unregister_chrdev = cdev_del + unregister_chrdev_region :

    image.png

  • 在 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博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值