设备树的方式--LED字符设备驱动

Linux内核使用了设备树之后,开发者就不需要再自己定义寄存器地址,自己手动映射虚拟地址,也不需要去配置IO属性等,

内核提供了一系列 of_xxxxxx(); 函数,这些函数可以从设备书中读取节点信息或属性,用于操作设备。(映射,配置,操作等)。

这些 OF 函数原型都定义在 include/linux/of.h 文件中。

Linux 内核使用 device_node 结构体来描述一个节点:

查找节点有关的 OF 函数有 5 个:

 

查找父子节点的 of 函数:

提取属性值的 of 函数:

Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中

 

其他常用 of 函数:

 

以下是采用设备树的方式,构建的LED驱动程序框架:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>

#define DTSLED_CNT		1  	/*设备号的个数*/
#define DTSLED_NAME	"dtsled"/*设备名*/
#define	LED_ON		0	/*灯状态:开*/
#define	LED_OFF		1	/*灯状态:关*/

/*定义映射后的寄存器变量,后边可以直接使用操作硬件*/
static void __iomem *CRU_CLKGATE14_CON;	/*时钟使能寄存器GPIO1--8,带写保护*/
static void __iomem *GRF_GPIO8A_IOMUX;	/*IO功能复用寄存器,带写保护*/
static void __iomem *GPIO_SWPORTA_DDR;	/*GPIO方向寄存器,不带写保护*/
static void __iomem *GPIO_SWPORTA_DR;	/*GPIO数据寄存器,不带写保护*/

/*dts 设备结构体*/
struct dtsled_dev{
	struct cdev cdev;	/*设备描述结构体*/
	struct class *class;	/*类*/
	struct device *device;	/*设备*/
	dev_t devid;		/*设备号*/
	int major;		/*主设备号*/
	int minor;		/*次设备号*/

};

struct dtsled_dev dtsled;	/*定义设备*/


static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &dtsled;	/*一般在打开设备时将设备赋给私有数据*/
	printk("This is open func in driver!\r\n");
	return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int ret = 0;
	unsigned char databuf[1];

	ret = copy_from_user(databuf, buf, cnt);
	if(ret < 0)
	{
		printk("kernel write failed\r\n");
		return -EFAULT;
	}

	if(databuf[0] == LED_ON)
	{
		//*GPIO_SWPORTA_DR &= ~(1<<1);
		writel((~(1<<1)),GPIO_SWPORTA_DR);
		printk("亮\r\n");
	}else if(databuf[0] == LED_OFF)
	{
		//*GPIO_SWPORTA_DR |= (1<<1);
		writel((1<<1),GPIO_SWPORTA_DR);
		printk("灭\r\n");
	}
	return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static struct file_operations dtsled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = led_release,
};


