Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
Linux嵌入式驱动开发02——驱动编译到内核
Linux嵌入式驱动开发03——杂项设备驱动(附源码)
Linux嵌入式驱动开发04——应用层和内核层数据传输
Linux嵌入式驱动开发05——物理地址到虚拟地址映射
Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写
Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
Linux嵌入式驱动开发08——字符设备(步步为营)
Linux嵌入式驱动开发09——平台总线详解及实战
Linux嵌入式驱动开发10——设备树开发详解
Linux嵌入式驱动开发11——平台总线模型修改为设备树实例
Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作
Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)
Linux嵌入式驱动开发15——等待队列和工作队列
Linux嵌入式驱动开发16——按键消抖实验(内核定时器)
Linux嵌入式驱动开发17——输入子系统
Linux嵌入式驱动开发18——I2C通信
设备树
展开设备树
开放固件,设备树起源于OF,所以我们在设备树中可以看到很多有of字母的函数
设备树代码
更通用的设备树代码,也就是相同芯片但不同平台都可以使用的代码
编译之后得到的DTB文件
设备树编译器
/proc/device-tree
cat model
这里的model打印信息就是我们的dtb文件中的定义的model信息,描述板子信息
通过这种方法,我们可以验证设备书中有没有成功添加进入我们的节点
例如,我们想要看我们添加的gpio的节点信息,进入到gpios文件夹
cd gpios
然后查看
cat compatible
除了通过上面的方法查看,还可以通过下面的方法
/sys/firmware/devicetree/base
在我们的imx6q-c-sabresd.dts文件中,添加我们的测试代码
test1:test{ // test1就是test的别名
#address-cells = <1>;
#size-cells = <1>;
compatible = "test"; // 相当于总线模型中用于匹配的name
reg = <0x20ac000 0x0000004>;
status = "okay";
};
make dtbs
make imx6q-c-sabresd.dtb
tftp方法见文章
uboot使用tftp网络启动加载zImage、dtb到内存,文件系统本地启动(通用!!!)
进入到开发板,可以看到我们的test文件夹,这就是我们刚才新建的一个子节点
打开test文件夹,可以看到我们刚才写入的信息都在里面
这样我们的设备节点就准备好了,剩下的就是等待驱动来跟我们的设备节点匹配,匹配就是通过我们的 compatible名字来进行匹配
现在我们需要修改compatible属性的名称,那么需要怎么修改呢?
一般不要直接修改,而是通过引用来进行修改
代码示例如下:
&gpio_user {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_user>;
fsl,user;
status = "okay";
test1:test{ // test1就是test的别名
#address-cells = <1>;
#size-cells = <1>;
compatible = "test"; // 相当于总线模型中用于匹配的name
reg = <0x20ac000 0x0000004>;
};
};
&test1{ // 直接引用别名就可以了
compatible = "test12345"; // 这样就可以覆盖掉之前的内容
status = "okay"; // 或者添加之前没有的内容
};
现在我们使用设备树来描述硬件信息,Linux给我们提供了一系列的函数来设置节点信息,这个of.h
文件在这个目录下
/include/linux/of.h
struct device_node {
const char *name;
const char *type;
phandle phandle;
const char *full_name;
struct fwnode_handle fwnode;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
struct property {
char *name;
int length;
void *value;
struct property *next;
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
inline struct device_node *of_find_node_by_path(const char *path)
代码中使用函数of_find_node_by_path来找打节点的路径,并提取节点的信息
从dts文件中,可以看到我们的test节点是在gpios节点下
经过板子验证,我们的设备树的节点test确实在gpio节点下,并且test节点的name就是我们的test
我们在driver,c文件中的代码也是跟我们的设备树节点设置一致,路径是“/gpios/test”
,我们只需要验证结果中能否正常输出test名字就可以了
/********获取节点的属性***********/
test_node_property = of_find_property(test_device_node, "compatible", &size);
if(test_node_property == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("test_node_property name is %s\n", test_node_property->name);
printk("test_node_property value is %s\n", (char *)test_node_property->value);
}
因为reg有两个u32类型的数,所以我们使用读u32类型数组的函数of_property_read_u32_array
ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
if(ret < 0) {
printk("of_property_read_u32_array error!!!\n");
return -1;
}else{
printk("out_values[0] is 0x%08x\n", out_values[0]);
printk("out_values[1] is 0x%08x\n", out_values[1]);
}
okay是一个字符串信息,所以使用函数of_property_read_string获得
/********获取节点status的属性***********/
ret = of_property_read_string(test_device_node, "status", &str);
if(ret < 0) {
printk("of_property_read_string error!!!\n");
return -1;
}else{
printk("of_property_read_string status is 0%s\n", str);
}
return 0;
#include
#include
#include
int size;
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;
static int driver_init(void)
{
int ret = 0;
printk("driver_init ok!!!\n"); // 在内核中无法使用c语言库,所以不用printf
/********查找指定路径的节点***********/
test_device_node = of_find_node_by_path("/gpios/test");
if(test_device_node == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("of_find_node_by ok!!!\n");
printk("test_device_node name is %s\n", test_device_node->name);
}
/********获取节点compatible的属性***********/
test_node_property = of_find_property(test_device_node, "compatible", &size);
if(test_node_property == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("test_node_property name is %s\n", test_node_property->name);
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 error!!!\n");
return -1;
}else{
printk("out_values[0] is 0x%08x\n", out_values[0]);
printk("out_values[1] is 0x%08x\n", out_values[1]);
}
/********获取节点status的属性***********/
ret = of_property_read_string(test_device_node, "status", &str);
if(ret < 0) {
printk("of_property_read_string error!!!\n");
return -1;
}else{
printk("of_property_read_string status is 0%s\n", str);
}
return 0;
}
static void driver_exit(void)
{
printk("driver_exit bye!!!\n");
}
module_init(driver_init);
module_exit(driver_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可