标准系统方案之扬帆移植案例
本文章是基于瑞芯微RK3399芯片的yangfan开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。 开发板系统移植采用Board仓和SoC代码分离方案,Board仓保存板载驱动的模块,例如音频,Camera,TP,WIFI等驱动模块的适配代码。在SoC仓保存与SoC驱动相关模块,例如I2C,ISP,RGA等驱动模块的适配代码。
产品配置和目录规划
产品配置
在产品目录下创建config.json文件,并指定CPU的架构。配置如下://vendor/yangfan//vendor/yangfan/rk3399.json
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />{
"product_name": "yangfan",---产品名:yangfan
"device_company": "rockchip",---单板厂商:rockchip
"device_build_path": "device/board/isoftstone/yangfan",---设备构建路径:device/board/isoftstone/yangfan
"target_cpu": "arm",---目标cpu:arm
"type": "standard",---配置系统的级别:standard
"version": "3.0",---版本:3.0
"board": "yangfan",---单板名:yangfan
"enable_ramdisk": true,---启用内存虚拟盘:true
"build_selinux": true,---构建selinux:true
"inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ],
"subsystems": [
{
"subsystem": "security",
"components": [
{
"component": "selinux",
"features": []
}
]
},
{
"subsystem": "communication",
"components": [
{
"component": "netmanager_ext",
"features": []
}
]
},
...
}
</code></span></span>
主要的配置内容包括:
- “product_name”: “yangfan”,—产品名:yangfan
- “device_company”: “rockchip”,—单板厂商:rockchip
- “device_build_path”: “device/board/isoftstone/yangfan”,—设备构建路径:device/board/isoftstone/yangfan
- “target_cpu”: “arm”,—目标cpu:arm
- “type”: “standard”,—配置系统的级别:standard
- “version”: “3.0”,—版本:3.0
- “board”: “yangfan”,—单板名:yangfan
- “enable_ramdisk”: true,—启用内存虚拟盘:true
已定义的子系统可以在中找到。当然也可以定制子系统。//build/subsystem_config.json
建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products子系统。该子系统为Hi3516DV300 SOC编译内核,不适合RK3568。
目录规划
芯片适配目录规划为:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />device
├── board --- 单板厂商目录
│ └── isoftstone --- 单板厂商名字:
│ └── yangfan --- 单板名:扬帆,主要放置开发板相关的驱动业务代码
└── soc --- SoC厂商目录
└── rockchip --- SoC厂商名字:rockchip
└── rk3399 --- SoC Series名:rk3399,主要为芯片原厂提供的一些方案,以及闭源库等
</code></span></span>
产品样例目录规划为:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />vendor
└── isoftstone
└── yangfan --- 产品名字:产品、hcs以及demo相关
</code></span></span>
内核启动
二级启动
二级启动简单来说就是将之前直接挂载系统,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init 。
RK3399适配主要是将主线编译出来的ramdisk打包到boot_linux.img中,主要有以下工作:
- 使能二级启动
在//vendor/yangfan/rk3399.json中使能enable_ramdisk。
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />{
"product_name": "yangfan",
"device_company": "rockchip",
"device_build_path": "device/board/isoftstone/yangfan",
"target_cpu": "arm",
"type": "standard",
"version": "3.0",
"board": "yangfan",
"enable_ramdisk": true,
"build_selinux": true,
...
}
</code></span></span>
- 将主线编译出来的ramdsik.img 打包到boot_linux.img
配置:
由于rk 启动uboot 支持从ramdisk启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img ,因此没有使用主线的its格式,具体配置就是在内核编译脚本 make-ohos.sh 中增加:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />function make_extlinux_conf()
{
dtb_path=$1
uart=$2
image=$3
echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF}
echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF}
echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF}
if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then
echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF}
fi
cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4"
echo " ${cmdline}" >> ${EXTLINUX_CONF}
}
</code></span></span>
打包
增加了打包boot镜像的脚本 make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容:
音频
简介
本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Audio驱动框架,包括Audio驱动的架构组成、功能部件的实现和服务节点详细介绍。

