【linux-IMX6ULL-LED字符驱动框架完善】

2024-05-25 15:36

本文主要是介绍【linux-IMX6ULL-LED字符驱动框架完善】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1.简介
  • 2.前置知识
    • 2.1 重要函数及结构体
    • 2.2 程序框架流程
  • 3. 代码详解:

1.简介

  在上节,我对linux-IMX6ULL-字符设备驱动简单框架实验进行了说明和构建,但是也存在几个问题;

  • 需要手动指定设备号,不能自动申请;
  • 需要在linux端手动创造设备节点,也就是要用maknod命令;
  • 没有引入实际设备;
      因此这节内容就根据上节的驱动框架,然后结合LED,实现设备号的自动分配和设备节点的自动创建;

2.前置知识

  由于本篇博客不属于教程类博客,只是作为学习总结和复盘,因此先把相关的重点知识给提前说明,也能起到一个便于快速回顾的目的;

2.1 重要函数及结构体

 下面的函数均进行了实参带入,具体原定义可以参考源码;

  • static void __iomem *IMX6U_CCM_CCGR1;
  • IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
  • register_chrdev_region(newchrled.devid,NEWCHRLED_COUNT,NEWCHRLED_NAME);
  • alloc_chrdev_region(&newchrled.devid,0,NEWCHRLED_COUNT,NEWCHRLED_NAME);
  • struct cdev cdev;
  • struct class *class;
  • struct device *device;
  • cdev_init(&newchrled.cdev, &newchrled_fops);
  • cdev_add(&newchrled.cdev, newchrled.devid,1);
  • class_create(THIS_MODULE, NEWCHRLED_NAME);
  • device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);

2.2 程序框架流程

3. 代码详解:

  注意几个点:

  1. 在写驱动程序时不能直接操控物理寄存器,我们只能操控虚拟化的地址,然后虚拟化的地址通过映射间接操控真实的寄存器;
  2. 操控虚拟化的寄存器地址时是通过read(),write()函数来完成的,不能直接赋值;
  3. 我们接收用户端的写的数据时要通过copy_from_user(databuf,buffer,count)函数来实现,不能直接获取;
  4. 注意出口函数里面的注销和删除顺序是有要求的,我们最开始是先注册的设备号,然后注册操作结构体,但是我们在出口函数里面是先删除操作结构体,然后再删除设备号,注意顺序是有要求的,其它也是一样的;

#define LED_MAJOR 200  
#define NEWCHRLED_NAME "newchrled1"
#define NEWCHRLED_COUNT 1/*物理地址*/
#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;/*宏定义,开关*/
#define	LEDOFF		0
#define LEDON		1/**LED 设备结构体**/
struct newchrled_dev{struct cdev cdev;     /*创建设备结构体*/struct class *class;  /*返回值都是指针类型*/struct device *device; /*创建设备的返回值,是个结构体指针*/dev_t devid;			/*设备号*/int major;				/*主设备号*/int minor;			/*次设备号*/
};
/*创建LED设备的结构体,这里没有初始化*/
struct newchrled_dev newchrled;
/*led开关函数封装*/
void led_switch(u8 sta)
{u32 val=0;if(sta==LEDON){val = readl(GPIO1_DR);val &= ~(1<<3);writel(val,GPIO1_DR);}if(sta==LEDOFF){val = readl(GPIO1_DR);val |= (1<<3);writel(val,GPIO1_DR);}
}
/*led初始化封装*/
void led_inti(void)
{unsigned int val = 0;/*把物理地址进行虚拟化映射,映射完后把虚拟地址赋值给前面定义的虚拟地址*/IMX6U_CCM_CCGR1 	= ioremap(CCM_CCGR1_BASE,4);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);/*开时钟*/val=readl(IMX6U_CCM_CCGR1);val &= ~(3<<26);/*clear*/val |= (3<<26);/*set bit 27 26 into 1*/writel(val,IMX6U_CCM_CCGR1);/*配置寄存器*/writel(0x5,SW_MUX_GPIO1_IO03);writel(0x10B0,SW_PAD_GPIO1_IO03);val = readl(GPIO1_GDIR);val |= (1<<3);writel(val,GPIO1_GDIR);
}static int newchrled_release(struct inode *inode, struct file *file)
{printk("Close ok\r\n");//struct newchrled_dev *dev=(struct newchrled_dev*)file->private_data;return 0;
}static int newchrled_open(struct inode *inode, struct file *file)
{printk("Open ok\r\n");//file->private_data = &newchrled;return 0;
}static ssize_t newchrled_write(struct file *file, const char __user *buffer,size_t count, loff_t *pos)
{unsigned int retvalue;unsigned char databuf[1];/*从用户哪里获取写入的数据,这里不能直接获得,要通过下面的函数进行获得*/retvalue=copy_from_user(databuf,buffer,count);if(retvalue<0){printk("Kernel write failed!\r\n");return -EFAULT;}/*判断是开灯还是关灯*/led_switch(databuf[0]);return 0;
}static const struct file_operations newchrled_fops={.owner 		= 	THIS_MODULE,.write		=	newchrled_write,.open		=	newchrled_open,.release	=	newchrled_release,
};/**into**/
static int __init newchrled_init(void)
{int ret = 0;printk("newchrled init!\r\n");/*1.初始化LED灯,地址映射*/	led_inti();/*2.注册设备号*/newchrled.major = 0;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);newchrled.major = MAJOR(newchrled.devid);newchrled.minor = MINOR(newchrled.devid);}if(ret<0){printk("newchrled chrdev err!\r\n");return -1;}printk("major=%d,minor=%d\r\n",newchrled.major,newchrled.minor);/*3 注册操作函数*/newchrled.cdev.owner=THIS_MODULE;cdev_init(&newchrled.cdev, &newchrled_fops);cdev_add(&newchrled.cdev, newchrled.devid,1);/*添加到linux内核中*///  第二步和第三歩本来在前两节是通过下面的函数实现的://  register_chrdev(LED_MAJOR, LED_NAME,&led_fops);//  这里改写成了改写成了两步,第一步是申请设备号,第二步是注册设备操作函数/*4.自动创建设备节点*/newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(newchrled.class)){return PTR_ERR(newchrled.class);}/*5.创建一个设备*/newchrled.device = device_create(newchrled.class, NULL, newchrled.devid,NULL,NEWCHRLED_NAME);if (IS_ERR(newchrled.device)){return PTR_ERR(newchrled.device);}return 0;
}/**exit**/
static void __exit newchrled_exit(void)
{printk("newchrled exit!\r\n");/*1.注销字符操作函数*/cdev_del(&newchrled.cdev);/*2.注销设备号*/unregister_chrdev_region(newchrled.devid,NEWCHRLED_COUNT);/*3.先摧毁设备*/device_destroy(newchrled.class, newchrled.devid);/*4.后摧毁类*/class_destroy(newchrled.class);
}module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");

这篇关于【linux-IMX6ULL-LED字符驱动框架完善】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1001914

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或