Linux 原生驱动迁移到 OpenHarmony/HDF 的理解
Linux 原生驱动迁移到 OpenHarmony/HDF 的理解
1. 这个问题到底在问什么
这个问题不是简单问“怎么把 Linux 驱动的 .c 文件拿到 OpenHarmony 里编译通过”。
面试官真正想考察的是:
- 你是否能判断原来的 Linux 驱动应该放在 OpenHarmony 的哪一层。
- 你是否理解 HDF 的驱动模型。
- 你是否知道 Linux 驱动和 OpenHarmony/HDF 驱动之间有哪些系统差异。
- 你是否能处理配置、入口、服务接口、权限、编译和产品裁剪等问题。
一句话理解:
这题考的是“驱动架构迁移能力”,不是“改 Makefile 能力”。
2. 迁移前先确认驱动所在层级
迁移前不能一上来就复制代码,而是要先判断驱动属于哪一类。
常见层级包括:
- Linux 内核原生驱动
- 用户态 HAL
- HDF kernel driver
- HDF user driver
原 Linux 驱动一般是:
应用
|
/dev/xxx、sysfs、ioctl
|
Linux kernel driver
|
硬件
OpenHarmony/HDF 中可能是:
应用 / 系统服务
|
HDI / HAL 接口
|
HDF user driver 或 HDF kernel driver
|
内核 / 硬件
所以迁移前要先问几个问题:
- 这个驱动是否必须运行在内核态?
- 目标 OpenHarmony 系统底层是 Linux 内核,还是 LiteOS 等轻量内核?
- 上层是继续通过
/dev/xxx、ioctl访问,还是通过 HDF/HDI 服务访问? - 原驱动是否已经很成熟,是否值得保留?
- 这个驱动是否适合按 HDF 模型重写?
3. 成熟 Linux 驱动不一定要重写
如果原来的 Linux 驱动很成熟,而且目标 OpenHarmony 产品底层仍然是 Linux 内核,通常可以保留原驱动。
例如:
- Wi-Fi
- GPU
- USB
- 摄像头
- 某些复杂外设驱动
这类驱动可以采用:
保留 Linux kernel driver
+
增加适配层 / HAL / HDF 服务接口
也就是说:
- 底层驱动仍然使用 Linux 原来的注册方式。
- 上层通过适配层把能力暴露给 OpenHarmony 系统服务或应用。
- 不一定要把成熟驱动完全改写成 HDF 驱动。
这类迁移重点通常是:
- 调整内核配置
- 适配设备树
- 适配编译系统
- 适配设备节点权限
- 适配 HAL/HDI/HDF 服务接口
- 确认产品配置中启用了相关功能
4. 新驱动可以按 HDF 模型开发
如果是新驱动,或者是比较简单的平台外设驱动,例如:
- GPIO 外设
- I2C 传感器
- SPI 设备
- 简单字符设备
- 板级外设控制驱动
这类驱动可以按 HDF 模型来开发。
HDF 驱动通常要处理以下内容:
HDF 配置
HDF 驱动入口
Bind / Init / Release
服务接口
Dispatch / ioctl-like 调用
OSAL 适配
下面逐个解释。
5. HDF 配置
HDF 配置主要用于告诉系统:
- 这个驱动叫什么
- 什么时候加载
- 是否对外发布服务
- 服务名是什么
- 权限是什么
- 硬件私有参数在哪里
常见配置包括:
device_info.hcs 描述驱动怎么被 HDF 加载
xxx_config.hcs 描述硬件自己的私有参数
device_info.hcs 中常见字段包括:
hostName 驱动属于哪个 host
moduleName 驱动模块名
serviceName 对外发布的服务名
policy 服务是否对外发布
preload 是否开机预加载
priority 加载优先级
permission 设备节点权限
deviceMatchAttr 匹配私有配置
最重要的匹配关系是:
moduleName 要和 HdfDriverEntry 里的 moduleName 对上
deviceMatchAttr 要和私有配置里的 match_attr 对上
serviceName 是上层查找服务时使用的名字
通俗理解:
HDF 配置就像“驱动身份证 + 加载说明书”。系统启动时先看配置,知道这个驱动叫什么、怎么加载、是否发布服务、权限是什么、硬件参数在哪里。
示例:
moduleName = "sample_driver";
serviceName = "sample_service";
deviceMatchAttr = "sample_config";
意思是:
系统要加载一个叫
sample_driver的驱动,它对外服务名叫sample_service,硬件参数去找sample_config。
6. HDF 驱动入口
Linux 驱动常见入口是:
module_init(xxx_init);
module_exit(xxx_exit);
HDF 驱动通常使用:
struct HdfDriverEntry g_sampleDriverEntry = {
.moduleVersion = 1,
.moduleName = "sample_driver",
.Bind = SampleDriverBind,
.Init = SampleDriverInit,
.Release = SampleDriverRelease,
};
HDF_INIT(g_sampleDriverEntry);
其中:
moduleVersion表示驱动版本。moduleName表示驱动模块名,需要和 HDF 配置里的moduleName对上。Bind用于绑定服务接口。Init用于初始化驱动和硬件。Release用于释放资源。HDF_INIT用于把这个驱动入口注册到 HDF 框架。
通俗理解:
Linux 驱动是自己通过
module_init、module_exit注册模块入口;HDF 驱动是把入口结构交给 HDF 框架,由框架根据配置来调用。
7. Bind / Init / Release
7.1 Bind
Bind 主要负责把驱动的服务接口挂到 HDF 框架中。
示例:
int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{
deviceObject->service = &g_sampleService.ioService;
return HDF_SUCCESS;
}
它通常做的事情:
- 创建或获取驱动服务对象。
- 把服务对象挂到
deviceObject->service。 - 让 HDF 框架知道这个驱动可以被上层访问。
通俗理解:
Bind 就是“登记窗口”。告诉 HDF:以后别人找这个驱动服务,就通过这个接口来找。
7.2 Init
Init 是真正初始化驱动和硬件的地方。
示例:
int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
{
// 读取 HCS 配置
// 申请内存
// 初始化 I2C/SPI/GPIO 等总线资源
// 注册中断
// 初始化芯片寄存器
return HDF_SUCCESS;
}
它通常做的事情:
- 读取 HCS 私有配置。
- 解析 GPIO、I2C、SPI、中断号等硬件参数。
- 申请内存。
- 初始化锁、线程、队列等软件资源。
- 初始化硬件。
- 注册中断。
- 检查芯片 ID。
- 设置驱动初始状态。
通俗理解:
Init 就是“正式开工”。比如给传感器上电、读 chip id、初始化寄存器、准备中断和数据结构。
7.3 Release
Release 负责释放资源。
示例:
void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
{
// 注销中断
// 停止线程
// 释放内存
// 销毁锁
// 关闭硬件资源
}
它通常做的事情:
- 注销中断。
- 停止线程。
- 释放内存。
- 销毁锁。
- 关闭 GPIO、I2C、SPI 等资源。
- 清理服务对象。
- 处理初始化失败后的回收。
通俗理解:
Release 就是“收摊”。前面 Init 申请了什么资源,这里就要按相反顺序还回去。
8. 服务接口
HDF 驱动如果要给上层调用,需要定义服务接口。
示例:
struct ISampleService {
struct IDeviceIoService ioService;
int32_t (*ReadData)(void);
int32_t (*SetMode)(uint32_t mode);
};
服务接口的作用是:
- 定义驱动对外提供哪些能力。
- 屏蔽驱动内部实现。
- 让上层通过统一接口访问驱动。
例如一个传感器驱动可以提供:
ReadData 读取传感器数据
SetMode 设置工作模式
Enable 使能设备
Disable 关闭设备
SetRate 设置采样率
通俗理解:
服务接口就像“菜单”。驱动告诉上层:我支持读数据、设置模式、打开、关闭,但你只能按我提供的接口来调用。
9. Dispatch / ioctl-like 调用
Linux 驱动里经常通过 ioctl 处理不同命令:
ioctl(fd, CMD_SET_MODE, arg);
HDF 中常见的方式是通过 Dispatch 处理命令。
基本思路是:
上层发送 command id
参数放到 HdfSBuf
驱动 Dispatch 收到命令
根据 cmd 分发到具体处理函数
结果写回 reply
伪代码:
static int32_t SampleDispatch(
struct HdfDeviceIoClient *client,
int32_t cmd,
struct HdfSBuf *data,
struct HdfSBuf *reply)
{
switch (cmd) {
case CMD_READ_DATA:
return SampleReadData(data, reply);
case CMD_SET_MODE:
return SampleSetMode(data);
default:
return HDF_ERR_NOT_SUPPORT;
}
}
通俗理解:
Dispatch 就是“总接线员”。上层说我要执行 1 号命令、2 号命令,Dispatch 根据命令号转给真正的处理函数。
需要注意:
cmd命令号要定义清楚,不能随意变化。data和reply要检查是否为空。- 要检查参数长度和类型。
- 用户态传来的参数不能直接信任。
- 非法命令要返回错误。
- 涉及敏感操作时要做权限校验。
10. OSAL 适配
OSAL 是 Operating System Abstraction Layer,也就是操作系统抽象层。
Linux 驱动中可能直接使用:
kmalloc
kfree
mutex
spinlock
msleep
request_irq
kthread
workqueue
copy_from_user
copy_to_user
HDF 驱动中通常尽量使用 OSAL 提供的统一接口,例如:
OsalMemCalloc
OsalMemFree
OsalMutex
OsalSpinlock
OsalMSleep
OsalThread
OsalRegisterIrq
OSAL 的作用是:
- 屏蔽不同内核之间的差异。
- 提高驱动在不同 OpenHarmony 系统形态中的可迁移性。
- 避免驱动强依赖 Linux 内核 API。
通俗理解:
OSAL 就像“统一插座转换器”。底层可能是 Linux,也可能是 LiteOS,驱动尽量不要直接依赖某个内核的接口,而是通过 HDF 提供的统一接口操作。
除了 OSAL,还有 PAL。
PAL 是 Platform Abstraction Layer,也就是平台抽象层,常见于:
GPIO
I2C
SPI
UART
PWM
RTC
Watchdog
例如写 I2C 传感器驱动时,应尽量通过 HDF/PAL 提供的统一 I2C 接口访问设备,而不是写死某个平台私有的 I2C API。
11. 迁移时常见风险点
迁移过程中要重点检查:
- OSAL 接口是否适配。
- 锁机制是否适配。
- 内存申请和释放是否正确。
- 线程和工作队列是否需要重写。
- 中断注册和处理方式是否兼容。
- 设备节点是否仍然需要保留。
- 设备节点权限是否正确。
- 用户态和内核态数据传递是否安全。
- 编译系统是否从 Makefile/Kconfig 迁移到对应的 GN/产品配置。
- 产品裁剪配置中是否启用了该驱动。
- HAL/HDI/HDF 服务名是否和上层调用一致。
- 初始化失败时是否能正确释放资源。
风险信号:
如果认为迁移只是复制
.c文件、改一下 Makefile,就说明没有理解 OpenHarmony/HDF 的驱动模型。
12. 面试表达版本
可以这样回答:
如果把 Linux 原生驱动迁移到 OpenHarmony/HDF,我不会一开始就直接复制代码。首先我会确认驱动所在层级,判断它是继续作为 Linux kernel driver 保留,还是改造成 HDF kernel driver、HDF user driver,或者只需要做 HAL/HDI 适配。
如果原来的 Linux 驱动已经很成熟,并且目标系统仍然基于 Linux 内核,我倾向于保留核心驱动,只做设备树、编译配置、权限和上层服务接口适配。这样风险更小,也能复用原来稳定的驱动逻辑。
如果是新驱动,或者需要纳入 HDF 框架统一管理,我会按 HDF 模型来实现。先写 HCS 配置,描述驱动加载信息、服务名、权限、加载优先级和硬件私有参数。然后实现
HdfDriverEntry,通过Bind、Init、Release替代 Linux 驱动中module_init、module_exit的思路。
Bind负责把服务接口挂到 HDF,Init负责读取配置、申请资源、初始化总线和硬件、注册中断,Release负责释放资源。对外调用可以通过服务接口或Dispatch机制完成,Dispatch可以理解为 HDF 版的ioctl,通过命令号和HdfSBuf传递参数。迁移过程中还要重点处理 OSAL、锁、内存、线程、中断、设备节点、权限、编译依赖和产品裁剪配置,不能只停留在复制
.c文件和修改 Makefile。
13. 最短记忆版
HCS 配置:
告诉系统驱动叫什么、怎么加载、是否发布服务、权限是什么、硬件参数在哪里。
DriverEntry:
告诉 HDF 驱动入口在哪里。
Bind:
把服务接口挂出去。
Init:
初始化硬件和软件资源。
Release:
释放资源。
Service:
定义上层能调用哪些能力。
Dispatch:
按命令号处理请求,类似 ioctl。
OSAL/PAL:
把 Linux API 换成 OpenHarmony/HDF 的统一抽象接口。
14. 一句话总结
Linux 驱动迁移到 OpenHarmony/HDF,本质是把原来围绕 Linux 内核接口组织的驱动,改造成符合 OpenHarmony 分层、HDF 配置、服务发布和 OSAL/PAL 抽象的驱动形态。
更多推荐


所有评论(0)