-
ADM(Audio Driver Model)
音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配HOS系统。
-
Audio Control Dispatch
接收lib层的控制指令并将控制指令分发到驱动层。
-
Audio Stream Dispatch
向上通过lib层完成数据流的接收,向下完成数据流对驱动层的分发。
-
Card Manager
多声卡管理模块。每个声卡含有Dai、Platform、Codec、Accessory、Dsp、Sapm模块。
-
Platform Driver
驱动适配层。
-
SAPM(Smart Audio Power Manager)
电源管理模块,对整个ADM电源进行功耗策略优化。
Audio驱动介绍
代码目录
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" />drivers
├── framework
│ └── model
│ │ └── audio #框架代码
│ │ ├─── common #公共实现
│ │ ├─── core #核心
│ │ ├─── dispatch #控制流和数据流实现
│ │ └── sapm #电源管理
│ └── include
│ └── audio #对外接口
├── adapter
│ └──khdf
│ └── linux
│ └── model
│ └── audio #编译文件
└── peripheral
└── audio
└── chipsets
└── rk3399 #驱动实现
├── accessory #SmartPA驱动
├── dai #I2S驱动
└── soc #Dma驱动
</code></span></span>
Audio流程说明
启动流程

- 系统启动时audio模块的Platform、Codec、Accessory、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
- 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
- ADM模块读取hdf_audio_driver_0(音频card_0)和hdf_audio_driver_1(音频card_1)配置信息,加载各模块的具体设备。
- ADM模块调用各模块的初始化函数对各模块设备进行初始化。
- 将初始化成功的音频设备添加到cardManager链表。
播放流程

- 播放音频,首先Interface Lib层通过播放流服务下发Render Open指令,Render Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。
- Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。
- Interface Lib层通过播放流服务下发硬件参数,Render Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。
- Interface Lib层通过播放流服务下发播放启动指令,Render Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。
- Interface Lib层通过播放流服务下发音频数据,Render Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。
- Interface Lib层通过播放流服务下发播放停止指令,Render Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。
- Interface Lib层通过播放流服务下发Render Close指令,Render Stream Dispatch服务收到指令后调用Platform AudioRenderClose接口对已申请资源进行释放。
控制流程

