两种源文件:
xxxxx.dts
, dts是device tree source的缩写xxxxx.dtsi
, dtsi是device tree source include的缩写,意味着这样源文件用于被dts文件包含用实际使用时,需要把dts文件编译成对应的二进制文件(.dtb文件
,dtb是device tree binary的缩写 )便于运行时存放在内存加快读取信息的速度
dts文件
主体内容由多个节点组成0或多个子节点
,形成树状关系
key-value键值对
来表示节点语法:
[label:] node-name[@unit-address] {
[properties definitions];
[child nodes];
};
label: 可选项,节点别名,为了缩短节点访问路径,后续节点中可以使用 &label 来表示引用指定节点
node-name: 节点名
unit-address: 设备地址,一般填写该设备寄存器组或内存块的首地址
properties definitions:属性定义
child nodes:子节点
属性语法:
[label:] property-name = value;
[label:] property-name;
属性可以无值
有值的属性,可以有三种取值:
1. arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示,空格分隔),用尖括号表示(< >)
2. string(字符串), 用双引号表示(" ")
3. bytestring(1个或多个字节,空格分隔),用方括号表示([])
4. 用,分隔的多值
根节点表示整块开发板的信息
#address-cells // 在子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells // 在子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备,即描述其兼容哪些平台
model // 比如有2款板子配置基本一致, 它们的compatible是一样的,那么就通过model来分辨这2款板子
所有设备树文件的必需节点,它定义了系统物理内存的 layout
device_type = "memory";
reg //用来指定内存的地址、大小
传递内核启动时使用的参数parameter
bootargs //字符串,内核启动参数, 跟u-boot中设置的bootargs作用一样
/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu
所以 /cpus
中有以下2个属性:
#address-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size) 必须设置为0
数字形式的节点标识,在后续节点中属性值性质表示某节点时,可以引用对应节点
如:
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node {
interrupt-parent = <1>; // 使用phandle值为1来引用上述节点
};
reg属性:表示内存区域region,语法:
reg = <address1 length1 [address2 length2] [address3 length3]>;
#address-cells:reg属性中, 使用多少个u32整数来描述地址(address),语法:
#address-cells = <数字>;
#size-cells:reg属性中, 使用多少个u32整数来描述大小(size),语法:
#size-cells = <数字>;
驱动和设备(设备节点)的匹配依据,compatible(兼容性)的值可以有不止一个字符串以满足不同的需求,语法:
compatible = "字符串1","字符串2",...;
a. 中断控制器节点用的属性:
interrupt-controller
一个无值空属性用来声明这个node接收中断信号,表示该节点是一个中断控制器
#interrupt-cells
这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符
b. 中断源设备节点用的属性:
interrupt-parent
:标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的,语法:
interrupt-parent = <引用某中断控制器节点>
interrupts 一个中断标识符列表,表示每一个中断输出信号,语法:
interrupts = <中断号 触发方式>
1 low-to-high 上升沿触发
2 high-to-low 下降沿触发
4 high level 高电平触发
8 low level 低电平触发
gpio也是最常见的IO口,常用的属性有:
a. 对于GPIO控制器:
gpio-controller
,无值空属性,用来说明该节点描述的是一个gpio控制器
#gpio-cells
,用来表示要用几个cell描述一个 GPIO引脚
b. 对于GPIO使用者节点:
gpio使用节点的属性
xxx-gpio = <&引用GPIO控制器 GPIO标号 工作模式>
工作模式:
1 低电平有效 GPIO_ACTIVE_HIGH
0 高电平有效 GPIO_ACTIVE_LOW
一般来说,每一种设备的节点属性设置都会有一些套路,比如可以设置哪些属性?属性值怎么设置?那怎么知道这些套路呢,有两种思路:
struct device_node
对应设备树中的一个节点
struct property
对应节点中一个属性
/**
include/of.h
of_find_node_by_path - 通过路径查找指定节点
@path - 结点在设备树中的路径,包含节点名也可以是节点的别名
成功:得到节点的首地址;失败:NULL
*/
struct device_node * of_find_node_by_path(const char *path);
/*
include/of.h
of_find_property - 提取指定属性的值
@np - 设备节点指针
@name - 属性名称
@lenp - 属性值的字节数
成功:属性值的首地址;失败:NULL
*/
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);
/**
* include/of_gpio.h
* of_get_named_gpio - 从设备树中提取gpio口
* @np - 设备节点指针
* @propname - 属性名
* @index - gpio口引脚标号
* 成功:得到GPIO口编号;失败:负数,绝对值是错误码
*/
int of_get_named_gpio(struct device_node *np, const char *propname, int index);
/*
功能:获得设备树中的中断号并进行映射
参数:node:设备节点
index:序号
返回值:成功:中断号 失败:错误码
*/
unsigned int irq_of_parse_and_map(struct device_node *node, int index);
of_property_read_string
/*
of_property_read_string - 提取字符串(属性值)
@np - 设备节点指针
@propname - 属性名称
@out_string - 输出参数,指向字符串(属性值)
成功:0;失败:负数,绝对值是错误码
*/
int of_property_read_string(struct device_node *np, const char *propname, const char **out_string);
读数值
int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)
int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value)
int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)
判断属性是否存在
int of_property_read_bool(const struct device_node *np,const char *propname)
读数组
int of_property_read_u32_array(const struct device_node *np,const char *propname,u32 *out_value,size_t sz)
int gpio_request(unsigned gpio,const char *label)
功能:其实就是让内核检查一下该GPIO引脚是否被其它设备占用,如果没有占用则返回0并用label做一下标记,表示被本设备占用,否则返回负数
gpio:得到的设备号
label:设备结点属性名称
void gpio_free(unsigned gpio)
功能:去除本设备对该GPIO的占用标记,表示本设备向内核归还对该GPIO引脚的使用权,此后其它设备可占用该GPIO引脚
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio,int value)
value:0-低电平,1-高电平
int gpio_get_value(unsigned gpio)
int gpio_set_value(unsigned gpio,int value)
..../linux3.14/arch/arm/boot/dts/exynos4412-fs4412.dts
fs4412-leds {
compatible = "fs4412,led2-5";
led2-gpio = <&gpx2 7 0>;
led3-gpio = <&gpx1 0 0>;
led4-gpio = <&gpf3 4 0>;
led5-gpio = <&gpf3 5 0>;
};
struct device_node
类型的地址值)of_get_named_gpio
函数得到某个GPIO的编号
truct leddev结构体
中记录
所有用到的GPIO编号
gpio_request函数向内核申请占用该引脚
,不用
该引脚时可通过gpio_free归还给内核
gpio_direction_input
和gpio_direction_output
函数来设置某个GPIO的作用例子程序:
dev_led2:
/*************************************************************************
> File Name: led2.c
> Author: xiuchengzhen
> CSDN: xiuchengzhen.blog.csdn.net
> Created Time: Tue 10 May 2022 07:29:52 PM PDT
************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include "dev_led2.h"
int major = 11; //主设备号
int minor = 0; //次设备号
int dev_num = 1; //设备数量
struct pdev //设备结构体
{
struct cdev leddev; //led结构体
int led2_num; //GPIO口编号
int led3_num;
int led4_num;
int led5_num;
};
struct pdev *pleddev = NULL;
int led_open(struct inode *pnode, struct file *pfile) //打开设备
{
/* 申请GPIO引脚占用 */
gpio_request(pleddev->led2_num, "led2");
gpio_request(pleddev->led3_num, "led3");
gpio_request(pleddev->led4_num, "led4");
gpio_request(pleddev->led5_num, "led5");
return 0;
}
int led_release(struct inode *pnode, struct file *pfile) //关闭设备
{
/* 申请归还GPIO引脚 */
gpio_free(pleddev->led2_num);
gpio_free(pleddev->led3_num);
gpio_free(pleddev->led4_num);
gpio_free(pleddev->led5_num);
return 0;
}
long leddev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case LED2_ON:
gpio_direction_output(pleddev->led2_num,1);
break;
case LED2_OFF:
gpio_direction_output(pleddev->led2_num,0);
break;
case LED3_ON:
gpio_direction_output(pleddev->led3_num,1);
break;
case LED3_OFF:
gpio_direction_output(pleddev->led3_num,0);
break;
case LED4_ON:
gpio_direction_output(pleddev->led4_num,1);
break;
case LED4_OFF:
gpio_direction_output(pleddev->led4_num,0);
break;
case LED5_ON:
gpio_direction_output(pleddev->led5_num,1);
break;
case LED5_OFF:
gpio_direction_output(pleddev->led5_num,0);
break;
default:
printk("cmd error!\n");
return -1;
}
return 0;
}
struct file_operations fops={
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.unlocked_ioctl = leddev_ioctl,
};
int __init led2_init(void)
{
dev_t devno = MKDEV(major, minor);
int ret = -1;
if((ret = register_chrdev_region(devno, dev_num, "dev_led")) < 0)
{
ret = alloc_chrdev_region(&devno, minor, dev_num, "dev_led");
if(ret < 0)
{
printk("alloc_chrdev_region fail!\n");
return -1;
}
major = MAJOR(devno);
}
/* 设备分配内存空间 */
pleddev = (struct pdev *)kmalloc(sizeof(struct pdev), GFP_KERNEL);
if(pleddev == NULL)
{
unregister_chrdev_region(devno, dev_num);
printk("pmydev malloc fail!\n");
return -1;
}
/* 设备初始化 */
cdev_init(&pleddev->leddev, &fops);
pleddev->leddev.owner = THIS_MODULE;
/* 设备添加 */
cdev_add(&pleddev->leddev, devno, dev_num);
/* 设备号获取 */
struct device_node *led_node = of_find_node_by_path("/fs4412-leds");
if(led_node == NULL)
{
printk("device_node get fail!\n");
return -1;
}
pleddev->led2_num = of_get_named_gpio(led_node, "led2-gpio", 0);
pleddev->led3_num = of_get_named_gpio(led_node, "led3-gpio", 0);
pleddev->led4_num = of_get_named_gpio(led_node, "led4-gpio", 0);
pleddev->led5_num = of_get_named_gpio(led_node, "led5-gpio", 0);
return 0;
}
void __exit led2_exit(void)
{
dev_t devno = MKDEV(major, minor);
/* 注销设备 */
cdev_del(&pleddev->leddev);
/* 注销设备号 */
unregister_chrdev_region(devno, dev_num);
/* 释放空间 */
kfree(pleddev);
}
MODULE_LICENSE("GPL");
module_init(led2_init);
module_exit(led2_exit);
dev_led2.h:
/*************************************************************************
> File Name: mytest.h
> Author: xiuchengzhen
> CSDN: xiuchengzhen.blog.csdn.net
> Created Time: Mon 16 May 2022 05:39:30 AM PDT
************************************************************************/
#ifndef DEVLED_H
#define DEVLED_H
#include <asm/ioctl.h>
#define IOCTL_TYPE 'k'
#define LED2_ON _IOR(IOCTL_TYPE, 1,int *)
#define LED2_OFF _IOR(IOCTL_TYPE, 2,int *)
#define LED3_ON _IOR(IOCTL_TYPE, 3,int *)
#define LED3_OFF _IOR(IOCTL_TYPE, 4,int *)
#define LED4_ON _IOR(IOCTL_TYPE, 5,int *)
#define LED4_OFF _IOR(IOCTL_TYPE, 6,int *)
#define LED5_ON _IOR(IOCTL_TYPE, 7,int *)
#define LED5_OFF _IOR(IOCTL_TYPE, 8,int *)
#endif
dev_app.c
:
/*************************************************************************
> File Name: led_app.c
> Author: xiuchengzhen
> CSDN: xiuchengzhen.blog.csdn.net
> Created Time: Fri 20 May 2022 01:26:49 AM PDT
************************************************************************/
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <time.h>
#include "dev_led.h"
int main(int argc, const char *argv[])
{
int ret;
if(argc < 2)
{
printf("Run without file!\n");
return -1;
}
/* 打开设备文件 */
int fd = open("/dev/dev_led", O_RDWR);
if(fd < 0)
{
perror("open");
return -1;
}
while(1)
{
ioctl(fd, LED2_ON, 0);
sleep(1);
ioctl(fd, LED2_OFF, 0);
sleep(1);
ioctl(fd, LED3_ON, 0);
sleep(1);
ioctl(fd, LED3_OFF, 0);
sleep(1);
ioctl(fd, LED4_ON, 0);
sleep(1);
ioctl(fd, LED4_OFF, 0);
sleep(1);
ioctl(fd, LED5_ON, 0);
sleep(1);
ioctl(fd, LED5_OFF, 0);
sleep(1);
}
/* 关闭设备 */
close(fd);
}
到这里就结束啦!