led {
#address-cell = <1>; /* 表示用一个32位的数来描述地址 */
#size-cell = <1>; /* 用1个32位的数来描述该地址的大小 */
compatible = "led";
status = "okey";
reg = < 0x020C406C 0x04 /* ccm_ccgr1 */
0x020E0068 0x04 /* sw_mux_gpio1_io03 */
0x020E02F4 0x04 /* sw_pad_gpio1_io03 */
0x0209C000 0x04 /* gpio1_dr */
0x0209C004 0x04 >; /* gpio1_gdir */
};
编译设备树文件生成设备树目标文件(.dtb)
编译设备树文件 glen@ubuntu:~/linux/imx6ull/linux/glen_linux$ make dtbs
产生设备对目标文件
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
make[1]: “include/generated/mach-types.h”已是最新。
CHK include/generated/bounds.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
DTC arch/arm/boot/dts/imx6ull-glen-emmc.dtb
编写根据设备树文件LED相关信息编写对应驱动程序及测试程序
/*
* 文件名 : dtsled.c
* 作者 : glen
* 描述 : dtsled驱动文件
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DTSLED_CNT 1 /* 设备个数 */
#define DTSLED_NAME "dtsled" /* 设备名称 */
#define LEDON "ON" /* 打开 */
#define LEDOFF "OFF" /* 关闭 */
/* 映射后的寄存器虚拟地址指针
*/
static void __iomem *ccm_ccgr1;
static void __iomem *sw_mux_gpio1_io03;
static void __iomem *sw_pad_gpio1_io03;
static void __iomem *gpio1_dr;
static void __iomem *gpio1_gdir;
/* dtsled设备结构体 */
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_dev; /* led设备 */
/**
* \brief : LED控制
* \par : status LEDON(打开) LEDOFF(关闭)
* \retval : 无
*/
void led_ctrl(const char *status)
{
u32 val = 0;
if (strcmp(status, LEDON) == 0) {
val = readl(gpio1_dr);
val &= ~(1 << 3);
writel(val, gpio1_dr);
} else if (strcmp(status, LEDOFF) == 0) {
val = readl(gpio1_dr);
val |= (1 << 3);
writel(val, gpio1_dr);
}
}
/**
* \brief : 打开设备
* \par : inode 传递给驱动
* filp 设备文件
* \retval : 0 成功 其它 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
/* 设置私有数据 */
filp->private_data = &dtsled_dev;
return 0;
}
/**
* \brief : 从设备读取数据
* \par : filp 要读取的设备文件(文件描述符)
* buf 返回给用户的数据缓冲区
* cnt 要读取的数据长度
* offt 相对于文件首地址的偏移
* \retval : 读取的字节数, 如果为负值, 表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/**
* \brief : 向设备写数据
* \par : filp 要写入的设备文件(文件描述符)
* buf 要写给用户的数据
* cnt 要写入的数据长度
* offt 相对于文件首地址的偏移
* \retval : 写入的字节数, 如果为负值, 表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
u8 data_buf[4];
ret = copy_from_user(data_buf, buf, ((cnt < sizeof(LEDOFF)) ? cnt : sizeof(LEDOFF)));
if (ret < 0) {
printk("Kernel write failed!\r\n");
return -EFAULT;
}
led_ctrl((const char *)data_buf);
return 0;
}
/**
* \brief : 关闭/释放设备
* \par : inode 传递给驱动
* filp 设备文件
* \retval : 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,
};
/**
* \brief : 驱动入口函数
* \par : 无
* \retval : 无
*/
static int __init led_init(void)
{
u32 val = 0;
int ret;
u32 reg_data[14];
const char *str;
struct property *prop;
/* 获取设备树中的属性数据
* 1. 获取设备节点 led
*/
dtsled_dev.nd = of_find_node_by_path("/led");
if (dtsled_dev.nd == NULL) {
printk("LED node can not found!\r\n");
return -EINVAL;
} else {
printk("LED node has been found!\r\n");
}
/* 2. 获取compatible属性内容 */
prop = of_find_property(dtsled_dev.nd, "compatible", NULL);
if (prop == NULL) {
printk("Compatible proterty can't be found!\r\n");
} else {
printk("compatible = %s\r\n", (char *)prop->value);
}
/* 3. 获取status属性内容 */
ret = of_property_read_string(dtsled_dev.nd, "status", &str);
if (ret < 0) {
printk("Status readed failed!\r\n");
} else {
printk("status = %s\r\n", str);
}
/* 4. 获取reg属性内容 */
ret = of_property_read_u32_array(dtsled_dev.nd, "reg", reg_data, 10);
if (ret < 0) {
printk("reg property read failed!\r\n");
} else {
u8 i = 0;
printk("reg data:");
for (i = 0; i < 10; i++) {
printk("%#X\t", reg_data[i]);
}
printk("\r\n");
}
/* 初始化LED */
ccm_ccgr1 = of_iomap(dtsled_dev.nd, 0);
sw_mux_gpio1_io03 = of_iomap(dtsled_dev.nd, 1);
sw_pad_gpio1_io03 = of_iomap(dtsled_dev.nd, 2);
gpio1_dr = of_iomap(dtsled_dev.nd, 3);
gpio1_gdir = of_iomap(dtsled_dev.nd, 4);
/* 使能GPIO1时钟 */
val = readl(ccm_ccgr1);
val |= (3 << 26);
writel(val, ccm_ccgr1);
/* 设置GPIO1_IO03的复用功能, 将其复用为GPIO1_IO03 */
writel(5, sw_mux_gpio1_io03);
/* 设置GPIO1_IO03的电气属性 */
writel(0x10B0, sw_pad_gpio1_io03);
/* 设置GPIO1_IO03为输出 */
val = readl(gpio1_gdir);
val |= (1 << 3);
writel(val, gpio1_gdir);
/* 默认关闭LED */
val = readl(gpio1_dr);
val |= (1 << 3);
writel(val, gpio1_dr);
/* 注册字符设备驱动
* 创建设备号
*/
if (dtsled_dev.major) {
/* 已定义了主设备号 */
dtsled_dev.devid = MKDEV(dtsled_dev.major, 0);
/* 申请设备号 */
register_chrdev_region(dtsled_dev.devid, DTSLED_CNT, DTSLED_NAME);
} else {
/* 申请设备号 */
alloc_chrdev_region(&dtsled_dev.devid, 0, DTSLED_CNT, DTSLED_NAME);
/* 获取分配设备号的主设备号 */
dtsled_dev.major = MAJOR(dtsled_dev.devid);
/* 获取分配设备号的次设备号 */
dtsled_dev.minor = MINOR(dtsled_dev.devid);
}
printk("dtsled_dev major=%d, minor=%d\r\n", dtsled_dev.major, dtsled_dev.minor);
/* 初始化cdev */
dtsled_dev.cdev.owner = THIS_MODULE;
cdev_init(&dtsled_dev.cdev, &dtsled_fops);
/* 添加一个cdev */
cdev_add(&dtsled_dev.cdev, dtsled_dev.devid, DTSLED_CNT);
/* 创建类 */
dtsled_dev.class = class_create(THIS_MODULE, DTSLED_NAME);
if (IS_ERR(dtsled_dev.class)) {
return PTR_ERR(dtsled_dev.class);
}
/* 创建设备 */
dtsled_dev.device = device_create(dtsled_dev.class, NULL, dtsled_dev.devid, NULL, DTSLED_NAME);
if (IS_ERR(dtsled_dev.device)) {
return PTR_ERR(dtsled_dev.device);
}
return 0;
}
/**
* \brief : 驱动出口函数
* \par : 无
* \retval : 无
*/
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(ccm_ccgr1);
iounmap(sw_mux_gpio1_io03);
iounmap(sw_pad_gpio1_io03);
iounmap(gpio1_dr);
iounmap(gpio1_gdir);
/* 注销字符设备驱动 */
cdev_del(&dtsled_dev.cdev);
/* 注销设备号 */
unregister_chrdev_region(dtsled_dev.devid, DTSLED_CNT);
device_destroy(dtsled_dev.class, dtsled_dev.devid);
class_destroy(dtsled_dev.class);
}
/*
* 设备注册入口和出口
*/
module_init(led_init);
module_exit(led_exit);
/* 模块的许可证声明
* static const char __UNIQUE_ID_license__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "license=GPL";
*/
MODULE_LICENSE("GPL");
/* 模块的作者声明
* static const char __UNIQUE_ID_author__COUNTER__[] \
* __used __attribute__((section(".modinfo"), unused, aligned(1))) \
* = "author=glen_cao"
*/
MODULE_AUTHOR("glen");
/*
* 文件名 : dtsled_test.c
* 作者 : glen
* 描述 : dtsled测试程序
*/
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/**
* @brief : main函数
* @par : argc argv数组元素的个数
* argv 参数数组
* @retval : 0 成功 其它 失败
*/
int main(int argc, char *argv[])
{
int fd, ret;
char *filename;
char data_buf[1];
if (argc != 3) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
/* 向/dev/led文件写入数据 */
ret = write(fd, argv[2], sizeof(argv[2]));
if (ret < 0) {
printf("LED control failed!\r\n");
close(fd);
return -1;
} else {
printf("Write parameter is %s\r\n", argv[2]);
}
/* 关闭文件 */
ret = close(fd);
if (ret < 0) {
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
glen@ubuntu:~/linux/imx6ull/linux/driver/4_dtsled$ make
make -C /home/glen/linux/imx6ull/linux/glen_linux M=/home/glen/linux/imx6ull/linux/driver/4_dtsled modules
make[1]: 进入目录“/home/glen/linux/imx6ull/linux/glen_linux”
CC [M] /home/glen/linux/imx6ull/linux/driver/4_dtsled/dtsled.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/glen/linux/imx6ull/linux/driver/4_dtsled/dtsled.mod.o
LD [M] /home/glen/linux/imx6ull/linux/driver/4_dtsled/dtsled.ko
make[1]: 离开目录“/home/glen/linux/imx6ull/linux/glen_linux”
glen@ubuntu:~/linux/imx6ull/linux/driver/4_dtsled$
4.2 编译产生目标文件
glen@ubuntu:~/linux/imx6ull/linux/driver/4_dtsled$ arm-linux-gnueabihf-gcc dtsled_test.c -o dtsled_test
4.3 把目标文件复制到NFS根文件系统:
glen@ubuntu:~/linux/imx6ull/linux/driver/4_dtsled$ sudo cp dtsled.ko dtsled_test ../../../../nfs/rootfs/lib/modules/4.1.15/ -f
/sys/firmware/devicetree/base # ls
#address-cells memory
#size-cells model
aliases name
backlight pxp_v4l2
chosen regulators
clocks reserved-memory
compatible soc
cpus sound
interrupt-controller@00a01000 spi4
led
可以看到,已经存在led文件夹。
5.2 加载LED驱动模块:
/lib/modules/4.1.15 # insmod dtsled.ko
LED node has been found!
compatible = led
status = okey
reg data:0X20C406C 0X4 0X20E0068 0X4 0X20E02F4 0X4 0X209C000 0X4 0X209C004 0X4
dtsled_dev major=249, minor=0
5.3 打开LED
/lib/modules/4.1.15 # ./dtsled_test /dev/dtsled ON
Write parameter is ON
在目标板上可以观察到LED灯亮起来。
5.4 关闭LED
/lib/modules/4.1.15 # ./dtsled_test /dev/dtsled OFF
Write parameter is OFF
在目标板上可以观察到LED灯灭掉。