OpenHarmony驱动开发三条路径:HDF、内核模块与配置适配
上周有个朋友问我一个问题:同样是把手里的开发板外设跑起来,为什么有的教程让写HDF驱动,有的教程直接把一个
.ko
内核模块编进去,还有的教程只是改改内核配置就把CH340、CP2102这类芯片识别出来了?他一开始以为这三种说法是新旧版本差异,翻了很多OpenHarmony资料还是懵。
这个困惑很典型。OpenHarmony的驱动开发不像单纯的Linux驱动那样只有一条主线,它天然存在三条路径:官方主推的HDF驱动框架、传统Linux内核模块、以及内核配置层面的外设适配。这三条路径不是替代关系,而是对应不同场景。折腾过几个项目之后,我越来觉得这个“三条路径”的框架值得单独写一篇梳理清楚,因为大多数人在开头选错路径,后面就一直在跟框架、配置、编译环境较劲,反而把真正的硬件工作给耽误了。
下面的内容适合三类人:刚接触OpenHarmony、想知道驱动该从哪下手的初学者;已经在Linux里写过驱动、想对比HDF差异的移植开发者;以及只想把手头板子的串口、网口、传感器快速用起来、不打算深挖框架的“拿来主义”选手。
1. 先区分三条路径:不是新旧之争,而是场景之分
1.1 为什么OpenHarmony驱动开发不能只学一种姿势
OpenHarmony的驱动子系统在设计上跟传统Linux有一个巨大的区别:它同时在支撑LiteOS和Linux两类内核,还要给上层应用提供统一、可裁剪的硬件访问能力。如果沿用Linux那套“一切皆文件、驱动就是file_operations”的模式,上层服务就得直接面对内核版本差异、设备节点混乱、权限管理各自为政的问题。
所以OpenHarmony在Linux内核驱动之上又建了一层HDF(HarmonyOS Driver Foundation)框架。HDF做的事情,相当于给驱动安了一个统一“入口系统”:驱动模块怎么被加载、怎么暴露服务、上层应用怎么找到这个设备、权限怎么控制,全部由HDF接管。这就是第一条路径,也是官方最推荐的方式。
但问题在于,HDF框架本身有学习成本,而且OpenHarmony的Linux内核仍然保留了完整的Linux驱动能力。于是就有了第二条路径:直接写一个传统内核模块,
insmod
加载,用
/dev
节点或者procfs、sysfs跟用户态通信。这种方式在快速验证硬件寄存器、移植现成Linux驱动、做原型Demo时非常高效。
至于第三条路径,是最容易被“搞驱动的人”忽略的:OpenHarmony内核本质上还是基于Linux内核维护的,内核源码的Kconfig里躺着大量已经写好的现成驱动。很多外设根本不需要你写一行驱动代码,只需要在
defconfig
里把对应的配置项打开,重新编译内核,硬件就被识别了。USB转串口芯片、USB网卡、存储控制器、I2C控制器,全都属于这一类。
这三条路径的关系可以用一个很朴素的类比:HDF是给设备建了一个正规的“物业服务处”,设备要上报、要跟业主沟通都得经过物业;裸内核模块是“自己开门做生意”,直接面对内核和设备,灵活但管理粗糙;内核配置则是“精装房直接拎包入住”,前提是房子已经盖好了,你只需要把门锁换掉。
1.2 三条路径的选型对照表
| 路径 | 开发方式 | 典型场景 | 学习成本 | 与OpenHarmony系统服务的关联 |
|---|---|---|---|---|
| HDF驱动框架 | 用HDF提供的DriverEntry模型,编写Bind/Init/Release,配HCS设备描述符 | 新产品量产的正式驱动、需要接入系统电源管理/服务发布/权限控制的设备、传感器/显示/音频/输入类设备 | 高 | 强。上层HAL和服务可以直接通过HDF接口访问设备 |
| 传统Linux内核模块 | 写module_init/module_exit,通过file_operations或miscdevice提供节点 | 快速验证硬件寄存器、移植现成Linux驱动、临时调试工具、不想接HDF的内部模块 | 中 | 弱。设备节点对上层是独立的,系统服务感知不到,需要自己处理权限 |
| 内核配置适配 | 修改defconfig,打开Kconfig里现成的驱动配置项 | USB转串口、USB网卡、存储、部分显示/声卡、调试器适配 | 低 | 中。设备节点正常生成,但仍然是传统Linux设备模型,不走HDF |
实际项目里,这三条路径经常是混用的。我见过很多量产方案的驱动架构是:核心传感器走HDF,带日志和在线升级能力;一块临时用的扩展板先挂个裸模块跑起来验证;主板上的CH340串口和USB网卡直接通过内核配置打开,完全没写驱动。
1.3 贯穿本文的实验参考
后面章节里用到的命令和路径,我主要以OpenHarmony 3.2/4.x版本的Linux 5.10内核为参照,这套操作在RK3568这类主流开发板上比较通用。如果你用的是其他版本或者其他芯片平台,位置可能略有差异,但逻辑完全一样。关键就是要会对自己手里的源码树“找到对应位置”,别死记路径。
准备一个最小实验环境,我建议不要一上来就买开发板。先在你的电脑上装个Ubuntu,配合QEMU跑一个x86的OpenHarmony镜像,或者直接在x86电脑上刷OpenHarmony的社区桌面版本,这样反复编译内核、验证模块的成本最低。等把整个编译、打包、启动流程摸熟,再切到真机板子上操作,问题会少很多。社区里经常有人问“为什么我的开发板编译一次要半小时、刷机还要拆Flash”,多半就是没先在虚拟机里把流程跑通。
2. HDF驱动框架路径:OpenHarmony官方主推的驱动开发方式
2.1 HDF的基本运行模型:从设备描述符到DriverEntry
HDF驱动的核心思想是“描述与实现分离”。你写了一个驱动,但它什么时候加载、挂在哪个host下面、对外怎么命名,不是由C代码里的
module_init
决定的,而是由HCS设备描述符决定的。
HCS是HDF的配置格式,后缀是
.hcs
,编译时会转成二进制,最终打包进系统镜像。系统启动时,HDF框架会解析这些描述符,把设备树里的节点和驱动模块里的
moduleName
配对,配对成功就调驱动的
Bind
和
Init
。所谓“设备挂载”,本质上是这个配对过程。
我自己刚开始接触HDF时,最大的误区是先写C代码,写完才发现不知道怎么把驱动“挂上去”。后来才意识到,对HDF来说,
.hcs
是驱动骨架的一部分,甚至比C代码更关键。一个驱动能不能被加载、设备节点怎么暴露,基本靠HCS描述符来控制。
2.2 一个最小HDF字符驱动骨架
先看C代码部分。这里用一个最小的LED驱动举例,它不做具体硬件操作,只把驱动生命周期打印出来,让你确认框架确实加载了它:
#include "hdf_base.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"
#define HDF_LOG_TAG my_led_driver
static int32_t MyLedBind(struct HdfDeviceObject *device)
{
HDF_LOGI("MyLedBind enter");
return HDF_SUCCESS;
}
static int32_t MyLedInit(struct HdfDeviceObject *device)
{
HDF_LOGI("MyLedInit enter");
return HDF_SUCCESS;
}
static void MyLedRelease(struct HdfDeviceObject *device)
{
HDF_LOGI("MyLedRelease enter");
}
struct HdfDriverEntry g_myLedDriverEntry = {
.moduleVersion = 1,
.moduleName = "my_led_driver",
.Bind = MyLedBind,
.Init = MyLedInit,
.Release = MyLedRelease,
};
HDF_INIT(g_myLedDriverEntry);
这段代码的套路是固定的。
Bind
负责把驱动跟设备服务绑定,
Init
做硬件初始化,
Release
做释放。绝大多数HDF驱动都是这个模板加具体业务逻辑。
注意
moduleName
是字符串
"my_led_driver"
,后面
.hcs
描述符里必须有一个节点的
moduleName
跟它完全一致,大小写都不能差。这里的
HDF_INIT
宏相当于传统驱动的
module_init
,但它注册的是HDF驱动入口,不是内核模块入口。
2.3 HCS设备描述符:驱动是怎么被挂到系统里的
接着写设备描述符,新建一个
my_led_config.hcs
:
root {
my_led_host :: host {
device_my_led :: device {
device0 :: deviceNode {
policy = 2;
priority = 100;
permission = 0664;
moduleName = "my_led_driver";
deviceMatchAttr = "my_led_config";
}
}
}
}
这个格式看着唬人,拆开其实就三部分。
host
是一个独立的驱动宿主环境,你可以把它理解为一个驱动仓,里面放一堆同类型的设备驱动。
device
表示这个host下挂了一类设备。
deviceNode
才是具体的设备节点,声明了驱动的名字、匹配属性和对外权限。
policy
字段值得单独说。它决定驱动的服务模式:
0
表示不对外发布服务,
1
表示内核态可用,
2
表示同时支持内核态和用户态访问。很多新手写HDF驱动后发现用户态程序打不开设备,大概率就是
policy
设成了0或者1。
配好之后,需要把这个
.hcs
文件加入到驱动构建的hdf hcs列表中。不同版本的构建组织方式不太一样,但常见做法是在
device/board/xxx/liteos_a
或对应的
hdf
配置目录里添加
hcs
条目,然后执行整包编译。先别管编译细节,重要的是理解这个流程:C代码负责实现,HCS负责描述,编译打包把两者配对,启动阶段完成加载。
2.4 为什么要优先考虑HDF:系统层面的收益
可能有人觉得,HDF这套流程比传统Linux驱动繁琐多了。但HDF带来的收益也是实实在在的。第一,用户态驱动的支持。HDF允许驱动逻辑的一部分跑在用户态,这样可以把复杂的算法逻辑放到用户态调,省得动不动就崩内核。第二,服务发布与订阅。驱动的能力可以通过HDF的服务接口发布给上层,应用不需要知道设备节点具体是
/dev/hdf/xxx
还是
/dev/xxx
,只要拿到服务句柄就行。第三,系统级节电和状态管理。HDF驱动可以更方便地接入OpenHarmony的电源管理、热插拔和权限控制体系。
真实项目里,如果这个设备是主板上的核心外设,比如陀螺仪、触控屏、指纹模组、音频Codec,我一定建议走HDF。因为后面系统框架层的传感器服务、输入服务、音频服务默认就是从HDF拿数据,你如果只写一个裸模块,想对接正式框架就得自己搭一套胶水层,工作量反而更大。
3. 裸内核模块路径:绕过HDF直接操作内核的务实选择
3.1 什么时候该放弃HDF去写.ko
既然HDF是官方推荐,为什么还要聊裸模块?因为实际开发中,HDF不是万能的。以下几种情况,裸模块反而是更高效的选择。
第一种是快速验证。芯片寄存器地址都不知道对不对的时候,要先用一段最简单的代码读写寄存器,确认上电时序和引脚定义。这种验证代码没必要写成HDF驱动,一个
.ko
加载进去、
dmesg
看输出、完事卸载,十分钟就验证完了。
第二种是移植现成Linux驱动。很多传感器、显示屏、网卡芯片,Linux内核里已经有厂商驱动或者开源驱动,代码形态是标准的内核模块。要把它们搬到OpenHarmony上,最快的方式是先当成裸模块编译跑通,确认硬件功能正常,再慢慢封装成HDF驱动接进系统。直接一上来就改造成HDF,很可能把驱动本身的问题和框架适配问题混在一起,排查很痛苦。
第三种是内核内部的调试工具。比如你想抓GPIO中断频率、要看电源域的寄存器状态、要给某个设备临时加一个调试接口。这些工具没必要暴露给上层服务,裸模块加个debugfs入口就够用了。
3.2 模块Makefile和依赖管理
OpenHarmony内核里编译裸模块的方式跟标准Linux几乎一样。写一个最简单的模块:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init demo_init(void)
{
printk(KERN_INFO "demo module loaded\n");
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "demo module unloaded\n");
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("your-name");
Makefile长这样:
obj-m := demo.o
KERNELDIR := /path/to/openharmony/kernel/linux
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- clean
这里有几个细节要提醒。
KERNELDIR
指向的必须是OpenHarmony实际编译用的内核源码树,而且这个源码树不能是全新的,得是已经执行过内核配置、生成了
Module.symvers
和头文件链接的树。你拿一台普通电脑上编译好的Ubuntu内核源码树去编OpenHarmony的模块,大概率会报版本不一致。交叉编译工具链前缀按平台改,ARM64用
aarch64-linux-gnu-
,ARM32用
arm-linux-gnueabihf-
。
编译完会得到
demo.ko
。传到开发板上执行:
insmod demo.ko
dmesg | tail
lsmod
rmmod demo
如果模块依赖其他符号,比如依赖内核里某个导出函数,编译完会出现
demo.ko
但
insmod
时说“unknown symbol”。这时候你要检查被依赖的模块是否已经加载,或者它的符号是否导出了。
3.3 开机自动加载与节点权限问题
裸模块不像HDF那样有HCS描述符自动管理,开机自启需要自己做。最快的方案是把
insmod
命令写进系统启动脚本里,或者把
.ko
放到内核模块加载目录,用
modprobe
配合
modules.alias
加载。OpenHarmony不同版本的自启脚本位置不太一样,常见的是
init
进程的cfg文件或者
/system/etc/init/*.cfg
,格式类似:
{
"services": [{
"name": "demo_module",
"path": ["/vendor/bin/insmod", "/vendor/modules/demo.ko"],
"once": true
}]
}
还有一个很容易被忽略的问题:裸模块生成的设备节点的权限。默认情况下,设备节点可能是
root:root
、权限
0600
,上层应用根本打不开。这时候要么改
udev
规则(如果内核支持),要么在模块里初始化设备时用
class_create
和
device_create
创建好节点并设置好权限,要么在系统服务里用
chmod
和
chown
修正。HDF框架里一个
permission = 0664
字段就能搞定的事,裸模块需要自己处理,这也是很多人说“裸模块不好做产品”的原因之一。
3.4 裸模块与HDF互操作的边界
还有一个小众但很实际的需求:能不能让HDF驱动去调用一个裸模块的导出接口?答案是可以的。裸模块可以用
EXPORT_SYMBOL
导出函数,HDF驱动的C代码直接声明外部函数来调用,链接层面和普通内核符号一样。反过来,裸模块访问HDF服务就比较麻烦,因为HDF的服务接口不是简单内核符号,要走HDF提供的API。
所以我的经验是:核心外设走HDF,辅助调试和一次性验证走裸模块。两个世界可以共存,但别把业务逻辑拆得稀碎,否则代码维护起来很痛苦。
4. 内核配置路径:让现成驱动直接可用的60%解法
4.1 你不需要写代码的外设驱动:串口芯片、调试器与网卡
很多刚接触OpenHarmony的人,一听说“驱动”两个字就觉得要写C代码。实际上,OpenHarmony内核沿用了Linux内核的配置体系,Kconfig里已经包含了成千上万个现成驱动。你要做的只是把对应的配置开关打开。拿大家都熟悉的几个芯片举例。
比如CH340、CH341这类USB转串口芯片,对应配置项是:
CONFIG_USB_SERIAL_CH341=y
CP2102对应:
CONFIG_USB_SERIAL_CP210X=y
FT232R、FT231X、FT232系列对应:
CONFIG_USB_SERIAL_FTDI_SIO=y
只要内核的USB host支持正常,把这些配置项打开,插上USB转串口线,系统就会自动生成
/dev/ttyUSB0
这种设备节点,根本不需要写驱动。类似的情况还有USB网卡、USB蓝牙、部分USB声卡、NVMe存储控制器等等,全都可以在Kconfig里找到对应开关。
J-Link、ST-Link这类调试器在OpenHarmony设备上又不太一样。它们不是“用户系统要驱动的外设”,而是“用户系统要作为USB设备被电脑端工具识别的”。如果你在开发板上跑OpenHarmony,然后通过J-Link接电脑调试,那不是给OpenHarmony装驱动,而是给电脑端的J-Link软件装驱动。当然,如果你的OpenHarmony设备要作为USB Host去访问一个J-Link探针或者ST-Link探针,那一般也不需要额外驱动,Linux标准的USB HID或USB bulk传输支持就够了。很多人在这个问题上绕圈子,其实是没分清“Host端”和“Device端”的角色。
4.2 defconfig下手实操:内核配置在哪改
OpenHarmony内核源码树的配置文件和Linux主线有个小差异。Linux主线一般用
arch/arm64/configs/xxx_defconfig
作为默认配置,OpenHarmony也有类似文件,但往往还会通过构建脚本动态修改。比较稳妥的做法是先看你现在用的内核源码树里的构建文档,找到目标平台对应的
defconfig
。
以Linux 5.10内核为例,常见位置是:
kernel/linux/linux-5.10/arch/arm64/configs/
里面可能有
ok3568_defconfig
、
rk3568_defconfig
或者厂商自定义的文件。修改方式有两种:
一种是直接编辑
defconfig
文件,把对应的
CONFIG_XXX=n
改成
CONFIG_XXX=y
,或者在文件末尾追加一行,然后重新编译内核。这种方式适合一次改一堆配置项,方便做配置基线管理。
另一种是在已经配置好的源码树里运行:
make ARCH=arm64 menuconfig
然后进入
Device Drivers -> USB support -> USB Serial Converter support
,在里面勾选对应的串口芯片驱动。保存后配置会写到
.config
里,你可以用
make savedefconfig
生成精简的配置,或者直接复制
.config
覆盖原来
defconfig
来固化改动。
还有一类驱动是以外部模块方式存在的,配置项是
=m
,编译后生成
.ko
,需要手动加载。建议在OpenHarmony设备上优先使用
=y
把常用驱动编进内核,能省掉不少insmod和权限的麻烦。
4.3 验证外设是否工作的完整链路
配置改完、内核编译完、系统启动后,怎么判断这个驱动真的生效了?不要只看一个命令。我一般按下面的顺序走一遍。
先看设备是否被USB总线枚举到:
lsusb
CH340的厂商ID通常是
1a86
,CP2102是
10c4
,FT232是
0403
。如果这条命令里能看到对应ID,说明硬件连接和USB控制器都是好的,问题大概率在内核驱动配置。
再看驱动是否绑定成功:
dmesg | grep -i tty
dmesg | grep -i usb
如果看到
ch341-uart converter now attached to ttyUSB0
这类的输出,说明串口驱动已经绑定成功。如果没有,先查
/sys/bus/usb/devices/
下面的目录结构,看看当前设备处于什么状态。
最后看设备节点:
ls -l /dev/ttyUSB*
能列出
ttyUSB0
就说明用户态可以访问了。再用
echo
和
cat
或者
stty
做一次最基础的读写测试。
这三步走完,90%的“外设没反应”问题都能定位出来:看不到ID是硬件问题,看到ID没绑定是驱动配置问题,看到节点没数据是应用层或模式问题。这个套路无论在OpenHarmony、普通Linux还是Android里都通用。
4.4 内核配置与模块编译的联动关系
配置驱动的过程中有一个关键机制要理解:内核配置项不只决定驱动是否编译,还决定了它怎么编译。
=y
编进内核镜像,
=m
编成独立模块,
=n
不编译。而一个驱动具体是哪种类型,由对应的
Makefile
里这段逻辑控制:
obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o
当
CONFIG_USB_SERIAL_CH341=y
时,
obj-y
会把
ch341.o
链接进内核。当
CONFIG_USB_SERIAL_CH341=m
时,
obj-m
会把它编成
ch341.ko
。搞清楚这条链路,你才能理解为什么改了defconfig之后需要重新编译整个内核镜像,也才能理解为什么有时候只编译模块不够,必须整包重编。
5. 三条路径共享的踩坑清单与调试手段
5.1 驱动编进内核但设备节点不出现
这是最高频的问题,三条路径都逃不过。
如果是HDF路径,先查
/dev/hdf/
目录下有没有对应的设备节点,再用
hdc shell
进去看内核日志:
hilog
或
dmesg
。常见原因是HCS里
policy
配置不对,或者
moduleName
跟C代码里的对不上。HDF日志里通常会打印“Load device failed”类似的字样,根据日志里的错误码去翻
drivers/hdf_core/framework
里的错误码定义。
如果是裸模块或者内核配置路径,先确认驱动确实加载了:
lsmod
dmesg | grep -i driver_name
再检查设备创建是否被注册。老牌的排查思路是看
/sys/class/
和
/sys/bus/
下有没有你的设备的子目录。如果驱动已经加载但没有创建设备,多半是驱动自己的
probe
没有执行,或者设备树里根本没有匹配的节点。很多人听到“设备树”觉得头疼,但排查到最后,往往问题就出在设备树里某个compatible字符串和驱动里对不上。
5.2 串口工具找不到ttyUSB0:不只是驱动的问题
很多人在OpenHarmony里用CH340,驱动配置已经打开了,
lsusb
也能看到芯片,但
/dev/ttyUSB0
就是不出现。这种情况先别怀疑驱动,先看看系统里有没有被
modem
或者
getty
之类的进程占用,再检查USB电源管理有没有把设备挂起。
另一个常见原因是rootfs里没有
/dev
节点自动创建的规则。OpenHarmony的devtmpfs如果没配对,设备节点不会自己冒出来。临时解决办法是手动执行:
mknod /dev/ttyUSB0 c 188 0
chmod 666 /dev/ttyUSB0
但这个办法只是应急,调试完还得把自动创建设备节点的机制修复,否则重启又没了。
5.3 依赖符号找不到与内核版本不匹配
编译裸模块报“Unknown symbol”是家常便饭。这类问题的本质是:你编译模块时用的内核源码树,和你运行模块的目标内核镜像不是同一个。符号表对不上,自然找不到。
怎么确认?先在目标设备上查找内核符号让你使用导出符号,OpenHarmony内核一般留了
/proc/kallsyms
,可以把它拷出来,然后在你编译模块的源码树里对比。如果直接对比太麻烦,最简单的办法是从头完整编一次你正在用的内核,然后把模块源码放到内核树外编译,保证
Module.symvers
一致。踩过几次坑之后我养成一个习惯:每拿到一套新SDK,第一件事就是把内核源码完整编译一遍,生成好
Module.symvers
和
arch/arm64/include/generated/
下的头文件,之后的模块编译就顺畅了。
5.4 调试手段要提前配置好
不管走哪条路径,内核尽量都把这几项调试能力打开,别等到出问题了才后悔:
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_KALLSYMS=y
CONFIG_KPROBES=y
有了这些,你可以用
debugfs
查看内核内部状态,用
tracefs
的
trace_printk
做轻量级日志,用
kprobe
在不停机的状况下挂到任意内核函数上。特别是
kprobe
,排查“为什么probe函数没执行”这类问题极其好用,比一遍遍重新编译内核高效多了。
5.5 HDF与裸模块混用时的命名冲突
最后提一个容易被忽略的坑:HDF驱动的
moduleName
和内核模块名是两个独立命名空间,但它们可能会在日志、sysfs里撞名。比如你有一个HDF驱动的
moduleName
叫
foo
,又加载了一个
foo.ko
,日志会产生误导,排查时经常分不清到底加载的是哪个。
我的习惯是给HDF驱动加统一前缀,比如
hdf_foo
,裸模块保持
foo
,这样从日志和
lsmod
输出里一眼就能区分。这个习惯帮我避免好多次看错日志白费时间。
6. 从零到一手把手:一个完整的驱动验证流程示例
前面把三条路径都展开了,最后用一个综合示例把它们串起来。假设你现在拿到了一块RK3568开发板,OpenHarmony系统已经能正常启动,目标是把一个USB转串口的CH340识别出来,并且跑一个简单的串口读写程序。
第一步,先不要急着写驱动。在电脑上准备OpenHarmony内核源码树,执行完整编译,确保内核源码的生成文件完整。这一步大约会花几十分钟,但能节省后面所有排错时间。
第二步,在内核源码树里确认或修改
defconfig
:
cd kernel/linux/linux-5.10
grep -n "CONFIG_USB_SERIAL_CH341" arch/arm64/configs/xxx_defconfig
如果没有就追加一行:
CONFIG_USB_SERIAL_CH341=y
CONFIG_USB_SERIAL_CP210X=y
CONFIG_USB_SERIAL_FTDI_SIO=y
第三步,用编译命令构建新内核和OpenHarmony系统镜像。OpenHarmony官方推荐用
hb
工具,具体构建命令按你SDK的文档来,一般是:
hb build -f -p rk3568
构建完成后,把新镜像烧录到开发板或者启动到QEMU里。
第四步,系统启动后插入CH340模块,执行验证:
lsusb
dmesg | grep -i ch341
ls -l /dev/ttyUSB0
如果
ttyUSB0
出现了,恭喜你,三次路径里的“内核配置路径”已经走通了。如果没出现,回去看第5章的排查清单。
第五步,如果你想体验HDF路径,就把第2章那个最小LED驱动也编进去。这里的建议是:在同一个源码树里,先保证裸模块和内核配置路径都验证通过,再往HDF上靠。一步一步来,不要把“跑起来一个设备”的目标和“学会HDF框架”的目标混在一起。
最后说句实在话
折腾OpenHarmony驱动开发这段时间,我最大的一个体会是:别被“框架”两个字吓住。HDF确实复杂,但它解决的是一个很实在的问题——让驱动和服务之间有统一、可控的协作方式。而很多硬件问题本质上是内核配置和硬件连接问题,跟用什么框架一点关系都没有。先把三条路径的分界搞清楚,再根据手里的任务选对路径,你就能少走很多弯路。
如果你刚开始接触,建议顺序是这样:第一周只做内核配置路径的事情,把手头板子上的串口、网卡、存储全都跑通;第二周试着写一个裸模块,把一个IO口拉高拉低,用示波器或者万用表确认引脚状态;第三周再把同样的逻辑改写成HDF驱动。等这三周过去,你对OpenHarmony驱动开发的理解,会比你盲目刷两个月教程要扎实得多。
更多推荐
所有评论(0)