Linux驱动开发(三)--新字符设备驱动开发 LED驱动开发升级

1、新字符设备驱动原理

使用 register_chrdev 函数注册字符设备的时候只需要给定一个主设备号即可,但是这样会
带来两个问题
  • 需要我们事先确定好哪些主设备号没有使用
  • 会将一个主设备号下的所有次设备号都使用掉,比如现在设置 LED 这个主设备号为200,那么 0~1048575(2^20-1) 这个区间的次设备号就全部都被 LED 一个设备分走了。这样太浪费次设备号了!一个 LED 设备肯定只能有一个主设备号,一个次设

旧字符驱动模式,如下所示:

新字符驱动模型,如下所示:

2、分配和释放设备号

解决这两个问题最好的方法就是要使用设备号的时候向 Linux 内核申请,需要几个就申请
几个, Linux 内核分配设备可以使用的设备号
如果没有指定设备号的话就使用如下函数来申请设备号:
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可
int register_chrdev_region(dev_t from, unsigned count, const char *name)
注销字符设备之后要释放掉设备号 ,不管是通过 alloc_chrdev_region 函数还是
register_chrdev_region 函数申请的设备号,统一使用如下释放函数:
void unregister_chrdev_region(dev_t from, unsigned count)
综上所述,新字符设备驱动下,设备号分配示例代码如下:
 int major; /* 主设备号 */
 int minor; /* 次设备号 */
 dev_t devid; /* 设备号 */
 
 if (major) { /* 定义了主设备号 */
 devid = MKDEV(major, 0); /* 大部分驱动次设备号都选择 0*/
 register_chrdev_region(devid, 1, "test");
 } else { /* 没有定义设备号 */
 alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */
 major = MAJOR(devid); /* 获取分配号的主设备号 */
 minor = MINOR(devid); /* 获取分配号的次设备号 */
}

注销设备很简单,因为我们的CNT是1嘛,如下所示:

unregister_chrdev_region(devid, 1);

3、新的字符设备注册方法(cdev)

其中我们要涉及用到cdev,在 Linux 中使用 cdev 结构体表示一个字符设备,cdev 结构体在 include/linux/cdev.h 文件中的定义如下:

 struct cdev {
  struct kobject kobj;
  struct module *owner;
  const struct file_operations *ops;
  struct list_head list;
  dev_t dev;
  unsigned int count;
};
cdev 中有两个重要的成员变量: ops dev ,这两个就是字符设备文件操作函数集合
file_operations 以及设备号 dev_t 。编写字符设备驱动之前需要定义一个 cdev 结构体变量,这个
变量就表示一个字符设备,如下所示:
struct cdev test_cdev;
参数 cdev 就是要初始化的 cdev 结构体变量,参数 fops 就是字符设备文件操作函数集合。
使用 cdev_init 函数初始化 cdev 变量和向 Linux 系统添加字符设备 (cdev 结构体变量)的示例代码如下:
struct cdev testcdev;
 
 /* 设备操作函数 */
 static struct file_operations test_fops = {
 .owner = THIS_MODULE,
 /* 其他具体的初始项 */
};
 
 testcdev.owner = THIS_MODULE;
 cdev_init(&testcdev, &test_fops); /* 初始化 cdev 结构体变量 */
 cdev_add(&testcdev, devid, 1); /* 添加字符设备 */

卸载删除驱动时如下所示:

cdev_del(&testcdev); /* 删除 cdev */

4、创建以及删除类和设备(class、device)

 struct class *class; /* 类 */ 
 struct device *device; /* 设备 */
 dev_t devid; /* 设备号 */ 
 
 /* 驱动入口函数 */
 static int __init led_init(void)
 {
     /* 创建类 */
     class = class_create(THIS_MODULE, "xxx");
     /* 创建设备 */
     device = device_create(class, NULL, devid, NULL, "xxx");
     return 0;
 }

 /* 驱动出口函数 */
 static void __exit led_exit(void)
 {
     /* 删除设备 */
     device_destroy(newchrled.class, newchrled.devid);
     /* 删除类 */
     class_destroy(newchrled.class);
 }

 module_init(led_init);
 module_exit(led_exit);

