字符驱动规范

看到之前的代码,第一反应是什么?这他妈的是什么狗屎。静态变量哪里放的都是,代码乱的根本就没人看。
那我们来梳理和规范运行代码。
1)面向对象思想
我们要定义很多静态变量。但是这些变量都是用来描述我们的设备的运行信息的,比如设备号。。。
这时候我们就可以用到面向对象的思想(这里是假的面向对象)用一个结构体来封装这些信息。这样就看起来结构清晰又高级。
2)出错处理
还有就是我们需要做很多带返回值的函数操作,去申请一些系统资源需。
但是如果函数出错,它是直接RETURN的,根本就没有去管申请的资源去做释放,这肯定不行,这是一种不负责任的做法。所以我们要做出错处理。
利用goto语句,跳转做资源释放。
3)我们的设备号是静态申请的,然后我们又不知道它到底存不存在。万一有这个设备号, 你还填和别人一样的,就不行了。
所以我们利用动态申请
————————————————修改过的代码—————————————————————

/*********** 头文件***********/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<asm/uaccess.h>
#include <asm/io.h>
#include<linux/kfifo.h>
#include<linux/slab.h>


//物理地址
#define GPX2_CON 0x11000C40
#define GPX2_SIZE 8


/********设备信息结构体*********/
struct led_descrp {
	unsigned int my_dev;  		//主设备号
	struct class *cls;  		//返回的类指针 DEVCLS = DEV CLASS
	struct device *dev; 		//返回的设备指针
	unsigned int kernel_val;    //数据交互缓冲区
	void *reg_virt_base; 		//设备基准地址
};
	
struct led_descrp *led_dev;   	//申明结构体对象      	全局

/************************文件操作结构体函数**************************/

ssize_t chr_dev_read (struct file *filp, char __user *buff, size_t count, loff_t *fops)
{
		int ret;
		printk("-------%s-------",__FUNCTION__);
		if((ret = copy_to_user(buff, &(led_dev->kernel_val), count)) > 0){
			printk("travel failed\n");
			return -EFAULT;}

		return 0;
}

ssize_t chr_dev_write (struct file *filp,char __user *buff, size_t count, loff_t *fops)
{
		int ret;
		unsigned long vaule;
		printk("-------%s-------",__FUNCTION__);
		if((ret = copy_from_user(&(led_dev->kernel_val) ,buff, count)) == 0){
				printk("user read:%d\n",led_dev->kernel_val);}
		else{
			printk("travel failed\n");
			return -EFAULT;}
		if(led_dev->kernel_val > 0){
			
			writel((readl(led_dev->reg_virt_base + 4) | (0x1 << 7)),(led_dev->reg_virt_base + 4));

		}else{

			writel((readl(led_dev->reg_virt_base + 4) & ~(0x1 << 7)),(led_dev->reg_virt_base + 4));
		}

		return 0;

}
int chr_dev_open (struct inode *inode, struct file *filp)
{
	printk("-------%s-------",__FUNCTION__);

		return 0;
}

int chr_dev_close(struct inode *inode, struct file *filp)
{
	printk("-------%s-------",__FUNCTION__);

		return 0;
}

/***********文件操作结构体***********/

const struct file_operations my_fops = {
	.read = chr_dev_read,
	.write = chr_dev_write,
	.open = chr_dev_open,
	.release = chr_dev_close,
};


	
/*************模块装载入口的实现*****************/

static int chr_drv_init (void){
	
		int ret;
		unsigned long vaule;
	//1、初始化设备结构体————申请结构体空间
		led_dev = kmalloc(sizeof(struct led_descrp), GFP_KERNEL);
		if(led_dev == NULL){
			printk(KERN_ERR "malloc eror\n");
			return -ENOMEM;
			}
			
	//2、申请设备号
		led_dev->my_dev = register_chrdev(0, "led",&my_fops); 		//设备号写0,动态申请,并且返回设备号
			if( led_dev < 0){
				printk(KERN_ERR "register_chrdev\n");
				return -ENODEV;
				goto err0;
				}
	//3、申请设备节点
		//3.1创建类
			led_dev->cls = class_create(THIS_MODULE,"chr_clas");
			if(IS_ERR(led_dev->cls)){
				printk(KERN_ERR "class_create\n");
				return -ENOMEM;
				goto err1;
				}
			
		//3.2申请节点
			led_dev->dev = device_create(led_dev->cls, NULL,MKDEV(led_dev->my_dev, 0), NULL, "LED");
			if(IS_ERR(led_dev->dev)){
				printk(KERN_ERR "device_create\n");
				return -ENOMEM;
				goto err2;
				}
	//4、对地址进行映射 
		led_dev->reg_virt_base = ioremap(GPX2_CON,GPX2_SIZE);
			if(led_dev->reg_virt_base == NULL){
				printk(KERN_ERR "ioremap\n");
				return -ENOMEM;
				goto err3;
				}
		//对地址配置成输出模式
			vaule = readl(led_dev->reg_virt_base);
			vaule = vaule & (~(0xf << 28)) | (0x1 <<28);
			writel(vaule, led_dev->reg_virt_base);
		
		return 0;

		
err3:
	device_destroy(led_dev->cls,MKDEV(led_dev->my_dev, 0));
		
err2:
	class_destroy(led_dev->cls);
	
err1:
	unregister_chrdev(led_dev->my_dev, "led");
		
err0:
	kfree(led_dev);

}



/*************模块装载入口的实现*****************/
static void chr_drv_exit(void){
	iounmap(led_dev->reg_virt_base);
	device_destroy(led_dev->cls,MKDEV(led_dev->my_dev, 0));
	class_destroy(led_dev->cls);
	unregister_chrdev(led_dev->my_dev, "led");
	kfree(led_dev);

}
	
	
/********模块装载和卸载入口的申明*************/
module_init(chr_drv_init);
module_exit(chr_drv_exit);
	
/***********GPL申明**********************/
	MODULE_LICENSE("GPL");

————————————逻辑清晰,代码好看————————————————————

3)
我们要知道应用程序和驱动扮演的是什么角色
用户态:应用程序
玩策略: 怎么去做
1, 一闪一闪
2,10s闪一次,也可以1s闪一次
3,一直亮
4,跑马灯
控制权是在应用程序(程序员)
--------------------------------------
内核态:驱动
玩机制: 能做什么
led:亮 和 灭
所以呢,我们在内核中只要实现这些机制就可以(亮or灭)
你只需要实现它能做的东西,然后给应用层提供接口,让它操作,它想怎么操作是是它的事。

4)我们在函数中用到了之前没有用过的两个函数
2, readl/writel();
u32 readl(const volatile void __iomem *addr)//从地址中读取地址空间到值
void writel(unsigned long value , const volatile void __iomem *add)
// 将value的值写入到addr地址
例子:
// gpio的输出功能的配置
u32 value = readl(led_dev->reg_virt_base);
value &= ~(0xf<<28);
value |= (0x1<<28)
writel(value, led_dev->reg_virt_bas);
就是把地址指针里面的值读出来,或者是往里面写值,这是一种比较官方的写法,更严谨。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值