5.3.3.注册字符设备驱动新接口3 : alloc_chrdev_region 更简便、更智能的方法是让内核给我们自动分配一个主设备号5.3.2.3、中途出错的倒影式错误处理方法

5.3.3.注册字符设备驱动新接口3
5.3.2.1、使用alloc_chrdev_region自动分配设备号
(1)register_chrdev_region是在事先知道要使用的主、次设备号时使用的;要先查看cat /proc/devices去查看没有使用的。


(2)更简便、更智能的方法是让内核给我们自动分配一个主设备号,使用alloc_chrdev_region就可以自动分配了。

  


1. alloc_chrdev_region 更简便、更智能的方法是让内核给我们自动分配一个主设备号

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)

dev_t *dev : 输出型参数 ,就是要分配的主 次 设备号 

unsigned baseminor : 次设备号的 起始 号

unsigned count : 几个 次设备号

const char *name :  设备名字

返回值 : 小于<0 代表错误

函数使用:
static dev_t mydev; /*这里用来接收 一个主次 设备号 , */
dev_t 类型的变量 !!!

retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME); /* 自动分配 主次设备号*/

12: 代表次设备号 从 12 开始


代码:


retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME); /* 自动分配 主次设备号*/
	if (retval  < 0) {
		printk(KERN_ERR "Unable to alloc_chrdev_region  %s  error \n",MYNAME);
		return -EINVAL;
	}
	printk(KERN_INFO " alloc_chrdev_region  success ok \n");
	printk(KERN_INFO "major = %d, minor = %d \n", MAJOR(mydev), MINOR(mydev));  /* MAJOR(mydev), MINOR(mydev)用这两个宏打印 主次设备号*/


(3)自动分配的设备号,我们必须去知道他的主次设备号,否则后面没法去mknod创建他对应的设备文件。


5.3.2.2、得到分配的主设备号和次设备号
(1)使用MAJOR宏和MINOR宏从dev_t得到major和minor
(2)反过来使用MKDEV宏从major和minor得到dev_t。
(3)使用这些宏的代码具有可移植性

运行结果:

5.3.2.3、中途出错的倒影式错误处理方法
(1)内核中很多函数中包含了很多个操作,这些操作每一步都有可能出错,而且出错后后面的步骤就没有进行下去的必要性了。

 

 

 我们 程序在 设计时 应该 在 某一个 函数出错后, 释放掉 锁占用 的内存 什么的 什么的。。。。

 

 

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>      //copy_from_user
#include <linux/errno.h>  // 错误码
#include <mach/regs-gpio.h>//arch/arm/mach-s5pv210/include/mach/regs-gpio.h  这两个顺序不能放错,c语言基础
#include <mach/gpio-bank.h> //arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>//包含 内核的 memset  、strcmp
#include <linux/io.h>  //ioremap
#include <linux/ioport.h>  //request_mem_region
#include <linux/cdev.h> // cdev结构体和 新注册接口函数


//#define MYMAJOR 200  /* 定义 register_chrdev 注册设备的 主设备号 : 先  cat /proc/devices  查看 200 有没有被占用  ,   新老注册函数 通用*/
#define MYCNT   1      /* 注册 几个次 设备号  ,新注册接口函数 */
#define MYNAME  "test_char" /* 定义 register_chrdev 注册设备的 设备名字  新老注册函数 通用 */

/*********静态内存*****************************/

#define GPJ0CON	  S5PV210_GPJ0CON	   // FD500240 虚拟地址	
#define GPJ0DAT	  S5PV210_GPJ0DAT	   // FD500244


#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)
/*********静态内存end*****************************/

/*********动态内存*****************************/

#define  GPJ0CON_PA	   0xE0200240          // 寄存器真实物理 地址
#define  GPJ0DAT_PA	   0xE0200244	   // 

unsigned int *pGPJOCON;  // ioremap 返回的 虚拟地址
unsigned int *pGPJODAT;

/*********动态内存end*****************************/


//int mymajor; /* 定义 register_chrdev 注册设备号 (这个是老驱动模块 )*/

char kbuf[100];/* 内核空间的 buf*/