整体案例代码如下所示:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define NEWCHRLED_CHT    1         /* 设备号个数 */
#define NEWCHRLED_NAME "newchrled" /* 设备名字   */
#define LEDOFF           0         /* 关灯         */
#define LEDON            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;

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设备 */

/*
 * @description : LED 打开/关闭
 * @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
 * @return : 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3); 
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val |= (1 << 3);
		writel(val, GPIO1_DR);
	} 
}


static int led_open (struct inode *inodp, struct file *filp)
{
	filp->private_data = &newchrled; 
	return 0;
}

static ssize_t led_read (struct file *filp, char __user *buf, 
									     size_t cnt, loff_t *offt)
{
	return 0;
}

/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write (struct file *filp, const char __user *buf, 
											    size_t cnt, loff_t *offt)
{
	int retvalue = 0;
	unsigned char databuf[1];
	unsigned char ledstat;
	
	retvalue = copy_from_user(databuf,buf,cnt);
	if(retvalue < 0){
		printk("kernel write failed\r\n");
		return -EFAULT;
	}
	ledstat = databuf[0];
	if(ledstat == LEDON){
		led_switch(LEDON);
	}
	else if(ledstat == LEDOFF){
		led_switch(LEDOFF);
	}
	return 0;
}

/*
 * @description : 关闭/释放设备
 * @param - filp : 要关闭的设备文件(文件描述符)
 * @return : 0 成功;其他 失败
 */
static int led_release (struct inode *inodp, struct file *filp)
{
	return 0;
}

static struct file_operations newchrled_fops = {
	.owner   = THIS_MODULE,
	.open    = led_open,
	.read    = led_read,
	.write   = led_write,
	.release = led_release,
};

static int __init led_init(void){
	int retvalue = 0;
	u32 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);

	/* 2、使能 GPIO1 时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26); /* 清除以前的设置 */
	val |= (3 << 26); /* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

    /* 3、设置 GPIO1_IO03 的复用功能,将其复用为
   	 * GPIO1_IO03,最后设置 IO 属性。
   	 */
   	writel(5, SW_MUX_GPIO1_IO03);
   
   	/* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */
   	writel(0x10B0, SW_PAD_GPIO1_IO03);
   
   	/* 4、设置 GPIO1_IO03 为输出功能 */
   	val = readl(GPIO1_GDIR);
   	val &= ~(1 << 3); /* 清除以前的设置 */
   	val |= (1 << 3); /* 设置为输出 */

	writel(val, GPIO1_GDIR);

	/* 5、默认关闭 LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3); 
	writel(val, GPIO1_DR);

	/*	//设备号 名字 字符模型驱动的一个结构体
	retvalue = register_chrdev(LED_MAJOR,LED_NAME,&newchrled_fops);
	if(retvalue < 0){
	// exit
	}	
	*/
	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if(newchrled.major){
		newchrled.devid = MKDEV(newchrled.major, 0);
		register_chrdev_region(newchrled.devid, NEWCHRLED_CHT, NEWCHRLED_NAME);
	} else {
		alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CHT, NEWCHRLED_NAME);
		newchrled.major = MAJOR(newchrled.devid);
		newchrled.minor = MINOR(newchrled.devid);
	}
	printk("newchrled major = %d,minor = %d \r\n",newchrled.major,newchrled.minor);
	/* 2、初始化     cdev */
	newchrled.cdev.owner = THIS_MODULE;
	cdev_init(&newchrled.cdev,&newchrled_fops);
	/* 3、添加一个 cdev */
	cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CHT);
	/* 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;
}

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);

	/* unregister_chrdev(LED_MAJOR,LED_NAME); */
	/* 注销字符设备 */
	cdev_del(&newchrled.cdev);
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_CHT);
	
	device_destroy(newchrled.class,newchrled.devid);
	class_destroy(newchrled.class);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("7yewh");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/732416.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这周,接连两位程序员猝死...

