看到之前的代码,第一反应是什么?这他妈的是什么狗屎。静态变量哪里放的都是,代码乱的根本就没人看。
那我们来梳理和规范运行代码。
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);
就是把地址指针里面的值读出来,或者是往里面写值,这是一种比较官方的写法,更严谨。