static struct cdev test_device_cdev; /* 定义一个cdev 结构体变量 test_device_cdev ,cdev_init函数的 第一个参数   新注册接口函数 */
static dev_t mydev; /*register_chrdev_region 第一个参数 主设备号,这里用来接收 一个主次 设备号 ,  新注册接口函数*/

/* NOTE  自己定义函数指针  test_chrdev_open  */
static int test_chrdev_open(struct inode *inode, struct file *file)
{
	/* 这个函数中真正应该 放置 打开这个硬件设备的 操作代码 ,我们先 printk 代替一下 */
	printk(KERN_INFO "test_chrdev_open module_test.c->test_chrdev_open \n");  
	
	/* 在应用app.c 执行open 时,就会执行 LED */
	rGPJ0CON = 0x11111111;
	//rGPJ0DAT = ((0<<3)|(0<<4)|(0<<5));  //LED亮
	
	return 0;

} /* test_chrdev_open() */




/* NOTE  自己定义函数指针 test_chrdev_release ,   release对应的就是 close  */
static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release module_test.c->test_chrdev_release \n");


		/* 在应用app.c 执行close 时,就会执行 LED灭 */
	rGPJ0DAT = ((1<<3)|(1<<4)|(1<<5)); //LED灭
	return 0;
}


static ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_release module_test.c->test_chrdev_read \n");
	
	/* 从内核 的 kbuf, 复制到用户的 ubuf  */
	ret = copy_to_user(ubuf,kbuf,size);    /* 成功后 就会拷贝到用户 ubuf */
	if(ret)  /* 如果 不成功复制则返回尚未成功复制剩下的字节数, 这里 就不做 纠错 机制了 */
	{
		printk(KERN_ERR "copy_to_user  fail \n"); 
		return -EINVAL;
	}
	printk(KERN_INFO "copy_to_user  OK!!! module_test.c->test_chrdev_write\n");
	
	
	
	
	return 0;
}

// 写函数的本质:将应用层 传递过来的数据先 复制到 内核中,然后将之正确的方式写入硬件完成的操作!(数据从应用层到驱动层的复制,)
// 内核有一个 虚拟地址空间,应用层有一个 虚拟地址空间
static ssize_t test_chrdev_write(struct file *file, const char __user *user_buf,
			size_t count, loff_t *ppos)
{
	int ret = -1;
	
	printk(KERN_INFO "test_chrdev_release module_test.c->test_chrdev_write\n");
	
	
	memset(kbuf,0,sizeof(kbuf)); /*清除kbuf */
	//使用改函数将: 应用层传过来的 ubuf 中的内容 拷贝到驱动空间中的 一个 kbuf 中
	/* 不能用memcpy(kbuf,buf); 因为 2 个 不在一个地址空间中,不能 比较 霍元甲和成龙 谁更厉害 ,不在一个年龄段*/
	ret = copy_from_user(kbuf,user_buf,count);    /*  成功后 就会 放到 kbuf 中 */
	if(ret)  /* 如果 不成功复制则返回尚未成功复制剩下的字节数, 这里 就不做 纠错 机制了 */
	{
		printk(KERN_ERR "copy_from_user fail \n"); 
		return -EINVAL;
	}
	printk(KERN_INFO "copy_from_user OK!!! module_test.c->test_chrdev_write\n");
	
	
	/* 真正的 驱动的 数据从 应用层 复制 到 驱动中后,我们就要根据这个数据去写硬件的操作,所以下面就应该操作硬件 */
	
	if(kbuf[0] == '1')
	{
		rGPJ0DAT = ((0<<3)|(0<<4)|(0<<5));  //LED亮
	}
	else if (kbuf[0]=='0')
	{
		rGPJ0DAT = ((1<<3)|(1<<4)|(1<<5)); //LED灭
	}

	return 0;
}


//自定义  file_operations 结构体 及其元素填充
/* NOTE  定义 register_chrdev 注册设备的 设备结构体 test_fops */
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,                    /* 所有的驱动 代码这一行不需要动,所有的都是这样,不是函数指针, 惯例直接写即可 */
	
	.open		= test_chrdev_open,  /* 将来应用 open 打开这个设备时实际 调用的就是这个 .open  函数指针*/
	.release	= test_chrdev_release,         /* release对应的就是 close    函数指针 */
	.write		= test_chrdev_write, 
	.read		= test_chrdev_read, 
};









