前面笔者已实现了用户程序控制led灯闪烁的驱动代码,但是由于代码不规范,显得乱七八糟的,因此需要规范化。如果比较大一点的工程没有规范的话,也不利于后期的跟新与维护。分析先前的程序不规范点有二:

  1. 定义的变量多而且散乱
  2. 程序没有错误处理机制

C语言虽然是面向过程的语言,但是可以利用结构体来实现面向对象的思想。通过引入面向对象的思想,来解决第一个问题。通过结构体将将相关的变量类型进行一次封装,构造出一个对象;而对于错误处理, 可以通过prink提示错误信息,然后goto语句跳转到错误处理的过程来处理。

 

面向对象

在面向对象的思想中,一切皆是对象。将led设备抽象成为一个对象,那它的主设备号、寄存器基地址等信息都可以看做它的属性,我们用结构体将这样一些数据类型进行封装。

struct led_desc{
//声明结构体类型,描述led信息
	unsigned int dev_major;		//描述主设备号
	struct class *cls;			
	struct device *dev; 
	void *reg_virt_base;		//寄存器基地址
};

定义结构体对象 ,led_dev明显是一个结构体指针

struct led_desc *led_dev;

为led_dev分配空间,对象的实例化

led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);

 

如果要引用这个对象的属性(结构体访问成员变量),例如

led_dev->dev_major

 

因为使用malloc分配的是块空间(堆),结束时需要手动释放资源(空间)

kfree(led_dev);

 

 

错误处理机制

当程序申请资源失败,我们不仅要分析判断这个错误,还要打印错误提示

	led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
	if(led_dev->dev_major < 0)
	{//打印错误
		printk(KERN_ERR "register_chrdev error\n");
	}

但是,,这样的做法是不对的。我们程序不能直接抛出错误信息,然后退出,给系统留下一个烂摊子啊!因为之前可能存在,已申请但是没有被释放的资源。正确的做法

错误源 ---> 程序判断 ---> (打印错误信息) ---> 设置错误码 ---> 跳转到错误处理 ---> 退出

	int ret;

	//led_dev分配空间,对象的实例化
	led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
	if(led_dev == NULL)
	{
		printk(KERN_ERR "malloc error\n");
		return -ENOMEM;
	}

	
	//动态向系统申请设备号
	led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
	if(led_dev->dev_major < 0)
	{
		printk(KERN_ERR "register_chrdev error\n");
		ret = -ENODEV;
		goto err_0;
	}


	err_0:
		kfree(led_dev);
		return ret;

总之,错误处理是要处理那些 在错误出现之前申请的资源,将其回收。

 

驱动代码

下面是对之前led驱动程序的一些改进。

  • 使用了结构体来描述设备信息
  • 使用goto进行错误处理
  • 修改申请主设备号为动态方式
  • 使用readl writel 接口函数读写地址
//led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>


#include <asm/uaccess.h>
#include <asm/io.h>


ssize_t led_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos);
ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos);
int led_drv_open(struct inode *inode, struct file *filp);
int led_drv_close(struct inode *inode, struct file *filp);


#define GPX2_CON 0x11000C40  
#define GPX2_SIZE 8

//volatile unsigned long *gpx2conf;
//volatile unsigned long *gpx2dat;

//static unsigned int dev_major = 250;
//static struct class *devcls;
//static struct device *dev;


const struct file_operations my_fops = {
	.open = led_drv_open,
	.read = led_drv_read,
	.write = led_drv_write,
	.release = led_drv_close,

};



struct led_desc{
//声明结构体类型,描述led信息
	unsigned int dev_major;		//描述主设备号
	struct class *cls;			
	struct device *dev; 
	void *reg_virt_base;		//寄存器基地址
};

//定义一个结构体变量,创建对象
struct led_desc *led_dev;




