linux led 设备树,Linux学习笔记(16)——基于设备树的LED设备驱动

在设备树文件(.dts)中添加LED相关信息

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;

}

编译LED驱动和测试文件,并复制到NFS根文件系统

4.1 编译产生dtsled.ko文件:

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

测试程序

5.1 重启开发板,查看设备树文件:

/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灯灭掉。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值