这周接连发生了两起不幸的事。俩位程序员去世的消息&#xff0c;深感悲伤和惋惜。 6月17号下午&#xff0c;一位负责研发的女员工在虾皮研发中心办公室猝死&#xff0c;年仅 30 岁。 官方通告&#xff1a; 同一天&#xff0c;另一位科大讯飞的高级测试工程师在家突发不适离世…

UDS服务——TransferData (0x36)

诊断协议那些事儿 诊断协议那些事儿专栏系列文章,本文介绍TransferData (0x36)—— 数据传输,用于下载/上传数据时用的,数据的传输方向由不同的服务控制:0x34服务表示下载,0x35服务表示上传。通过阅读本文,希望能对你有所帮助。 文章目录 诊断协议那些事儿传输数据服务…

Xshell7免费版下载安装使用

​一、下载安装​ 1.打开官网下载 https://www.xshell.com/zh/free-for-home-school/ 2.选择合适的下载路径&#xff0c;点击下载按钮&#xff0c;然后按照提示完成安装。 二、Xshell7的使用&#xff0c;Xhell连接Linux 1.连接之前&#xff0c;确保在Linux中开启SSH。参考&a…

VBA学习(15):工作表加密保护后却把密码忘记了?

今天把过去的一篇推文重新整理一下&#xff0c;提供两种解除工作表加密的方法。 一种是傻瓜模式的VBA&#xff0c;复制运行以下代码&#xff0c;即可抹除当前工作簿所有工作表的保护加密。 Sub UnProtct()MsgBox "破解提示&#xff1a;当要求输入密码时请点击取消&#…

Pnpm:包管理的新星,如何颠覆 Npm 和 Yarn

在探索现代 JavaScript 生态系统时&#xff0c;我们常常会遇到新兴技术的快速迭代和改进。其中&#xff0c;包管理工具的发展尤为重要&#xff0c;因为它们直接影响开发效率和项目性能。最近&#xff0c;pnpm 作为一种新的包管理工具引起了广泛关注。它不仅挑战了传统工具如 np…

激励-保健理论和公平理论

激励-保健理论 herzberg的激励-保健理论中&#xff0c;保健因素是context of a job&#xff0c;激励因素是content of a job。 context of a job是受组织控制的因素&#xff0c;比如工作条件&#xff0c;基本工资&#xff0c;公司政策等&#xff0c;个人无法支配。content of…

【深入浅出MySQL】「数据同步架构」分析探索Canal开源技术原理和架构

分析探索Canal开源技术原理和架构 背景说明Canal基本介绍Canal作用方向MySQL同步原理Binlog Dump交互Binlog的协议模型Canal的模拟slave角色Canal的消费订阅 Canal Server模块Canal Instance模块参考资料类似开源项目 背景说明 在早期阶段&#xff0c;阿里巴巴B2B公司由于其在…

WPF文本框中加提示语

效果&#xff1a; WPF中貌似不能像winfrom里一样直接加提示语&#xff0c;需要使用TextBox.Style&#xff0c;将Trigger标签插入进去。 贴源码&#xff1a; <WrapPanel Name"TakeOverExpressNo1"><Label Content"物流单号&#xff1a;"><…

力扣SQL50 每月交易 I 求和 SUM(条件表达式) DATE_FORMAT(日期,指定日期格式)

Problem: 1193. 每月交易 I &#x1f468;‍&#x1f3eb; 参考题解 Code select DATE_FORMAT(trans_date, %Y-%m) AS month,country,count(*) as trans_count,count(if(state approved, 1, NULL)) as approved_count,sum(amount) as trans_total_amount,sum(if(state appr…

MS17-010(Eternal blue永恒之蓝)漏洞利用+修复方法

目录 一、漏洞简介 漏洞原理 影响版本 二、漏洞复现 三、复现过程 1、扫描局域网内的C段主机&#xff08;主机发现&#xff09; 扫描结果&#xff1a; 2.使用MSF的永恒之蓝漏洞模块 3.对主机进行扫描&#xff0c;查看其是否有永恒之蓝漏洞 4.准备攻击 四、漏洞利用 …

