06-设备树led驱动开发

设备树 LED 驱动开发

通过设备树开发 LED 驱动与之前的唯一区别就在于获取 LED 相关寄存器的方式有所变化,之前是直接在驱动文件中定义,现在是通过设备树来定义

设备树 LED 驱动结构体定义

通过设备树开发 LED 驱动,定义结构体时与之前相比多了一个struct device_node *nd

struct dtsled_dev {
	dev_t devid; //设备号
	struct cdev cdev; // cdev
	struct class *class; // 类
	struct device *device; // 设备
	int major; // 主设备号
	int minor; // 次设备号
	struct device_node *nd; //设备节点
};

设备树修改

本章节暂不介绍设备树相关语法,直接按照如下方式修改验证即可
打开设备树文件arch/arm/boot/dts/am335x-boneblack.dts,在根节点中添加如下节点:

	led-demo{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "beaglebone-black-led";
		status = "okay";
		reg = <0x4804C134 0x4 0x4804C13C 0x4>; /*GPIO1_OE_ADDR*/ /*GPIO1_DATAOUT_ADDR*/
	};

编译设备树make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- am335x-boneblack.dtb O=am335x_demo/
使用新生成的设备树文件启动系统

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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>

#define LED_MAJOR 200 /* 主设备号 */
#define LED_NAME "led" /* 设备名字 */

#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *virtual_gpio1_oe_addr;
static void __iomem *virtual_gpio1_dataout_addr;

struct dtsled_dev {
	dev_t devid; //设备号
	struct cdev cdev; // cdev
	struct class *class; // 类
	struct device *device; // 设备
	int major; // 主设备号
	int minor; // 次设备号
	struct device_node *nd; //设备节点
};

struct dtsled_dev dtsled; // led设备

/*
 * @description		: LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * @return 			: 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if (sta == LEDON) {
		val = readl(virtual_gpio1_dataout_addr);
		val &= ~(1 << 21);
		writel(val, virtual_gpio1_dataout_addr);
	} else if (sta == LEDOFF) {
		val = readl(virtual_gpio1_dataout_addr);
		val |= (1 << 21);
		writel(val, virtual_gpio1_dataout_addr);
	}
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &dtsled; // 设置私有数据
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt,
			loff_t *offt)
{
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt,
			 loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

	retvalue = copy_from_user(
		databuf, buf,
		cnt); //必须从用户空间(buf)复制块数据到内核空间(databuf),因为用户空间的地址可能是虚拟地址,不能长久保存
	if (retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0]; /* 获取状态值 */

	if (ledstat == LEDON) {
		led_switch(LEDON); /* 打开LED灯 */
	} else if (ledstat == LEDOFF) {
		led_switch(LEDOFF); /* 关闭LED灯 */
	}
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @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,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init led_init(void)
{
	u32 val = 0;
	int ret;
	u32 regdata[14] = { 0 };
	const char *str;
	struct property *proper;

	/* 获取设备树中的属性数据 */
	/* 1、获取设备节点:alphaled */
	dtsled.nd = of_find_node_by_path("/led-demo");
	if (dtsled.nd == NULL) {
		printk("led-demo node not find!\r\n");
		return -EINVAL;
	} else {
		printk("led-demo node find!\r\n");
	}

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

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

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

	/* 初始化LED */
	virtual_gpio1_oe_addr = of_iomap(dtsled.nd, 0);
	virtual_gpio1_dataout_addr = of_iomap(dtsled.nd, 1);

	printk("virtual_gpio1_oe_addr = %p, virtual_gpio1_dataout_addr = %p\r\n",
	       virtual_gpio1_oe_addr, virtual_gpio1_dataout_addr);

	/* 3、默认关闭LED */
	val = readl(virtual_gpio1_dataout_addr);
	val &= ~(1 << 21);
	writel(val, virtual_gpio1_dataout_addr);

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (dtsled.major) { /*  定义了设备号 */
		dtsled.devid = MKDEV(dtsled.major, 0);
		register_chrdev_region(dtsled.devid, 1, "dtsled");
	} else { /* 没有定义设备号 */
		alloc_chrdev_region(&dtsled.devid, 0, 1,
				    "dtsled"); /* 申请设备号 */
		dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
		dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
	}
	printk("newcheled major=%d,minor=%d\r\n", dtsled.major, dtsled.minor);

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

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

	/* 4、创建类 */
	dtsled.class = class_create(THIS_MODULE, "dtsled");
	if (IS_ERR(dtsled.class)) {
		return PTR_ERR(dtsled.class);
	}

	/* 5、创建设备 */
	dtsled.device =
		device_create(dtsled.class, NULL, dtsled.devid, NULL, "dtsled");
	if (IS_ERR(dtsled.device)) {
		return PTR_ERR(dtsled.device);
	}

	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(virtual_gpio1_oe_addr);
	iounmap(virtual_gpio1_dataout_addr);

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

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

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

测试 APP

测试 APP 同章节 4

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值