设备树(4) - 应用案例

1. 前言

        使用设备树之前,关于设备的硬件信息都是在arch/arm/plat-xxx和arch/arm/mach-xxx下通过填充结构体的方式进行硬编码,当硬件有小改动时,我们需要修改硬件信息,然后编译内核,在调试阶段反复编译内核是一个很烦、很耗时、没有技术含量的事情;

        然后设备树出现了,使用设备树的情况下,当硬件有小改动时,只需要重新编译设备树文件就好了,不用重新编译内核;

        按道理来说,设备树的机制已经很完美了,但是现在有这样一个任务,一块设备上预留了两个摄像头芯片的位置,分别是高清摄像头和标清摄像头,实际焊接的时候只会焊接一块芯片,高配设备只焊接高清摄像头芯片,低配设备则反之。需求:在kernel初始化过程中,通过MCU发送过来的消息来选择初始化高清摄像头芯片还是标清摄像头芯片。

2. 分析

2.1 改什么地方

        内核将各个device_node转化成各种设备时,会根据其status属性的值来决定是否转化,比如i2c设备,i2c在初始化过程中会遍历i2c节点下的子节点,status属性为okay的子节点就会被注册(i2c_device_register)为i2c设备,设备和驱动匹配后就会进行初始化。因此只需要根据MCU发过来的消息将对应的设备树节点里的status属性值改为okay即可。

2.2 在什么时候改

        dtb块-----device_node-----具体的设备结构体-----设备初始化。

        四个阶段,很明显修改dtb块(二进制形式)的难度很大,不适合尝试,而设备什么时候初始化也不太好把握(但是是可以知道什么时候开始初始化设备的,最先初始化的是platform设备,这些设备的初始化是集中在某一个段里的,所以在这个段执行之前就可以),所以最好的时机就是内核将dtb块解析完毕以后,立刻使用Linux提供的OF系列函数提供的接口,查找、修改节点,我们且把这种方式称作“动态修改设备树”。

3. 动态修改设备树

3.1 选定修改位置

        在博客设备树(三)中有设备树解析的详细流程,从那篇博客可以知道,函数关系如下:
start_kernel--->setup_arch(&command_line)--->unflatten_device_tree();

        在unflatten_device_tree()函数中,会将dtb转化为node结构体,所以修改位置选在unflatten_device_tree()函数内部的末尾、或者紧跟unflatten_device_tree()函数调用的地方最佳。

3.2 具体方案

3.2.1 在kernel/drivers/of/fdt.c中添加两个函数接口,如下:

//因为这个修改是在内核启动很早的时候,内存操作比较敏感,稍有不慎就可能导致系统无法启动,所以这里参照内核解析dtb 时申请内存的方式和步骤(unflatten_dt_node函数),封装了一个创建属性的新接口
static struct property *create_new_property(char *name, void *value, int value_len)
{
    struct property *new_prop;
    void *mem;
    int malloc_size = 0;
    /* 1. calc the whole malloc memory size */
    malloc_size = strlen(name)+1;
    malloc_size += value_len;
    malloc_size += sizeof(struct property);

    /* 2. alloc the memory*/
    mem = early_init_dt_alloc_memory_arch(malloc_size + 4, 4);
    memset(mem, 0, malloc_size + 4);
    new_prop = unflatten_dt_alloc(&mem, sizeof(struct property),
    __alignof__(struct property));
    new_prop->value = unflatten_dt_alloc(&mem, value_len, 1);
    new_prop->name = (char*)unflatten_dt_alloc(&mem, strlen(name)+1, 1);

    /* 3. fill the data*/
    memcpy(new_prop->value, value, value_len);
    new_prop->length = value_len;
    strcpy(new_prop->name, name);

    return new_prop;
}
static void camera_adaption(void)
{
struct device_node *np = NULL;
struct property *new_prop = NULL;

np = of_find_node_by_name(NULL, "Camera_std"); //通过节点名找到需要修改的节点
new_prop = create_new_property("status", "okay", sizeof("okay"));
of_update_property(np, new_prop);    //将status属性修改为okay
}

3.2.2 然后在unflatten_device_tree(void)函数的尾部调用camera_adaption()函数即可(unflatten_device_tree函数在kernel/drivers/of/fdt.c的底部)

void __init unflatten_device_tree(void)
{
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);

	/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
	of_alias_scan(early_init_dt_alloc_memory_arch);

	unittest_unflatten_overlay_base();
    
    //加入动态修改设备树的接口
    camera_adaption();
}
---------------------------------------------------------------------------------
//或者放在调用unflatten_device_tree函数的地方[arch/arm/kernel/setup.c]
void __init setup_arch(char **cmdline_p)
{
    ······
    unflatten_device_tree();
    //加入动态修改设备树的接口
    camera_adaption();
}

4. 动态修改设备树的由来

        对于修改设备树节点这个事儿,刚开始的时候小编也是没什么思绪,网上相关的资料基本没有(可能是因为这种需求不常见吧,万恶的需求,淦······),还好Linux有提供修改设备树的OF函数接口系列,通过阅读内核中提供的文档,我注意到其中一句话的大意是设备树可以被动态修改,于是小编就去搜索动态修改设备树,感谢万能的互联网,真的让我找到了唯一的一篇相关的博文,在参考那篇博文和分析源码的情况下,终于搞定了这个需求。

4.1 总结

        Linux内核自带的文档很有用,很有阅读的必要。

        至于那唯一 一篇相关的博文,我现在已经找不到链接了,所以就不贴出来了(主要还是懒得找,哈哈哈······)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值