华为---OSPF被动接口配置(四)

9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口&#xff0c;成为被动接口后&#xff0c;将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息&#xff0c;即已运行在OSPF协议…

【泛微系统】解决启动非标功能时提示客户ID不一致的问题

解决启动非标时提示CID不一致的问题 泛微OA系统是一个非常丰富的系统,我们在日常工作中会经常遇到很多业务需求,我们会用到很多功能来承载这些需求的实现;OA系统里有标准功能,也有非标准的功能;对于非标准的功能需要打非标补丁包; 有些同学在个人学习系统的过程中会安装本…

[图解]企业应用架构模式2024新译本讲解16-行数据入口2

1 00:00:00,750 --> 00:00:02,470 好&#xff0c;我们来看代码 2 00:00:03,430 --> 00:00:06,070 我们一步一步执行 3 00:00:42,500 --> 00:00:45,000 先初始化数据 4 00:00:52,300 --> 00:00:53,650 创建连接 5 00:00:55,900 --> 00:00:56,970 这里面 6 0…

Electron快速入门(二):在(一)的基础上修改两个文件完成自定义显示时间和天气的标题栏

修改主进程:main.js // main.jsconst { app, BrowserWindow } = require("electron"); const path = require("node:path"); const createWindow = () => {try {const mainWindow = new BrowserWindow({width: 1200,height: 870,alwaysOnTop: true,fr…

aws的alb,多个域名绑定多个网站实践

例如首次创建的alb负载均衡只有www.xxx.com 需要添加 负载 test2.xxx.com aws的Route 53产品解析到负载均衡 www.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx test2.xxx.com 添加CNAME&#xff0c;到负载均衡的dns字段axx 主要介绍目标组和规则 创建alb就不介…

【报错解决】引入@ComponentScan注解注册bean容器后,导致的接口404问题

引入ComponentScan注解注册bean容器后&#xff0c;导致的接口404问题 背景 由于微服务开发中&#xff0c;经常需要在公共模块在引入一些公共模块&#xff0c;供其他服务使用&#xff0c;但是其他服务需要在启动类中配置ComponentScan注解扫描这个公共模块下注册的 bean&#…

sdlan如何智能组网?

【天联】组网是一款异地组网内网穿透产品&#xff0c;由北京金万维科技有限公司自主研发&#xff0c;旨在解决不同地区电脑与电脑、设备与设备、电脑与设备之间的信息远程通信问题。【天联】的操作简单、跨平台应用、无网络要求以及独创的安全加速方案等特点&#xff0c;使得它…

全国实体商铺店铺商家采集工具,一键采集商家手机号,让你轻松找到目标客户

随着互联网的发展&#xff0c;越来越多的商家开始在网上开展业务&#xff0c;实体商铺的竞争也日益激烈。为了更好地吸引客户&#xff0c;很多商家都选择了线上推广和营销。然而&#xff0c;仅仅依靠线上推广是远远不够的&#xff0c;线下的实体商铺也需要积极拓展客源。因此&a…

C语言学习记录20240622

这次需要用 C 语言库 Allegro 写爆破彗星游戏。项目有一些描述如需要绘制飞船、彗星、子弹&#xff0c;需要响应按键实现飞船加速、减速、转向、开火&#xff0c;需要绘制弹道&#xff0c;需要实现彗星旋转、缩放&#xff0c;需要碰撞检测&#xff0c;需要显示计分。 这些用 w…

Docker 搭建 MinIO 对象存储

Docker 搭建 MinIO 对象存储 一、MinIO MinIO 是一个高性能的对象存储服务器&#xff0c;用于构建云存储解决方案。MinIO 允许你存储非结构化数据&#xff08;如图片、视频、日志文件等&#xff09;以对象的形式。MinIO 提供简单的部署选项和易于使用的界面&#xff0c;允许你…