// 模块安装函数
static int __init chrdev_init(void)
{	
	int retval;  /*register_chrdev_region 的返回值 */
	
	
	printk(KERN_INFO "chrdev_init helloworld init an zhuang qu dong \n");
	// 使用新的cdev接口来注册字符设备驱动
	// 新的接口注册字符设备驱动需要2步
	
	// 第1步:注册/分配主次设备号
	retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME); /* 自动分配 主次设备号*/
	if (retval  < 0) {
		printk(KERN_ERR "Unable to alloc_chrdev_region  %s  error \n",MYNAME);
		goto flag1;
	}
	printk(KERN_INFO " alloc_chrdev_region  success ok \n");
	printk(KERN_INFO "major = %d, minor = %d \n", MAJOR(mydev), MINOR(mydev));  /* MAJOR(mydev), MINOR(mydev)用这两个宏打印 主次设备号*/

#if 0
	mydev = MKDEV(MYMAJOR,0);/* MKDEV : 算出 主 次 设备号 ,我们这里有 主设备号200,  次设备号起始为 0 */
	retval = register_chrdev_region(mydev, MYCNT, MYNAME); 
	if (retval) {
		printk(KERN_ERR "Unable to register minors for %s  error \n",MYNAME);
		return -EINVAL;
	}
	printk(KERN_INFO " register_chrdev_region success ok \n");
#endif	
	// 第2步:注册字符设备驱动
	cdev_init(&test_device_cdev, &test_fops);/* 初始化,两个参数都是取地址,说明都是指针,cdev 结构体变量 test_device_cdev和 file_operations 结构体变量 test_fops*/
	retval = cdev_add(&test_device_cdev, mydev, MYCNT);  /* 完成真正的 驱动注册*/
	if (retval) {
		printk(KERN_ERR "Unable to cdev_add error error \n");
		goto flag2;
	}
	printk(KERN_INFO " cdev_add success ok \n");
	

	//使用动态映射 操作 寄存器   第3步:
	if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON_PA")) /* 向内核申请(报告)需要映射的内存资源。*/
		goto flag3;
		
		
	if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CDAT_PA")) /* 向内核申请(报告)需要映射的内存资源。*/
		goto flag3;
		
	//第4步:	
	pGPJOCON = ioremap(GPJ0CON_PA, 4);//真正用来实现映射,传给他物理地址他给你映射返回一个虚拟地址
	pGPJODAT = ioremap(GPJ0CON_PA+4, 4);//真正用来实现映射,传给他物理地址他给你映射返回一个虚拟地址
	
	*pGPJOCON = 0x11111111;
	*(pGPJOCON+1) = ((0<<3)|(0<<4)|(0<<5));  //LED亮	//*pGPJODAT = ((0<<3)|(0<<4)|(0<<5));  等同于 *(pGPJOCON+1) = ((0<<3)|(0<<4)|(0<<5)); 
	
	return 0;
	
// 如果第4步才出错跳转到这里来	
flag4:
	release_mem_region(GPJ0CON_PA,4);//释放申请的内存 物理内存
	release_mem_region(GPJ0DAT_PA,4); //释放申请的内存
	
// 如果第3步才出错跳转到这里来
flag3:
	// 真正注销字符设备驱动用cdev_del
	cdev_del(&test_device_cdev);

// 如果第2步才出错跳转到这里来
flag2:
	// 在这里把第1步做成功的东西给注销掉
	unregister_chrdev_region(mydev, MYCNT);  /* 去注销申请的主次设备号 */
// 如果第1步才出错跳转到这里来
flag1:	
	return -EINVAL;	

}

// 模块卸载函数
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit xie zai \n");

	// 使用新的接口来注销字符设备驱动
	// 注销分2步:
	// 第一步真正注销字符设备驱动用cdev_del
	cdev_del(&test_device_cdev);
	// 第二步去注销申请的主次设备号
	unregister_chrdev_region(mydev, MYCNT);
	
	
	

