Linux驱动-设备树

        设备树文件描述硬件的底层信息,一般由厂家提供,一个.dts文件对应一个ARM设备,一般放在arch/arm/boot/dts下,可由dtc编译为二进制的dtb。

文件包含在.dts 文件中可以通过“#include” 来引用.h.dtsi .dts 文件。

        一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、IIC 等等

数据类型

设备结点

label: node-name@unit-address
​​​​​​​
其中 node-name 是必选项,而 unit-address 则是可选项。 name 是一个 ASCII 字符串,用于描述节点对应的设备类型,如:“uart1 ”,可以清晰的描述出设备的功能。如果一个节点描述的设备有地址,则应该给出@unit-address
可以直接通过 &label 来访问这个节点

        iic结点:

//在 imx6ull.dtsi 文件中
i2c1: i2c@021a0000 { 
    #address-cells = <1>; 
    #size-cells = <0>; 
    compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; 
    reg = <0x021a0000 0x4000>; 
    interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; 
    clocks = <&clks IMX6UL_CLK_I2C1>; 
    status = "disabled"; 
};

//通过 label 引用向其中添加其他设备信息
&i2c1 { 
    clock-frequency = <100000>; 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_i2c1>; 
    status = "okay"; 
   
    mag3110@0e { 
        compatible = "fsl,mag3110"; 
        reg = <0x0e>; 
        position = <2>; 
    }; 
};

标准属性

  • compatible属性
"manufacturer,model"
例如
compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";​​​​​​​
  • model属性:描述设备模块信息
  • status属性:status 属性用来表示节点的状态,其实就是硬件的状态,实际中,基本只用“okay”和“disable”

  • #address-cells和#size-cells属性
为适应不同平台可能会有不同的地址线,规定一个 32 位的长度为一个 cell
"#address-cells" 属性用来表示总线地址需要几个 cell 表示
"#size-cells" 属性用来表示子总线地址空间的长度需要几个 cell 表示
  • reg属性

"reg"属性用来表示节点地址资源的,比如常见的就是寄存器的起始地址及大小。要想表示一块连续地址,必须包含起始地址和空间大小两个参数,如果有多块地址,那么就需要多组这样的值表示。

  • ranges属性
'reg' 属性类似, 不同的是'ranges' 属性的每个元素是三元组,按照前后顺序分别是 ( 子总线地址,父总线地址,大小 )
soc {  
    compatible = "simple-bus"; 
    #address-cells = <1>; 
    #size-cells = <1>; 
    ranges = <0x0 0xe0000000 0x00100000>; 

    serial { 
        device_type = "serial"; 
        compatible = "ns16550"; 
            reg = <0x4600 0x100>;  
            clock-frequency = <0>; 
            interrupts = <0xA 0x8>; 
            interrupt-parent = <&ipic>; 
    }; 
};

of函数获取status属性

/*
 * @Description: of函数获取status属性
 */
