嵌入式Linux驱动 GPIO操作改进 包括从驱动到测试程序

嵌入式Linux驱动 GPIO操作改进 包括从驱动到测试程序

说明

这篇博客记录的是上一次编写GPIO驱动的改进版本。这里改进了两个地方:

  • 面向对象的编程思想 结构体来表示一个对象
  • 做出错判断
    • 在某个位置出错,就要在那儿释放申请的内存

字符的驱动编写小结

字符设备驱动编写步骤

这里出现的代码是从后面的程序拷贝出来的。

字符设备驱动编写步骤:

  • 实现加载和卸载的入口函数
    • module_init(chr_io_init);
    • module_exit(chr_io_exit);
  • 模块加载入口函数中:
  • 申请主设备号(用于区分和管理不同的字符设备)
    • register_chrdev(DEV_MAJOR , “io”,&file_ops);
  • 创建设备节点文件(为用户提供一个可操作的文件接口)
    • struct class* class_create(THIS_MODULE, “io_cls”);
    • struct device* device_create(devcls, NULL, dev_num, NULL, “io”);
  • 硬件的初始化
    • 地址的映射
      ioremap(GPIOL2_CON, LEN);
    • 中断的申请
    • 实现硬件的寄存器初始化
  • 实现file_operations(文件接口的实现)

操作寄存器的方式总结

  • 操作寄存器地址的方式
    • 举例:volatile unsigned long *gpxcon;
    • gpxcon &=~(1<<10);
    • static inline u32 readl(const volatile void __iomem *addr)
      //从寄存器读值
      static inline void writel(unsigned int b, volatile void __iomem *addr)
      //向寄存器写值
    • 举例(这里的代码是会在后面出现的)
	    u32 value = readl(led_dev->reg_virt_base);
	    value &=~(1<<0);
	    value |=1<<0;
	    writel(value, led_dev->reg_virt_base);
	    gpiol20_dat = gpiol20_cof+1;
		 writel(read(led_dev->reg_virt_base+4) |(1<<0), led_dev->reg_virt_base+4);

代码

#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>

#define GPIOL2_CON 0x11000000+0x0100
#define LEN 8

/*
用一个类来管理
*/
struct led_desc{
	unsigned int dev_major;
	struct class *cls;
	struct device *dev;
	void* reg_virt_base;
};

static struct led_desc *led_dev;

ssize_t chr_drv_read (struct file *filp, char __user *buffer, size_t count, loff_t *fpos)
{
	u32 value=0;
	int ret;
	printk("-------%s-------\n",__FUNCTION__);
	
	value = readl(led_dev->reg_virt_base+4);
	value &=1<<0;
	ret = copy_to_user(buffer, &value, count);
	return 0 ;
}

ssize_t chr_drv_write (struct file *flip, const char __user *buffer, size_t count, loff_t *fpos)
{
	int ret;
	int value;
	static int num=0;
	printk("write:%d ",num++);
	ret = copy_from_user(&value, buffer, count);

	if(value == 1)
	{
	printk("vaule:%d \n",value);
	writel(readl(led_dev->reg_virt_base+4) |(1<<0), 				led_dev->reg_virt_base+4);
	}
	else
	{
	printk("vaule:%d \n",value);
	writel(readl(led_dev->reg_virt_base+4) & ~(1<<0), led_dev->reg_virt_base+4);
	}
	
	return 0;
}

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

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

static struct file_operations file_ops={
	
		.owner = THIS_MODULE,
		.open = chr_drv_open,
		.read = chr_drv_read,
		.write = chr_drv_write,
		.release = chr_drv_release,
};
		
static int __init chr_io_init(void)
{
	int ret;
	u32 value;
	printk("-----------%s--------\n",__FUNCTION__);
	//GFP_KERNEL 如果内存不够用函数会一直阻塞(休眠)
	led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
	if(led_dev == NULL)
	{
		//KERN_ERR 用于筛选调试信息
		printk(KERN_ERR "malloc error!\n");
		return  -ENOMEM;
	}
	
	//动态分配设备节点
	led_dev->dev_major = register_chrdev(0 , "led",&file_ops);
	
	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(GPIOL2_CON, LEN);
	if(IS_ERR(led_dev->reg_virt_base))
	{
		printk(KERN_ERR "ioremap error!\n");
		ret = -ENOMEM;
		goto err_3;
	}
	//规范的输入输出方式
	value = readl(led_dev->reg_virt_base);
	value &=~(1<<0);
	value |=1<<0;
	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, "led0"); //先释放设备号 再释放内存空间 
err_0:
	kfree(led_dev);//防止在发生错误的时候以前申请的内存得不到释放 
	return ret;
}

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

	iounmap(led_dev->reg_virt_base);
	unregister_chrdev(led_dev->dev_major, "led0");
	device_destroy(led_dev->cls, MKDEV(led_dev->dev_major,0)); //和创建相反
	class_destroy(led_dev->cls);
	
}

MODULE_LICENSE("GPL");
module_init(chr_io_init);
module_exit(chr_io_exit);


发布了18 篇原创文章 · 获赞 92 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_27350133/article/details/84797086
今日推荐