/**** 动态内存 释放   ****/
	*pGPJODAT = ((1<<3)|(1<<4)|(1<<5));   //LED灭
	iounmap(pGPJOCON);//解除映射时,传给他一个虚拟地址
	iounmap(pGPJODAT);//解除映射时,传给他一个虚拟地址
	
	release_mem_region(GPJ0CON_PA,4);//释放申请的内存 物理内存
	release_mem_region(GPJ0DAT_PA,4); //释放申请的内存
	
}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息



 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: vue.js devtools_5.3.3.crx是一个针对Vue.js开发者设计的浏览器插件。通过安装这个插件,开发者可以在浏览器中方便地调试和监控Vue.js应用程序。 vue.js devtools_5.3.3.crx提供了一系列功能,让开发者能够好地理解和优化Vue.js应用程序。首先,它可以显示Vue组件层次结构,让开发者可以清楚地了解应用程序的组件结构。其次,它可以查看和修改组件的props、data、computed等属性,方便开发者进行调试和修改。此外,vue.js devtools_5.3.3.crx还提供了事件追踪功能,可以帮助开发者分析Vue.js应用程序中的事件触发情况。 除了上述基本功能,vue.js devtools_5.3.3.crx还提供了一些高级功能。例如,它可以让开发者在控制台中编写和执行Vue.js代码,方便快捷地进行调试。它还可以捕捉并显示Vue.js应用程序中的警告和错误信息,让开发者可以及时发现和修复问题。此外,vue.js devtools_5.3.3.crx还支持时间旅行功能,可以回滚和重放应用程序的状态变化,帮助开发者好地进行应用程序状态的调试。 总的来说,vue.js devtools_5.3.3.crx是一个非常有用的工具,对于Vue.js开发者来说,它能提供很多方便的调试和监控功能,帮助开发者高效地开发和维护Vue.js应用程序。 ### 回答2: vue.js devtools_5.3.3.crx是Vue.js开发人员工具的浏览器扩展文件。Vue.js是一种流行的JavaScript框架,用于构建用户界面。该框架使开发人员能够轻松地构建可扩展且高效的Web应用程序。 Vue.js devtools是一个用于调试Vue.js应用程序的强大工具。它可以与Chrome浏览器一起使用,以可视化方查看和分析Vue.js应用程序的状态,组件层次结构,事件和性能。 Vue.js devtools允许开发人员监视和检查Vue.js应用程序中的组件树。它显示了每个组件的实时状态,包括data、props、computed和methods等属性。这使开发人员可以轻松地检查和修改组件的状态,以便好地了解应用程序的工作方。 另外,Vue.js devtools还提供了强大的事件跟踪功能。它可以捕获并显示应用程序中触发的所有事件,包括DOM事件和自定义事件。这对于调试和优化应用程序的事件处理非常有帮助。 除了状态和事件跟踪,Vue.js devtools还提供了性能分析功能。它可以测量应用程序的渲染时间,以及每个组件的性能指标。这为开发人员提供了深入了解应用程序性能的能力,并帮助他们优化和改进应用程序的使用体验。 总之,vue.js devtools_5.3.3.crx是一个强大的工具,为Vue.js开发人员提供了许多便利的功能,可以帮助他们轻松地调试、优化和改进Vue.js应用程序的开发过程。 ### 回答3: vue.js devtools是一种用于Vue.js开发和调试的浏览器扩展程序,具体版本5.3.3.crx是指该扩展程序的版本。 Vue.js是一种流行的JavaScript框架,用于构建用户界面。它通过组件化的方提供了一种便捷、高效的开发方。Vue.js devtools是为了方便开发人员在开发和调试Vue.js应用程序时提供的工具。 Vue.js devtools可以通过浏览器的扩展程序安装,并且与开发者工具紧密集成,以提供强大的开发和调试功能。它可以让开发人员实时查看和调试Vue组件的状态和属性,监控数据的变化,检查组件层次结构和生命周期钩子函数的执行,以及性能分析和优化Vue应用程序等。 版本5.3.3.crx表示这是Vue.js devtools的特定版本。版本通常用于标识软件的不同发布版本,以便开发者和用户可以区分不同版本之间的功能变化和改进。对于使用Vue.js devtools的开发人员来说,特定版本可能对于记录问题、求助社区或者查看发行说明来说是非常有用的。 总之,在Vue.js开发中,使用Vue.js devtools可以提高开发效率和调试能力。版本5.3.3.crx则是Vue.js devtools的一个特定版本,标识了该版本的特定功能和改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大漠飞鹰6666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值