#include <linux/init.h>   	//初始化头文件
#include <linux/module.h>   //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>		//添加头文件
int size;					//定义长度
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node; //定义结构体表示我们的节点
struct property *test_node_property;  //定义结构体表示我们的节点属性
static int hello_init(void)
{
	int ret;
	printk("hello world! \n");
	/**********添加我们要查找的节点的代码*******************************/
	// of_find_node_by_path函数通过路径查找节点
	test_device_node = of_find_node_by_path("/test");
	if (test_device_node == NULL)
	{
		//判断是否查找节点成功
		printk("of_find_node_by_path is error \n");
		return -1;
	}
	//打印节点的名字
	printk("test_device_node name is %s\n", test_device_node->name);
	/**********获取compatible属性内容的代码****************************/
	// of_find_property函数查找节点属性
	test_node_property = of_find_property(test_device_node, "compatible", &size);
	if (test_node_property == NULL)
	{
		//判断是否查找到节点属性内容
		printk("test_node_property is error \n");
		return -1;
	}
	//打印属性compatible的名字
	printk("test_node_property name is %s\n", test_node_property->name);
	//打印属性compatible的值
	printk("test_node_property value is %s\n", (char *)test_node_property->value);
	/**********获取reg属性内容的代码**********************************/
	ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
	if (ret < 0)
	{
		//打印获取失败
		printk("of_property_read_u32_array is error  \n"); 
		return -1;
	}
	printk("out_values[0] is 0x%8x\n", out_values[0]);
	printk("out_values[1] is 0x%8x\n", out_values[1]);
	/**********获取status属性内容的代码*********************************/
	// of_property_read_string读取字符串属性
	ret = of_property_read_string(test_device_node, "status", &str); 
	if (ret < 0)
	{
		//打印获取失败
		printk("of_property_read_string is error  \n"); 
		return -1;
	}
	//打印status属性
	printk("status is %s \n", str);
	return 0;
}
static void hello_exit(void)
{
	printk("gooodbye! \n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

设备树下的platform驱动,pinctl和gpio子系统

/*
 * @Description: 设备树下的平台总线驱动,匹配成功后,去设备树文件中获取硬件信息,然后物理地址映射为虚拟地址,接下来可以注册字符设备和杂项设备
 
 */
/*
 * @Description: 蜂鸣器驱动
 */

#include <linux/init.h>			   //初始化头文件
#include <linux/module.h>		   //最基本的文件,支持动态添加和卸载模块。
#include <linux/platform_device.h> //platform平台设备相关的头文件
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/miscdevice.h> //包含了miscdevice结构的定义及相关的操作函数。
#include <linux/fs.h>		  //文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/uaccess.h>	  //包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/io.h>		  //包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/gpio.h>
#include <linux/of_gpio.h>
int size;
int beep_gpio = 0;
int ret = 0;
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;
unsigned int *vir_gpio_dr;

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
	printk("misc_read\n ");
	return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
	/*应用程序传入数据到内核空间,然后控制蜂鸣器的逻辑,在此添加*/

	// kbuf保存的是从应用层读取到的数据
	char kbuf[64] = {0};
	// copy_from_user 从应用层传递数据给内核层
	if (copy_from_user(kbuf, ubuf, size) != 0)
	{
		// copy_from_user 传递失败打印
		printk("copy_from_user error \n "); 
		return -1;
	}
	//打印传递进内核的数据
	printk("kbuf is %d\n ", kbuf[0]); 
	//如果传递进的数据是1,则蜂鸣器响
	if (kbuf[0] == 1)
	{
		gpio_set_value(beep_gpio, 1);
	}
	//如果传递进的数据是0,则蜂鸣器不响
	else if (kbuf[0] == 0)
		gpio_set_value(beep_gpio, 0);
	return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
	printk("hello misc_relaease bye bye \n ");
	return 0;
}
int misc_open(struct inode *inode, struct file *file)
{
	printk("hello misc_open\n ");
	return 0;
}
//文件操作集
struct file_operations misc_fops = {
	.owner = THIS_MODULE,
	.open = misc_open,
	.release = misc_release,
	.read = misc_read,
	.write = misc_write,
};
//杂项设备结构体
struct miscdevice misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "hello_misc",
	.fops = &misc_fops,
};
/**
 * @description: platform 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
 * @param {*}pdev : platform 设备
 * @return {*}0,成功;其他负值,失败
 */
int beep_probe(struct platform_device *pdev)
{
	printk("beep_probe\n");
	//获得设备节点
	test_device_node = of_find_node_by_path("/test");
	if (test_device_node == NULL)
	{
		printk("of_find_node_by_path is error \n");
		return -1;
	}
	/*******我们要使用我们的GPIO就要从设备树来获取**********/

	/* 	of_get_named_gpio函数获取 GPIO 编号,
	因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,
	此函数会将设备树中类似<&gpio1 3 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号 */
	beep_gpio = of_get_named_gpio(test_device_node, "beep-gpio", 0);
	if (beep_gpio < 0)
	{
		printk("of_get_named_gpio is error \n");
		return -1;
	}
	printk("beep_gpio is %d \n", beep_gpio);
	//申请一个 GPIO 管脚
	ret = gpio_request(beep_gpio, "beep");
	if (ret < 0)
	{
		printk("gpio_request is error \n");
		return -1;
	}
	//设置某个GPIO为输出,并且设置默认输出值
	gpio_direction_output(beep_gpio, 1);
	//注册杂项设备
	ret = misc_register(&misc_dev);
	if (ret < 0)
	{
		printk("misc registe is error \n");
	}
	printk("misc registe is succeed \n");
	return 0;
}

int beep_remove(struct platform_device *pdev)
{
	gpio_free(beep_gpio);
	printk("beep_remove\n");
	return 0;
}
const struct platform_device_id beep_idtable = {
	.name = "beep_test",
};
const struct of_device_id of_match_table_test[] = {
	{.compatible = "test1234"},
	{},
};
struct platform_driver beep_driver = {
	//第三步 在beep_driver结构体中完成了beep_probe和beep_remove
	.probe = beep_probe,
	.remove = beep_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "beep_test",
		.of_match_table = of_match_table_test 
	},
	//第四步 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配
	.id_table = &beep_idtable
};

static int beep_driver_init(void)
{
	//第一步 我们看驱动文件要从init函数开始看
	int ret = 0;
	//第二步 在init函数里面注册了平台设备驱动platform_driver
	ret = platform_driver_register(&beep_driver);
	if (ret < 0)
	{
		printk("platform_driver_register error \n");
	}
	printk("platform_driver_register ok \n");

	return 0;
}
static void beep_driver_exit(void)
{
	printk("gooodbye! \n");
	misc_deregister(&misc_dev);
	platform_driver_unregister(&beep_driver);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值