1. Linux内核中模拟外设的两种实现方案
在嵌入式Linux开发中,我们经常需要为尚未到货的硬件编写驱动程序。这时候,用普通内存模拟外设寄存器就成了一项必备技能。最近我在开发一个GPIO扩展芯片驱动时,就采用了这种技术方案。通过内核的地址映射机制,我们可以把任意一段物理内存当作设备寄存器来访问,实现完整的读写测试流程。
1.1 方案选型与技术背景
在Linux内核中模拟外设主要涉及两个关键技术点:内存预留和地址映射。内存预留确保这段内存不会被系统另作他用,地址映射则让内核能够以访问设备寄存器的方式操作这段内存。
传统上有两种实现方式:
- 引用预留内存:在设备树中声明reserved-memory节点,然后通过memory-region属性引用
- 直接寄存器声明:在设备节点中直接用reg属性声明寄存器区域
第一种方式更适合复杂的存储区域划分场景,比如需要预留大块连续内存时。第二种方式则更接近真实硬件设备的描述方式,适合寄存器区域明确的设备。
关键区别:memory-region方式下,驱动需要通过of_parse_phandle解析内存区域;而reg属性方式可以直接用platform_get_resource获取资源。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 设备树配置详解
2.1 预留内存方式的设备树配置
先看第一种方式的设备树配置示例:
dts复制reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
/* 4KB内存用于模拟寄存器 */
syscon_mem: syscon-mem@10000000 {
reg = <0x70000000 0x400>;
no-map;
};
};
ioremapdemo {
compatible = "demo,ioremap";
memory-region = <&syscon_mem>;
};
这里有几个关键点需要注意:
no-map属性告诉内核不要为这段内存创建直接映射- 地址0x70000000需要根据具体平台的内存布局调整
- 大小0x400(1KB)足够模拟大多数简单外设的寄存器组
2.2 直接寄存器声明的设备树配置
第二种方式的设备树更为简洁:
dts复制reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
syscon_mem: syscon-mem@10000000 {
reg = <0x70000000 0x400>;
no-map;
};
};
ioremapdemo {
co