static int __init led_drv_init(void)
{
	int ret;
	printk("-------%s-------------\n", __FUNCTION__);

	//led_dev分配空间,对象的实例化
	led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
	if(led_dev == NULL)
	{
		printk(KERN_ERR "malloc error\n");
		return -ENOMEM;
	}

	
	//动态向系统申请设备号
	led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
	if(led_dev->dev_major < 0)
	{
		printk(KERN_ERR "register_chrdev error\n");
		ret = -ENODEV;
		goto err_0;
	}




	//创建设备结点
	led_dev->cls = class_create(THIS_MODULE, "led_cls");
	if(IS_ERR(led_dev->cls))
	{
		printk(KERN_ERR "class_create error\n");
		ret = PTR_ERR(led_dev->cls); //½«Ö¸Õë³ö´íµÄ¾ßÌåÔ­Òòת»»³ÉÒ»¸ö³ö´íÂë
		goto err_1;
	}

	led_dev->dev = device_create(led_dev->cls, NULL, 
				MKDEV(led_dev->dev_major, 0), NULL, "led%d", 0);
	if(IS_ERR(led_dev->dev))
	{
		printk(KERN_ERR "device_create error\n");
		ret = PTR_ERR(led_dev->dev); //½«Ö¸Õë³ö´íµÄ¾ßÌåÔ­Òòת»»³ÉÒ»¸ö³ö´íÂë
		goto err_2;
	}


	//将物理地址映射成为虚拟地址,用指针指向这个地址
	led_dev->reg_virt_base = ioremap(GPX2_CON, GPX2_SIZE);
	if(led_dev->reg_virt_base == NULL)
	{
		
		printk(KERN_ERR "ioremap error\n");
		ret = -ENOMEM;
		goto err_3;
	}

	//GPX2_7设置成输出模式
	u32 value = readl(led_dev->reg_virt_base);
	value &= ~(0xf<<28);
	value |= (0x1<<28);
	writel(value, led_dev->reg_virt_base);	

	return 0;

	err_3:
		device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
	
	err_2:
		class_destroy(led_dev->cls);
	
	err_1:
		unregister_chrdev(led_dev->dev_major, "led_dev_test");
	
	err_0:
		kfree(led_dev);
		return ret;

}

static void __exit led_drv_exit(void)
{
	printk("-------%s-------------\n", __FUNCTION__);

	//取消地址映射
	iounmap(led_dev->reg_virt_base);

	//销毁这个设备结点
	device_destroy(led_dev->cls, MKDEV(led_dev->dev_major, 0));
	class_destroy(led_dev->cls);

	
	//释放这个设备号
	unregister_chrdev(led_dev->dev_major, "led_dev_test");

	//释放结构体空间
	kfree(led_dev);

}


module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");


static int kernel_val = 555;

//  read(fd, buf, size);
ssize_t led_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
	//printk("-------%s-------\n", __FUNCTION__);
	
	int ret;
	ret = copy_to_user(buf, &kernel_val, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}

	return 0;
}

ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	//printk("-------%s-------\n", __FUNCTION__);
	
	int ret;
	int value;
	ret = copy_from_user(&value,  buf, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}


	if(value){
		writel( readl(led_dev->reg_virt_base + 4) | (1<<7),   led_dev->reg_virt_base + 4 );
		
	}else{
		writel( readl(led_dev->reg_virt_base + 4) & ~(1<<7),   led_dev->reg_virt_base + 4 );
	}


	
	return 0;
	
}

int led_drv_open(struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n", __FUNCTION__);
	return 0;

}


int led_drv_close(struct inode *inode, struct file *filp)
{
	printk("-------%s-------\n", __FUNCTION__);
	return 0;

}

 

查看实验结果

修改应用程序,识别设备结点为 /dev/led0

编译并移动文件到nfs根目录

root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make
root@linux:/mnt/hgfs/sharefolder/kernel/linux-3.14-fs4412/drivers/mydrivers/chr_drv# make install

开发板加载模块,执行应用程序

[root@farsight drv_module]# ls
chr_drv.ko  chr_test    led_drv.ko  led_test
[root@farsight drv_module]# insmod led_drv.ko
[ 5097.315000] -------led_drv_init-------------
[root@farsight drv_module]# ./led_test
[ 5104.010000] -------led_drv_open-------

 

又可以观察开发板上led是闪烁状态了。