linux驱动开发学习2 设备树
设备树
dtb板级信息文件,大势所趋,一定要学
如何确定要编译哪个DTS文件:查看linux下的arch/arm/boot/dts/Makefile
DTS语言有属于自己的语法:以树形来描述设备信息
包括外部的灯、按键、传感器,内部的中断控制器,IIC、 SPI、 RAM 、晶振、芯片等
学习文件:imx6ull-alientek-emmc.dts、Devicetree SpecificationV0.2.pdf、Power_ePAPR_APPROVED_v1.12.pdf三个文件
结构:一级子节点,二级子节点,三级子节点。。。
@后面的绝对地址 一般都是外设寄存器的起始地址
/dts-v1/;#include.h
#include .dtsi/{model "Freescale i.MX6 ULL 14x14 EVK Board";compatible "fsl,imx6ull-14x14-evk","fsl,imx6ull";chosen{};memory{//一级子节点};reserved-memory{};backlight{};pxp_v412{};
};
通过查看设备树可以获得例如IIC的频率和地址 才能写IIC设备的驱动文件,还要看mag3110的驱动手册
imx6ull-alientek-emmc.dts中添加自定义节点
可以在 /proc/device-tree/中查看
特殊设备树aliases:作用 起别名
gpio0 = &gpio1
gpio1 = &gpio2
设备树的属性
#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。
#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性
=========================================================================
根节点/下面的compatible属性
绑定信息文档
Linux内核的OF操作函数~(驱动如何获取到设备树中节点信息)
重点在于如何使用of函数 1 获取节点 2 获取节点属性值
驱动要想获取到设备树节点内容,首先要找到节点
查找父/子节点的 OF 函数
提取属性值的 OF 函数
#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 <linux/of.h>#include <linux/of_address.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名 : dtsled.c作者 : 左忠凯版本 : V1.0描述 : LED驱动文件。其他 : 无论坛 : www.openedv.com日志 : 初版V1.0 2019/7/9 左忠凯创建***************************************************************/#define DTSLED_CNT 1 /* 设备号个数 */#define DTSLED_NAME "dtsled" /* 名字 */#define LEDOFF 0 /* 关灯 */#define LEDON 1 /* 开灯 *//* 映射后的寄存器虚拟地址指针 */static void __iomem *IMX6U_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; /* 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(GPIO1_DR);val &= ~(1 << 3); writel(val, GPIO1_DR);}else if(sta == LEDOFF) {val = readl(GPIO1_DR);val|= (1 << 3); writel(val, GPIO1_DR);} }/** @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);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];const char *str;struct property *proper;/* 获取设备树中的属性数据 *//* 1、获取设备节点:alphaled of_find_node_by_path函数 */dtsled.nd = of_find_node_by_path("/alphaled");if(dtsled.nd == NULL) { /* 失败 */printk("alphaled node nost find!\r\n");return -EINVAL;} else {printk("alphaled 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, 10);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 */#if 0/* 1、寄存器地址映射 *//* 所有的寄存器信息被取值以后都保存再regdata[]数组中,所以可以操作数据取值 */IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);GPIO1_DR = ioremap(regdata[6], regdata[7]);GPIO1_GDIR = ioremap(regdata[8], regdata[9]);#else/* 这里就是与第三个实验不一样的地方,他不是自己写在.c里的,而是从设备树里取值*//* 可以使用of_iomap直接完成 设备节点 到 虚拟地址的内存映射,而不需要数据取值但一定要注意是 of_iomap 获得的是reg属性的值 */IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);GPIO1_DR = of_iomap(dtsled.nd, 3);GPIO1_GDIR = of_iomap(dtsled.nd, 4);#endif/* 2、使能GPIO1时钟 */val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26); /* 清楚以前的设置 */val |= (3 << 26); /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 3、设置GPIO1_IO03的复用功能,将其复用为* GPIO1_IO03,最后设置IO属性。*/writel(5, SW_MUX_GPIO1_IO03);/*寄存器SW_PAD_GPIO1_IO03设置IO属性*bit 16:0 HYS关闭*bit [15:14]: 00 默认下拉*bit [13]: 0 kepper功能*bit [12]: 1 pull/keeper使能*bit [11]: 0 关闭开路输出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 110 R0/6驱动能力*bit [0]: 0 低转换率*/writel(0x10B0, SW_PAD_GPIO1_IO03);/* 4、设置GPIO1_IO03为输出功能 */val = readl(GPIO1_GDIR);val &= ~(1 << 3); /* 清除以前的设置 */val |= (1 << 3); /* 设置为输出 */writel(val, GPIO1_GDIR);/* 5、默认关闭LED */val = readl(GPIO1_DR);val |= (1 << 3); writel(val, GPIO1_DR);/* 注册新版字符设备驱动 一共分两步 注册设备号 和 添加设备节点*//* 1、创建设备号 */if (dtsled.major) { /* 定义了设备号 */dtsled.devid = MKDEV(dtsled.major, 0);register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);} else { /* 没有定义设备号 */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); /* 2、新的字符设备号注册方法初始化cdev在linux中使用cdev结构体表示一个字符设备,include/linux/cdev.h */dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);/* 3、添加一个cdev */cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);//* 通过mdev来实现设备文件节点的自动创建与删除 *//* 自动创建设备节点 *//* 4、创建类 */dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if (IS_ERR(dtsled.class)) {return PTR_ERR(dtsled.class);}/* 5、创建设备 */dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if (IS_ERR(dtsled.device)) {return PTR_ERR(dtsled.device);}return 0;}/** @description : 驱动出口函数* @param : 无* @return : 无*/static void __exit led_exit(void){/* 取消映射 */iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/* 注销字符设备驱动 */cdev_del(&dtsled.cdev);/* 删除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("zuozhongkai");
#include "stdio.h"#include "unistd.h"#include "sys/types.h"#include "sys/stat.h"#include "fcntl.h"#include "stdlib.h"#include "string.h"/***************************************************************Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.文件名 : ledApp.c作者 : 左忠凯版本 : V1.0描述 : chrdevbase驱测试APP。其他 : 无使用方法 :./ledtest /dev/led 0 关闭LED./ledtest /dev/led 1 打开LED 论坛 : www.openedv.com日志 : 初版V1.0 2019/1/30 左忠凯创建***************************************************************/#define LEDOFF 0#define LEDON 1/** @description : main主程序* @param - argc : argv数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/int main(int argc, char *argv[]){int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;}
linux驱动开发学习2 设备树相关推荐
- Linux驱动开发中与设备树相关的6种debug方法
整理出了6种驱动开发时与设备注册.设备树相关的调试方法,彼此间没有优先级之分,每种方法不一定是最优解,但可以作为一种debug查找问题的手段,快速定位问题原因.例如在芯片验证时,不同时钟频率下系统启动 ...
- Linux驱动开发:字符设备驱动开发实战
Linux驱动开发:字符设备驱动开发实战 一.工程创建 VSCode 创建工程,设置 C/C++ 配置,导入 linux kernel 源码目录,方便 vscode 写代码自动补全,vscode 配置 ...
- 驱动开发基础知识——设备树
BSP开发工程师[原来BSP就是那些被指臃肿的文件啊 BSP的出生 Linux经过不断的发展,原先嵌入式系统的三层结构逐步演化成为一种四层结构. 这个新增加的中间层次位于操作系统和硬件之间,包含了系统 ...
- 一、linux驱动开发-8.2-设备树下platform驱动
一.简介 之前没用设备树的时候,是分别编写并注册platform_device和platform_driver来表示设备和驱动,如果使用设备树,设备的描述就放在设备树中,因此只需要实现platform ...
- Linux驱动开发学习笔记【12】:Linux自带LED灯驱动
目录 一.内核自带LED驱动使能 二.内核自带LED驱动分析 三.内核自带LED驱动使用 一.内核自带LED驱动使能 在Linux内核中,已经自带了LED灯的驱动程序,使用的就是platform平台驱 ...
- Linux驱动开发 15 块设备驱动框架
CAN I.MX6ULL 带有两个 CAN 控制器: FlexCAN1 和 FlexCAN2 , NXP 官方的 EVK 开发板这两个 CAN 接口都用到了,因此 NXP 官方的设备树 ...
- 一、linux驱动开发-8.1-platform设备驱动
一.驱动的分离与分层 1.1.驱动的分隔与分离 通过驱动的分隔,也就是将主机驱动和设备驱动分隔开来,通过总线就行匹配,当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配 的 ...
- Linux驱动开发学习笔记【8】:Linux中断系统
目录 一.Linux内核中断处理过程 1.1.裸机中断 1.2.linux中断 二.linux中断的上半部和下半部 2.1 软中断 2.2 tasklet 2.3 工作队列 2.4 中断线程化 三.设 ...
- linux驱动私有数据,linux驱动开发之字符设备--私有数据和container_of
前言 驱动开发中通常为设备定义一个设备相关的设备结构体,其包含该设备的cdev .私有数据.信号量.irq等这些信息. 驱动开发中通常将文件的私有数据private_data指向设备结构体,在read ...
最新文章
- 推荐8个值得每天一看的网站,值得收藏起来!
- CCF201403-1 相反数(100分)
- Linux下部署开源版“禅道”项目管理系统
- PM——【1】维护功能位置主数据
- 慎用AXIS2(续)
- 音频处理十:( mp3 与wav 格式的相互转换)
- 工作184:自定义事件
- 获取客户端的IP地址
- python distance matrix_Python 矩阵转置的几种方法小结
- SCRUM的五个事件
- STM32标准库与HAL库中的Mode和Pull设置
- matlab画图不显示中文_[过时] [LaTeX 使用] 升级 macOS 10.15 后 ctex 文档不显示中文的临时方案...
- 【DDD】领域驱动设计实践 —— Application层实现
- Scratch编程与美术:制作美丽的烟花!
- 微信扫描二维码快速登录网站
- 成都榆熙:做拼多多电商如何优化用户消费体验?
- Algorithm保姆级笔记(基础+提高+Top1+杂题+Top2+蓝桥杯)
- 进军数字货币新业务,500.com能否“涅槃”重生?
- set_xscale 表示x轴缩放比例,一张图明明白白
- 计算机视觉大型攻略 —— SLAM(2) Graph-based SLAM(基于图优化的算法)