- 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Get函数接口获取可设置音量范围。
- Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Set函数接口设置音量。
实现说明
-
驱动注册
以codec的注册函数为例,当codec驱动初始化时调用如下codec注册函数,将codec注册到codecController链表中。
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /><em><span style="color:green">int32_t</span></em> <span style="color:#000000">AudioRegisterCodec</span><span style="color:#0055af">(<span style="color:olive">struct</span> HdfDeviceObject *device, <span style="color:olive">struct</span> CodecData *codecData, <span style="color:olive">struct</span> DaiData *daiData)</span> { ... codec = (<span style="color:olive">struct</span> CodecDevice *)OsalMemCalloc(<span style="color:olive">sizeof</span>(*codec)); ... OsalMutexInit(&codec->mutex); codec->devCodecName = codecData->drvCodecName; codec->devData = codecData; codec->device = device; ret = AudioSocRegisterDai(device, daiData); ... DListInsertHead(&codec-><span style="color:green">list</span>, &codecController); ... } <strong>c</strong></code></span></span> -
数据流数据分发
当录音或者播放时,上层lib层通过dispatch将数据下发或读取数据,此接口接收到lib层的请求后,将数据进行分发或将数据返回。
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /><em><span style="color:green">static</span></em> <em><span style="color:green">int32_t</span></em> <span style="color:#000000">StreamDispatch</span><span style="color:#0055af">(<span style="color:olive">struct</span> HdfDeviceIoClient *client, <em><span style="color:green">int</span></em> cmdId, <span style="color:olive">struct</span> HdfSBuf *data, <span style="color:olive">struct</span> HdfSBuf *reply)</span> { <em><span style="color:green">unsigned</span></em> <em><span style="color:green">int</span></em> count = <span style="color:olive">sizeof</span>(g_streamDispCmdHandle) / <span style="color:olive">sizeof</span>(g_streamDispCmdHandle[<span style="color:navy">0</span>]); <span style="color:olive">for</span> (<em><span style="color:green">unsigned</span></em> <em><span style="color:green">int</span></em> i = <span style="color:navy">0</span>; i < count; ++i) { <span style="color:olive">if</span> ((cmdId == (<em><span style="color:green">int</span></em>)(g_streamDispCmdHandle[i].cmd)) && (g_streamDispCmdHandle[i].func != <span style="color:navy">NULL</span>)) { <span style="color:olive">return</span> g_streamDispCmdHandle[i].func(client, data, reply); } } ADM_LOG_ERR(<span style="color:green">"invalid [cmdId=%d]"</span>, cmdId); <span style="color:olive">return</span> HDF_FAILURE; } <strong>c</strong></code></span></span> -
控制功能注册接口
音量控制、增益控制、通路控制等控制功能都是通过此接口添加到声卡控制列表。
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /><em><span style="color:green">int32_t</span></em> <span style="color:#000000">AudioAddControls</span><span style="color:#0055af">(<span style="color:olive">struct</span> AudioCard *audioCard, <em><span style="color:green">const</span></em> <span style="color:olive">struct</span> AudioKcontrol *controls, <em><span style="color:green">int32_t</span></em> controlMaxNum)</span> { ... <span style="color:olive">for</span> (i = <span style="color:navy">0</span>; i < controlMaxNum; i++) { control = AudioAddControl(audioCard, &controls[i]); <span style="color:olive">if</span> (control == <span style="color:navy">NULL</span>) { ADM_LOG_ERR(<span style="color:green">"Add control fail!"</span>); <span style="color:olive">return</span> HDF_FAILURE; } DListInsertHead(&control-><span style="color:green">list</span>, &audioCard->controls); } ADM_LOG_DEBUG(<span style="color:green">"Success."</span>); <span style="color:olive">return</span> HDF_SUCCESS; } <strong>c</strong></code></span></span> -
电源管理接口
添加组件实现:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /><em><span style="color:green">int32_t</span></em> <span style="color:#000000">AudioSapmNewComponents</span><span style="color:#0055af">(<span style="color:olive">struct</span> AudioCard *audioCard, <em><span style="color:green">const</span></em> <span style="color:olive">struct</span> AudioSapmComponent *component, <em><span style="color:green">int32_t</span></em> cptMaxNum)</span> { ... <span style="color:olive">for</span> (i = <span style="color:navy">0</span>; i < cptMaxNum; i++) { ret = AudioSapmNewComponent(audioCard, component); <span style="color:olive">if</span> (ret != HDF_SUCCESS) { ADM_LOG_ERR(<span style="color:green">"AudioSapmNewComponent fail!"</span>); <span style="color:olive">return</span> HDF_FAILURE; } component++; } <span style="color:olive">return</span> HDF_SUCCESS; } <strong>c</strong></code></span></span>添加通路实现:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /> <em><span style="color:green">int32_t</span></em> <span style="color:#000000">AudioSapmAddRoutes</span><span style="color:#0055af">(<span style="color:olive">struct</span> AudioCard *audioCard, <em><span style="color:green">const</span></em> <span style="color:olive">struct</span> AudioSapmRoute *route, <em><span style="color:green">int32_t</span></em> routeMaxNum)</span> { ... <span style="color:olive">for</span> (i = <span style="color:navy">0</span>; i < routeMaxNum; i++) { ret = AudioSapmAddRoute(audioCard, route); <span style="color:olive">if</span> (ret != HDF_SUCCESS) { ADM_LOG_ERR(<span style="color:green">"AudioSapmAddRoute failed!"</span>); <span style="color:olive">return</span> HDF_FAILURE; } route++; } <span style="color:olive">return</span> HDF_SUCCESS; } <strong>c</strong></code></span></span>添加控制功能实现:
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /> <em><span style="color:green">int32_t</span></em> <span style="color:#000000">AudioSapmNewControls</span><span style="color:#0055af">(<span style="color:olive">struct</span> AudioCard *audioCard)</span> { ... DLIST_FOR_EACH_ENTRY(sapmComponent, &audioCard->components, <span style="color:olive">struct</span> AudioSapmComponent, <span style="color:green">list</span>) { <span style="color:olive">if</span> (sapmComponent->newCpt) { <span style="color:olive">continue</span>; } <span style="color:olive">if</span> (sapmComponent->kcontrolsNum > <span style="color:navy">0</span>) { sapmComponent->kcontrols = OsalMemCalloc(<span style="color:olive">sizeof</span>(<span style="color:olive">struct</span> AudioKcontrol*) * sapmComponent->kcontrolsNum); <span style="color:olive">if</span> (sapmComponent->kcontrols == <span style="color:navy">NULL</span>) { ADM_LOG_ERR(<span style="color:green">"malloc kcontrols fail!"</span>); <span style="color:olive">return</span> HDF_FAILURE; } } <span style="color:olive">switch</span> (sapmComponent->sapmType) { <span style="color:olive">case</span> AUDIO_SAPM_ANALOG_SWITCH: <span style="color:olive">case</span> AUDIO_SAPM_MIXER: <span style="color:olive">case</span> AUDIO_SAPM_MIXER_NAMED_CTRL: <span style="color:olive">case</span> AUDIO_SAPM_SPK: <span style="color:olive">case</span> AUDIO_SAPM_PGA: ret = AudioSapmNewMixerControls(sapmComponent, audioCard); <span style="color:olive">break</span>; <span style="color:olive">case</span> AUDIO_SAPM_MUX: <span style="color:olive">case</span> AUDIO_SAPM_VIRT_MUX: <span style="color:olive">case</span> AUDIO_SAPM_VALUE_MUX: ret = AudioSapmNewMuxControls(sapmComponent, audioCard); <span style="color:olive">break</span>; <span style="color:olive">default</span>: ret = HDF_SUCCESS; <span style="color:olive">break</span>; } ... ReadInitComponentPowerStatus(sapmComponent); sapmComponent->newCpt = <span style="color:navy">1</span>; DListInsertTail(&sapmComponent->dirty, &audioCard->sapmDirty); } ret = AudioSapmPowerComponents(audioCard); ... <span style="color:olive">return</span> HDF_SUCCESS; } <strong>c</strong></code></span></span> -
控制流数据分发
当录音或者播放时,上层lib层通过dispatch将控制指令下发,此接口接收到lib层的控制指令后,将控制指令分发到各驱动模块。
<span style="background-color:#f6f8fa"><span style="color:#000000"><code><img data-cke-saved-src="https://docs.openharmony.cn/copy.png" src="https://docs.openharmony.cn/copy.png" alt="" width="20" height="20" class="copy-img" /><em><span style="color:green">static</span></em> <em><span style="color:green">int32_t</span></em> <span style="color:#000000">ControlDispatch</span><span style="color:#0055af">(<span style="color:olive">struct</span> HdfDeviceIoClient *client, <em><span style="color:green">int</span></em> cmdId, <span style="color:olive">struct</span> HdfSBuf *data, <span style="color:olive">struct</span> HdfSBuf *reply)</span> { ... <span style="color:olive">if</span> (cmdId >= AUDIODRV_CTRL_IOCTRL_ELEM_BUTT || cmdId < <span style="color:navy">0</span>) { ADM_LOG_ERR(<span style="color:green">"Invalid

最低0.47元/天 解锁文章
1365

被折叠的 条评论
为什么被折叠?