// 重头戏
static int __init led_init(void)
{
	int ret;
	struct property *proper;
	const char* str;
	unsigned int regdata[8];
	struct device_node *nd;	/*设备节点*/
	/*从设备树中获取属性*/
	/*获取节点*/
	printk(KERN_ALERT "This is init func !\r\n");
	nd = of_find_node_by_path("/alphaled");
	if(nd == NULL){
		printk(KERN_ALERT "alphaled node can't found!\r\n");
		return -EINVAL;
	}else{
		printk(KERN_ALERT "alphaled node has been found!\r\n");
	}

	#if 1
	/*获取compatible属性内容*/
	proper = of_find_property(nd, "compatible", NULL);
	if(proper == NULL)
	{
		printk(KERN_ALERT "compatible property find failed\r\n");
	}else{
		printk(KERN_ALERT "compatible = %s\r\n", (char*)proper->value);
	}

	/*获取status属性内容*/
	ret = of_property_read_string(nd, "status", &str);
	if(ret < 0)
	{
		printk(KERN_ALERT "status read failed!\r\n");
	}else{
		printk(KERN_ALERT "status = %s\r\n",str);
	}
	#endif

	/*获取reg属性的内容*/
	ret = of_property_read_u32_array(nd, "reg", regdata, 8);
	if(ret < 0)
	{
		printk(KERN_ALERT "reg property read failed!\r\n");
	}else{
		u8 i = 0;
		printk(KERN_ALERT "reg data:\r\n");
		for(i = 0; i < 8; i++)
		printk(KERN_ALERT "%X ", regdata[i]);
		printk(KERN_ALERT "\r\n");
	}


	#if 1				//此种方式可行,但和之前的方式没什么区别
	CRU_CLKGATE14_CON = ioremap(regdata[0], regdata[1]);
	GRF_GPIO8A_IOMUX = ioremap(regdata[2], regdata[3]);
	GPIO_SWPORTA_DDR = ioremap(regdata[4], regdata[5]);
	GPIO_SWPORTA_DR = ioremap(regdata[6], regdata[7]);

	#endif
#if 0				//以下才是大部分驱动的正确使用方式,但是在rk3288上,运行就回出现段错误,而且没有排查出原因。

	/*初始化LED*/
	CRU_CLKGATE14_CON = of_iomap(nd, 0);			//这一步就出错
	if(CRU_CLKGATE14_CON == NULL)
	{
		printk(KERN_ALERT "get iomap failed !\r\n");
		return -1;
	}
	

	printk("CRU_CLK14_CON = %x \n",(unsigned int)CRU_CLKGATE14_CON);

	GRF_GPIO8A_IOMUX  = of_iomap(nd, 1);

	GPIO_SWPORTA_DDR  = of_iomap(nd, 2);

	GPIO_SWPORTA_DR   = of_iomap(nd, 3);
	#endif




	/* 使能GPIO8A的时钟 */
//	*CRU_CLKGATE14_CON  = ( (1<<(8+16)) | (0<<8) );	
	val = ( (1<<(8+16)) | (0<<8) );
	writel(val,CRU_CLKGATE14_CON);

	/* 设置GPIO8_A1的复用,复用为GPIO功能 */
//	*GRF_GPIO8A_IOMUX = ( 3<<(2+16) | (0<<2));
	val = ( 3<<(2+16) | (0<<2));
	writel(val,GRF_GPIO8A_IOMUX);

	/* 设置电器属性,即配置成输出模式 */
//	*GPIO_SWPORTA_DDR |= ( 1<<1 );
	val = ( 1<<1 );
	writel(val,GPIO_SWPORTA_DDR);

	/* 默认关灯 */
//	*GPIO_SWPORTA_DR |= (1<<0);
	val = (1<<0);
	writel(val,GPIO_SWPORTA_DR);





	/*注册字符设备*/
	dtsled.major = 0;	/*设备号由内核分配*/
	if(dtsled.major)
	{
		dtsled.devid = MKDEV(dtsled.major, 0);
		ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
	} else {	/*没有定义设别号,向内核申请*/
			ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);
			dtsled.major = MAJOR(dtsled.devid);
			dtsled.minor = MINOR(dtsled.devid);
			printk("dtsled: major=%d, minor=%d\r\n",dtsled.major,dtsled.minor);
		}

	if(ret < 0)
	{
		printk("register char dev fail\r\n");
		return ret;
	}


	/*初始化cdev*/
	dtsled.cdev.owner = THIS_MODULE;
	cdev_init(&dtsled.cdev, &dtsled_fops);

	/*添加一个cdev*/
	cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);

	/*创建类*/
	dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
	if(dtsled.class == NULL)
	{
		printk("create class fail\r\n");
		return -1;
	}


	/*创建设备*/
	dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
	if(dtsled.device == NULL)
	{
		printk("create device fail\r\n");
		return -1;
	}

	return ret;
}


static void __exit led_exit(void)
{
	/*取消映射*/
	iounmap(CRU_CLKGATE14_CON);
	iounmap(GRF_GPIO8A_IOMUX);
	iounmap(GPIO_SWPORTA_DDR);
	iounmap(GPIO_SWPORTA_DR);

	/*注销字符设备驱动*/
	cdev_del(&dtsled.cdev);
	unregister_chrdev_region(dtsled.devid, DTSLED_CNT);	/*注销设备号*/

	device_destroy(dtsled.class, dtsled.devid);
	class_destroy(dtsled.class);
}


module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kevin");

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值