标准系统方案之扬帆移植案例
本文章是基于瑞芯微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 [cmdId=%d]."</span>, cmdId); <span style="color:olive">return</span> HDF_FAILURE; } <span style="color:olive">for</span> (i = <span style="color:navy">0</span>; i < HDF_ARRAY_SIZE(g_controlDispCmdHandle); ++i) { <span style="color:olive">if</span> ((cmdId == (<em><span style="color:green">int</span></em>)(g_controlDispCmdHandle[i].cmd)) && (g_controlDispCmdHandle[i].func != <span style="color:navy">NULL</span>)) { <span style="color:olive">return</span> g_controlDispCmdHandle[i].func(client, data, reply); } } <span style="color:olive">return</span> HDF_FAILURE; } <strong>c</strong></code></span></span>
Audio服务介绍
服务节点
基于ADM框架的audio驱动对HDI层提供三个服务hdf_audio_render、hdf_audio_capture、hdf_audio_control。 开发板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" />console:/dev # ls -al hdf_audio_*
crw------- 1 system system 249, 5 1970-01-01 00:21 hdf_audio_capture //录音数据流服务。
crw------- 1 system system 249, 3 1970-01-01 00:21 hdf_audio_codec_dev0 //音频设备名称。
crw------- 1 system system 249, 4 1970-01-01 00:21 hdf_audio_control //音频控制流服务。
crw------- 1 system system 249, 6 1970-01-01 00:21 hdf_audio_render //播放数据流务。
</code></span></span>
-
音频控制流服务
用来接收上层lib层下发的控制指令,包括音量控制、增益控制、通路控制,这些控制指令都是通过控制流服务下发到驱动。
-
音频数据播放流服务
用来接收上层lib层下发的音频数据和播放相关的参数,还有播放的启动、暂停、恢复、停止指令,这些指令都是由播放数据流下发到驱动。
-
音频数据录音流服务
用来向上层lib层传输音频数据和接收上层lib层下发的录音相关的参数,还有录音的启动、暂停、恢复、停止指令,这些指令都是由录音数据流下发到驱动。
驱动服务
每个audio设备包括如下服务:
hdf_audio_codec_dev0 | 音频设备名称 |
---|---|
dma_service_0 | dma 驱动服务 |
dai_service | cpu dai 驱动服务 |
codec_service_0 | codec 驱动服务 |
dsp_service_0 | dsp 驱动服务(可选项) |
hdf_audio_codec_dev1 | 音频设备名称 |
---|---|
dma_service_0 | dma 驱动服务 |
dai_service | cpu dai 驱动服务 |
codec_service_1 | accessory 驱动服务(特指smartPA) |
dsp_service_0 | dsp 驱动服务(可选项) |
代码路径
<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/rockchip/rk3399/hdf_config/khdf
├── audio #audio私有配置文件
├── device_info
| └── device_info.hcs #设备配置文件
└── hdf.hcs #引用hcs配置文件
</code></span></span>
配置节点说明
以codec驱动为例,在device_info.hcs文件中的audio host节点下添加codec节点信息。
<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" /> audio :: host {
hostName = "audio_host";
priority = 60;
...
device_codec :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
preload = 0;
permission = 0666;
moduleName = "CODEC_ES8316";
serviceName = "codec_service_0";
deviceMatchAttr = "hdf_codec_driver";
}
}
...
}
</code></span></span>
实现驱动
在驱动文件中实现与device_info.hcs配置节点moduleName相同的驱动逻辑。
<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" />/* HdfDriverEntry implementations */
static int32_t Es8316DriverBind(struct HdfDeviceObject *device)
{
...
return HDF_SUCCESS;
}
static int32_t Es8316DriverInit(struct HdfDeviceObject *device)
{
...
return HDF_SUCCESS;
}
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_es8316DriverEntry = {
.moduleVersion = 1,
.moduleName = "CODEC_ES8316",
.Bind = Es8316DriverBind,
.Init = Es8316DriverInit,
.Release = NULL,
};
HDF_INIT(g_es8316DriverEntry);
</code></span></span>
总结
基于HDF框架的ADM音频框架,为Open Harmony的音频开发提供了统一的架构基础,为各平台音频驱动适配提供了统一的接口。音频驱动可以一平台开发多平台适用,提高了开发效率。此文档对ADM框架进行了简单的介绍,希望有助于开发者开发和应用。
Camera
简介
本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Camera驱动框架,包括Camera驱动的架构组成、功能部件的实现和服务节点详细介绍。
Camera驱动框架图
OpenHarmony HDF Camera驱动模块架构图
以Camera Host 部分做如下说明:
- HDI实现层(HDI Implementation):对上实现Open Harmony OS相机标准南向接口。
- 框架层(PipelineCore):对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能。
- 适配层(Platform Adaption):屏蔽底层芯片和OS差异,支持多平台适配。
对于rk3399E/T的Usb Camera来分析,内核使用linux-4.19。Usb Camera依赖linux下的V4L2的uvc,从上面的框架图分析HDF Camera已经实现了兼容linux 的 V4L2 uvc,所以调试过程首先要保证uvc所涉及的USB和Camera的驱动正常。
Camera驱动介绍
配置信息
arch/arm64/configs/rockchip_linux_defconfig
<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" />CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=y
<strong>c</strong></code></span></span>
节点信息
插入Usb Camera 前
<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" /># ls -l dev/video*
crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0
crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1
crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2
crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3
crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4
crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5
crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6
crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7
crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8
crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9
#
</code></span></span>
插入Usb Camera后新增节点dev/video10和dev/video11
<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" /># ls -l dev/video*
crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0
crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1
crw------- 1 root root 81, 10 2013-01-18 11:01 dev/video10
crw------- 1 root root 81, 11 2013-01-18 11:01 dev/video11
crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2
crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3
crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4
crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5
crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6
crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7
crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8
crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9
#
</code></span></span>
打开设备节点
在Open Harmony OS的代码环境中,编译如下代码为可执行程序,在开发板测执行,无报错说明该节点open success。
<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" /><span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdio.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/types.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/stat.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><fcntl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdlib.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><unistd.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/ioctl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><linux/videodev2.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><string.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/mman.h></span></span>
<em><span style="color:green">int</span></em> <span style="color:#000000">main</span><span style="color:#0055af">(<em><span style="color:green">void</span></em>)</span>
{
<span style="color:green">// 1. 打开设备</span>
<em><span style="color:green">int</span></em> fd = open(<span style="color:green">"/dev/video10"</span>, O_RDWR);
<span style="color:olive">if</span> (fd < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"open device fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
close(fd);
<span style="color:olive">return</span> <span style="color:navy">0</span>;
}
<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" /><span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdio.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/types.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/stat.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><fcntl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdlib.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><unistd.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/ioctl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><linux/videodev2.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><string.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/mman.h></span></span>
<em><span style="color:green">int</span></em> <span style="color:#000000">main</span><span style="color:#0055af">(<em><span style="color:green">void</span></em>)</span>
{
<span style="color:green">// 1. 打开设备</span>
<em><span style="color:green">int</span></em> fd = open(<span style="color:green">"/dev/video10"</span>, O_RDWR);
<span style="color:olive">if</span> (fd < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"open device fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
<span style="color:green">// 2. 获取摄像头支持的格式 ioctl(文件描述符, 命令, 与命令对应的结构体)</span>
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_fmtdesc</span> <span style="color:#0055af">v4fmt</span>;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
<em><span style="color:green">int</span></em> i = <span style="color:navy">0</span>;
<span style="color:olive">while</span>(<span style="color:navy">1</span>) {
v4fmt.index = i++;
<em><span style="color:green">int</span></em> ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
<span style="color:olive">if</span> (ret < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"get fmt fail\n"</span>);
}
<em><span style="color:green">unsigned</span></em> <em><span style="color:green">char</span></em> *p = (<em><span style="color:green">unsigned</span></em> <em><span style="color:green">char</span></em>*)&v4fmt.pixelformat;
<span style="color:green">printf</span>(<span style="color:green">"index=%d\n"</span>, v4fmt.index);
<span style="color:green">printf</span>(<span style="color:green">"flags=%d\n"</span>, v4fmt.flags);
<span style="color:green">printf</span>(<span style="color:green">"description=%s\n"</span>, v4fmt.description);
<span style="color:green">printf</span>(<span style="color:green">"pixelformat=%c%c%c%c\n"</span>, p[<span style="color:navy">0</span>], p[<span style="color:navy">1</span>], p[<span style="color:navy">2</span>], p[<span style="color:navy">3</span>]);
<span style="color:green">printf</span>(<span style="color:green">"reserved=%d\n"</span>, v4fmt.reserved[<span style="color:navy">0</span>]);
}
close(fd);
<span style="color:olive">return</span> <span style="color:navy">0</span>;
}
<strong>c</strong></code></span></span>
在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。结果显示支持YUYV和MJPEG 2种输出格式。
index=0
flags=0
description=YUYV 4:2:2
pixelformat=YUYV
reserved=0
index=1
flags=1
description=Motion-JPEG
pixelformat=MJPG
reserved=0
设置缓冲区队列
<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" /><span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdio.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/types.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/stat.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><fcntl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><stdlib.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><unistd.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/ioctl.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><linux/videodev2.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><string.h></span></span>
<span style="color:green">#<span style="color:olive">include</span> <span style="color:green"><sys/mman.h></span></span>
<em><span style="color:green">int</span></em> <span style="color:#000000">main</span><span style="color:#0055af">(<em><span style="color:green">void</span></em>)</span>
{
<span style="color:green">// 1. 打开设备</span>
<em><span style="color:green">int</span></em> fd = open(<span style="color:green">"/dev/video10"</span>, O_RDWR);
<span style="color:olive">if</span> (fd < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"open device fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
<span style="color:green">// 2. 设置采集格式</span>
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_format</span> <span style="color:#0055af">vfmt</span>;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vfmt.fmt.pix.width = <span style="color:navy">640</span>;
vfmt.fmt.pix.height = <span style="color:navy">480</span>;
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; <span style="color:green">// 设置的视频采集格式(与上面获取的格式一致)</span>
<em><span style="color:green">int</span></em> ret = ioctl(fd, VIDIOC_S_FMT, &vfmt); <span style="color:green">// 设置格式</span>
<span style="color:olive">if</span> (ret < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"set fmt fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
<span style="color:green">memset</span>(&vfmt, <span style="color:navy">0</span>, <span style="color:olive">sizeof</span>(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt); <span style="color:green">// 获取格式</span>
<span style="color:olive">if</span> (ret < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"set->get fmt fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
<span style="color:green">// 3. 申请内核缓冲区队列</span>
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_requestbuffers</span> <span style="color:#0055af">reqbuffer</span>;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = <span style="color:navy">4</span>; <span style="color:green">// 申请4个缓冲区</span>
reqbuffer.memory = V4L2_MEMORY_MMAP; <span style="color:green">// 内存映射方式 MMAP/USERPTR</span>
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); <span style="color:green">// 分配内存</span>
<span style="color:olive">if</span> (ret < <span style="color:navy">0</span>) {
<span style="color:green">printf</span>(<span style="color:green">"req buffer fail\n"</span>);
<span style="color:olive">return</span> <span style="color:navy">-1</span>;
}
<span style="color:green">// 4. 关闭设备</span>
close(fd);
<span style="color:olive">return</span> <span style="color:navy">0</span>;
}
<strong>c</strong></code></span></span>
在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。
执行结果:req buffer fail
原因分析:ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); 失败
定位方法1:在内核中加LOG定位VIDIOC_REQBUFS失败的地方。发现所有的ioctl命令下发后都会使用drivers/media/v4l2-core/v4l2-ioctl.c中的video_usercopy接口,但还是没有定位到具体的失败原因。
定位方法2:求助视美泰验证linux-4.19内核debian版本的Usb Camera是否OK。结果:debian版本使用gst-launch-1.0 v4l2src device=/dev/video10 ! image/jpeg, width= 1280, height=720, framerate=30/1 ! jpegparse ! mppjpegdec ! kmssink sync=false命令后HDMI屏幕可以出来正常的预览画面。
通过如上的操作后,基本可以确认linux的V4L2 uvc驱动和外设Usb Camera驱动都是正常的。接下来就该调试Open Harmony OS的HDF Camera了。
接口介绍
查看现有Open Harmony OS上的关于camera的可执行程序:ohos_camera_demo、v4l2_main
ohos_camera_demo
执行结果:输入o后无预览画面,也无LOG报错信息。
<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" /># ohos_camera_demo
GetUintParameter debug.bytrace.tags.enableflags error.
Options:
-h | --help Print this message
-o | --offline stream offline test
-c | --capture capture one picture
-w | --set WB Set white balance Cloudy
-v | --video capture Viedeo of 10s
-a | --Set AE Set Auto exposure
-f | --Set Flashlight Set flashlight ON 5s OFF
-q | --quit stop preview and quit this app
o
Options:
-h | --help Print this message
-o | --offline stream offline test
-c | --capture capture one picture
-w | --set WB Set white balance Cloudy
-v | --video capture Viedeo of 10s
-a | --Set AE Set Auto exposure
-f | --Set Flashlight Set flashlight ON 5s OFF
-q | --quit stop preview and quit this app
</code></span></span>
原因分析:ohos_camera_demo目前仅支持MPP,不支持V4L2,故先放弃该demo调试。
v4l2_main
执行结果:输入u 报错:ERROR:main test:cannot open framebuffer /dev/fb0 file node
<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" />Options:
-h | --help Print this message
-p | --preview start preview on platform sensor
-c | --capture capture one picture
-w | --set WB Set white balance Cloudy
-e | --Set AE Set exposure time
-v | --video capture Viedeo of 10s
-u | --uvc start preview on uvc preview
-a | --Set ATE Set Auto exposure
-q | --quit stop preview and quit this app
INFO:please input command(input -q exit this app)
u
ERROR:main test:cannot open framebuffer /dev/fb0 file node
INFO:V4L2OpenDevice /dev/video10
</code></span></span>
原因分析:查看dev/fb0节点是否存在:不存在fb0节点。继续查找根目录下有无其他fb0节点,出现了dev/graphics/fb0节点。
<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" /># ls -l dev/fb0
ls: dev/fb0: No such file or directory
# find -name fb0
./dev/graphics/fb0
./sys/class/graphics/fb0
./sys/devices/platform/display-subsystem/graphics/fb0
</code></span></span>
需把v4l2_main可执行程序中的dev/fb0改为dev/graphics/fb0。此处fb0为framebuffer,作用是在屏幕上显示预览画面。
修改点:drivers/peripheral
<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" /><span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">index b351f49..d9c4cb3 100755</span>
<span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">@@ -186,9 +186,9 @@</span> RetCode FBInit()
if (g_fbFd)
return RC_OK;
<span style="color:green">- g_fbFd = open("/dev/fb0", O_RDWR);</span>
<span style="color:green">+ g_fbFd = open("/dev/graphics/fb0", O_RDWR);</span>
if (g_fbFd < 0) {
<span style="color:green">- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0");</span>
<span style="color:green">+ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0");</span>
return RC_ERROR;
}
<span style="color:green">diff --git a/camera/hal/test/v4l2/src/test_display.cpp b/camera/hal/test/v4l2/src/test_display.cpp</span>
<span style="color:green">index db908e7..7025deb 100644</span>
<span style="color:green">--- a/camera/hal/test/v4l2/src/test_display.cpp</span>
<span style="color:green">+++ b/camera/hal/test/v4l2/src/test_display.cpp</span>
<span style="color:green">@@ -114,9 +114,9 @@</span> void TestDisplay::FBLog()
RetCode TestDisplay::FBInit()
{
<span style="color:green">- fbFd_ = open("/dev/fb0", O_RDWR);</span>
<span style="color:green">+ fbFd_ = open("/dev/graphics/fb0", O_RDWR);</span>
if (fbFd_ < 0) {
<span style="color:green">- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0");</span>
<span style="color:green">+ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0");</span>
return RC_ERROR;
}
<span style="color:green">@@ -439,4 +439,4 @@</span> void TestDisplay::StopStream(std::vector<int>& captureIds, std::vector<int>& str
std::cout << "<span style="color:green">==========[test log]check Capture: ReleaseStreams fail, rc = " << rc << std::endl;</span>
}
}
<span style="color:green">-}</span>
\ No newline at end of file
<span style="color:green">+}</span>
<span style="color:green">diff --git a/display/hal/default/display_layer.c b/display/hal/default/display_layer.c</span>
<span style="color:green">index ee7a825..e12a653 100644</span>
<span style="color:green">--- a/display/hal/default/display_layer.c</span>
<span style="color:green">+++ b/display/hal/default/display_layer.c</span>
<span style="color:green">@@ -24,7 +24,7 @@</span>
#define DEV_ID 0
#define LAYER_ID 0
<span style="color:green">-#define FB_PATH "/dev/fb0"</span>
<span style="color:green">+#define FB_PATH "/dev/graphics/fb0"</span>
#define DISP_WIDTH 800
#define DISP_HEIGHT 480
#define BITS_PER_PIXEL 32
<strong>diff</strong></code></span></span>
修改后重新编译v4l2_main,编译命令:./build.sh --product-name rk3399 --ccache --build-target v4l2_main
编译成功后可执行程序路径:./out/rk3399/hdf/hdf/v4l2_main
将新编译的v4l2_main推送到开发板测的system/bin路径下:
<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" />hdc shell "mount -o rw,remount /"
hdc file send D:\cyyanl\work\RockChip\bin\v4l2_main /system/bin
</code></span></span>
继续执行v4l2_main结果后无framebuffer报错,说明该问题已解决。(另一种思路:分析fb0为何在/dev/graphics/fb0而不是常规的/dev/fb0,然后修改为/dev/fb0。后面有时间再调试该思路)
新报错:ERROR:error: ioctl VIDIOC_QUERYBUF failed.
<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" />Options:
-h | --help Print this message
-p | --preview start preview on platform sensor
-c | --capture capture one picture
-w | --set WB Set white balance Cloudy
-e | --Set AE Set exposure time
-v | --video capture Viedeo of 10s
-u | --uvc start preview on uvc preview
-a | --Set ATE Set Auto exposure
-q | --quit stop preview and quit this app
INFO:please input command(input -q exit this app)
u
INFO:the fixed information is as follow:
INFO:id=
INFO:sem_start=0
INFO:smem_len=2457600
...
INFO:V4L2AllocBuffer
INFO:V4L2AllocBuffer:memoryType_ = 2
INFO:V4L2AllocBuffer:V4L2_MEMORY_USERPTR = 2
INFO:V4L2AllocBuffer:VIDIOC_QUERYBUF = 3226490377
ERROR:error: ioctl VIDIOC_QUERYBUF failed.
ERROR:error: Creatbuffer: V4L2AllocBuffer error
ERROR:main test:V4L2PreviewThread CreatBuffer fail i = 0
</code></span></span>
原因分析:ioctl(fd, VIDIOC_QUERYBUF, &buf)失败。回过头再看"调试linux L4V2 uvc驱动章节->设置格式申请缓冲区队列"中的报错也是
ioctl(fd, VIDIOC_REQBUFS, &reqbuffer)。由此分析出Open Harmony OS上的ioctl VIDIOC_REQBUFS都会报错。再看两次失败的差异点:
内存映射方式不同: V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR
从OpenHarmony的issuse得知暂不支持V4L2_MEMORY_MMAP内存映射,映射方式就分析到这里,接下来还是用v4l2_main的V4L2_MEMORY_USERPTR进行调试分析。
接着再分析ioctl(fd, VIDIOC_QUERYBUF, &buf)失败,查看VIDIOC_QUERYBUF的定义:videodev2.h
<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" /><span style="color:green">#<span style="color:olive">define</span> VIDIOC_QUERYBUF_IOWR(<span style="color:green">'V'</span>, 9, struct v4l2_buffer)</span>
<strong>c</strong></code></span></span>
此处插入ioctl的定义:int ioctl(int fd, int cmd, …); VIDIOC_QUERYBUF作为cmd的入参,是int类型。也就是一个数字命令码,该命令码通过ioctl发送给内核后,会有与之对应的函数操作,故用户态下发的命令码应和内核接受的命令码一致。下面验证命令码一致性。
videodev2.h中一共有77个和内核交互的命令码,把内核态和用户态的都打印出来做对比:
用户态加打印:drivers/peripheral
<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" /><span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span>
<span style="color:green">index d7dd15f..f7254b4 100644</span>
<span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span>
<span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span>
<span style="color:green">@@ -162,37 +162,119 @@</span> RetCode HosV4L2Buffers::V4L2DequeueBuffer(int fd)
return RC_OK;
}
<span style="color:green">+static void cyyanl_printf_cmd(void)</span>
<span style="color:green">+{</span>
<span style="color:green">+#if 1</span>
<span style="color:green">+ CAMERA_LOGD("*************************************************************************************");</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = 0x%x\n", VIDIOC_QUERYCAP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = 0x%x\n", VIDIOC_ENUM_FMT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = 0x%x\n", VIDIOC_G_FMT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = 0x%x\n", VIDIOC_S_FMT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = 0x%x\n", VIDIOC_REQBUFS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = 0x%x\n", VIDIOC_QUERYBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = 0x%x\n", VIDIOC_G_FBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = 0x%x\n", VIDIOC_S_FBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = 0x%x\n", VIDIOC_OVERLAY );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = 0x%x\n", VIDIOC_QBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = 0x%x\n", VIDIOC_EXPBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = 0x%x\n", VIDIOC_DQBUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = 0x%x\n", VIDIOC_STREAMON );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = 0x%x\n", VIDIOC_STREAMOFF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = 0x%x\n", VIDIOC_G_PARM );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = 0x%x\n", VIDIOC_S_PARM );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = 0x%x\n", VIDIOC_G_STD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = 0x%x\n", VIDIOC_S_STD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = 0x%x\n", VIDIOC_ENUMSTD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = 0x%x\n", VIDIOC_ENUMINPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = 0x%x\n", VIDIOC_G_CTRL );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = 0x%x\n", VIDIOC_S_CTRL );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = 0x%x\n", VIDIOC_G_TUNER );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = 0x%x\n", VIDIOC_S_TUNER );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = 0x%x\n", VIDIOC_G_AUDIO );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = 0x%x\n", VIDIOC_S_AUDIO );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = 0x%x\n", VIDIOC_QUERYCTRL );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = 0x%x\n", VIDIOC_QUERYMENU );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = 0x%x\n", VIDIOC_G_INPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = 0x%x\n", VIDIOC_S_INPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = 0x%x\n", VIDIOC_G_EDID );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = 0x%x\n", VIDIOC_S_EDID );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = 0x%x\n", VIDIOC_G_OUTPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = 0x%x\n", VIDIOC_S_OUTPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = 0x%x\n", VIDIOC_ENUMOUTPUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = 0x%x\n", VIDIOC_G_AUDOUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = 0x%x\n", VIDIOC_S_AUDOUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = 0x%x\n", VIDIOC_G_MODULATOR );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = 0x%x\n", VIDIOC_S_MODULATOR );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = 0x%x\n", VIDIOC_G_FREQUENCY );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = 0x%x\n", VIDIOC_S_FREQUENCY );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = 0x%x\n", VIDIOC_CROPCAP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = 0x%x\n", VIDIOC_G_CROP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = 0x%x\n", VIDIOC_S_CROP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = 0x%x\n", VIDIOC_G_JPEGCOMP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = 0x%x\n", VIDIOC_S_JPEGCOMP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = 0x%x\n", VIDIOC_QUERYSTD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = 0x%x\n", VIDIOC_TRY_FMT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = 0x%x\n", VIDIOC_ENUMAUDIO );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = 0x%x\n", VIDIOC_ENUMAUDOUT );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = 0x%x\n", VIDIOC_G_PRIORITY );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = 0x%x\n", VIDIOC_S_PRIORITY );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = 0x%x\n", VIDIOC_G_SLICED_VBI_CAP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = 0x%x\n", VIDIOC_LOG_STATUS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = 0x%x\n", VIDIOC_G_EXT_CTRLS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = 0x%x\n", VIDIOC_S_EXT_CTRLS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = 0x%x\n", VIDIOC_TRY_EXT_CTRLS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = 0x%x\n", VIDIOC_ENUM_FRAMESIZES );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = 0x%x\n", VIDIOC_ENUM_FRAMEINTERVALS);</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = 0x%x\n", VIDIOC_G_ENC_INDEX );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = 0x%x\n", VIDIOC_ENCODER_CMD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = 0x%x\n", VIDIOC_TRY_ENCODER_CMD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = 0x%x\n", VIDIOC_S_HW_FREQ_SEEK );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = 0x%x\n", VIDIOC_CREATE_BUFS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = 0x%x\n", VIDIOC_PREPARE_BUF );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = 0x%x\n", VIDIOC_G_SELECTION );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = 0x%x\n", VIDIOC_S_SELECTION );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = 0x%x\n", VIDIOC_DECODER_CMD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = 0x%x\n", VIDIOC_TRY_DECODER_CMD );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = 0x%x\n", VIDIOC_ENUM_DV_TIMINGS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = 0x%x\n", VIDIOC_QUERY_DV_TIMINGS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = 0x%x\n", VIDIOC_DV_TIMINGS_CAP );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = 0x%x\n", VIDIOC_ENUM_FREQ_BANDS );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = 0x%x\n", VIDIOC_DBG_G_CHIP_INFO );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = 0x%x\n", VIDIOC_QUERY_EXT_CTRL );</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = 0x%x\n", BASE_VIDIOC_PRIVATE );</span>
<span style="color:green">+ CAMERA_LOGD("*************************************************************************************");</span>
<span style="color:green">+#endif</span>
<span style="color:green">+}</span>
<span style="color:green">+</span>
RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr<FrameSpec>& frameSpec)
{
struct v4l2_buffer buf = {};
struct v4l2_plane planes[1] = {};
<span style="color:green">- CAMERA_LOGD("V4L2AllocBuffer\n");</span>
<span style="color:green">+ CAMERA_LOGD("V4L2AllocBuffer enter\n");</span>
<span style="color:green">+ cyyanl_printf_cmd();</span>
if (frameSpec == nullptr) {
CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n");
return RC_ERROR;
}
<span style="color:green">-</span>
switch (memoryType_) {
case V4L2_MEMORY_MMAP:
// to do something
break;
case V4L2_MEMORY_USERPTR:
<span style="color:green">+ CAMERA_LOGD("V4L2AllocBuffer:V4L2_MEMORY_USERPTR = %d\n", V4L2_MEMORY_USERPTR);</span>
buf.type = bufferType_;
buf.memory = memoryType_;
buf.index = (uint32_t)frameSpec->buffer_->GetIndex();
<span style="color:green">-</span>
if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
<span style="color:green">+ CAMERA_LOGD("V4L2AllocBuffer:V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = %d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);</span>
buf.m.planes = planes;
buf.length = 1;
}
<strong>diff</strong></code></span></span>
内核态打印:kernel/linux/linux-4.19/
<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" /><span style="color:green">diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c</span>
<span style="color:green">index 0842a47c6..8aa60407f 100644</span>
<span style="color:green">--- a/drivers/media/usb/uvc/uvc_driver.c</span>
<span style="color:green">+++ b/drivers/media/usb/uvc/uvc_driver.c</span>
<span style="color:green">@@ -2902,10 +2902,93 @@</span> struct uvc_driver uvc_driver = {
},
};
<span style="color:green">+static void cyyanl_printk_cmd(void)</span>
<span style="color:green">+{</span>
<span style="color:green">+ printk("*************************************************************************************");</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = %ld\n", VIDIOC_QUERYCAP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = %ld\n", VIDIOC_ENUM_FMT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = %ld\n", VIDIOC_G_FMT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = %ld\n", VIDIOC_S_FMT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = %ld\n", VIDIOC_REQBUFS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = %ld\n", VIDIOC_QUERYBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = %ld\n", VIDIOC_G_FBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = %ld\n", VIDIOC_S_FBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = %ld\n", VIDIOC_OVERLAY );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = %ld\n", VIDIOC_QBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = %ld\n", VIDIOC_EXPBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = %ld\n", VIDIOC_DQBUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = %ld\n", VIDIOC_STREAMON );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = %ld\n", VIDIOC_STREAMOFF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = %ld\n", VIDIOC_G_PARM );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = %ld\n", VIDIOC_S_PARM );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = %ld\n", VIDIOC_G_STD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = %ld\n", VIDIOC_S_STD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = %ld\n", VIDIOC_ENUMSTD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = %ld\n", VIDIOC_ENUMINPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = %ld\n", VIDIOC_G_CTRL );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = %ld\n", VIDIOC_S_CTRL );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = %ld\n", VIDIOC_G_TUNER );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = %ld\n", VIDIOC_S_TUNER );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = %ld\n", VIDIOC_G_AUDIO );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = %ld\n", VIDIOC_S_AUDIO );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = %ld\n", VIDIOC_QUERYCTRL );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = %ld\n", VIDIOC_QUERYMENU );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = %ld\n", VIDIOC_G_INPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = %ld\n", VIDIOC_S_INPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = %ld\n", VIDIOC_G_EDID );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = %ld\n", VIDIOC_S_EDID );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = %ld\n", VIDIOC_G_OUTPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = %ld\n", VIDIOC_S_OUTPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = %ld\n", VIDIOC_ENUMOUTPUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = %ld\n", VIDIOC_G_AUDOUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = %ld\n", VIDIOC_S_AUDOUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = %ld\n", VIDIOC_G_MODULATOR );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = %ld\n", VIDIOC_S_MODULATOR );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = %ld\n", VIDIOC_G_FREQUENCY );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = %ld\n", VIDIOC_S_FREQUENCY );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = %ld\n", VIDIOC_CROPCAP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = %ld\n", VIDIOC_G_CROP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = %ld\n", VIDIOC_S_CROP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = %ld\n", VIDIOC_G_JPEGCOMP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = %ld\n", VIDIOC_S_JPEGCOMP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = %ld\n", VIDIOC_QUERYSTD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = %ld\n", VIDIOC_TRY_FMT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = %ld\n", VIDIOC_ENUMAUDIO );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = %ld\n", VIDIOC_ENUMAUDOUT );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = %ld\n", VIDIOC_G_PRIORITY );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = %ld\n", VIDIOC_S_PRIORITY );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = %ld\n", VIDIOC_G_SLICED_VBI_CAP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = %ld\n", VIDIOC_LOG_STATUS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = %ld\n", VIDIOC_G_EXT_CTRLS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = %ld\n", VIDIOC_S_EXT_CTRLS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = %ld\n", VIDIOC_TRY_EXT_CTRLS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = %ld\n", VIDIOC_ENUM_FRAMESIZES );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = %ld\n", VIDIOC_ENUM_FRAMEINTERVALS);</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = %ld\n", VIDIOC_G_ENC_INDEX );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = %ld\n", VIDIOC_ENCODER_CMD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = %ld\n", VIDIOC_TRY_ENCODER_CMD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = %ld\n", VIDIOC_S_HW_FREQ_SEEK );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = %ld\n", VIDIOC_CREATE_BUFS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = %ld\n", VIDIOC_PREPARE_BUF );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = %ld\n", VIDIOC_G_SELECTION );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = %ld\n", VIDIOC_S_SELECTION );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = %ld\n", VIDIOC_DECODER_CMD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = %ld\n", VIDIOC_TRY_DECODER_CMD );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = %ld\n", VIDIOC_ENUM_DV_TIMINGS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = %ld\n", VIDIOC_QUERY_DV_TIMINGS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = %ld\n", VIDIOC_DV_TIMINGS_CAP );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = %ld\n", VIDIOC_ENUM_FREQ_BANDS );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = %ld\n", VIDIOC_DBG_G_CHIP_INFO );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = %ld\n", VIDIOC_QUERY_EXT_CTRL );</span>
<span style="color:green">+ printk("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = %ld\n", BASE_VIDIOC_PRIVATE );</span>
<span style="color:green">+ printk("*************************************************************************************");</span>
<span style="color:green">+}</span>
<span style="color:green">+</span>
static int __init uvc_init(void)
{
int ret;
<span style="color:green">-</span>
<span style="color:green">+ printk("cyyanl enter uvc_init\n");</span>
<span style="color:green">+ cyyanl_printk_cmd();</span>
uvc_debugfs_init();
ret = usb_register(&uvc_driver.driver);
<strong>diff</strong></code></span></span>
打印结果对比:VIDIOC_QUERYBUF用户态为0xc0505609,内核态为0xc0585609 还有其他几处命令码也不同。
分析命令码不一致问题,先找出VIDIOC_QUERYBUF用户态和内核态实际编译时所定义的地方差异:
用户态:kernel/linux/patches/linux-5.10/prebuilts/usr/include/linux/videodev2.h +1358
内核态:kernel/linux/linux-4.19/include/uapi/linux/videodev2.h +2361
此时怀疑VIDIOC_QUERYBUF不一致是头文件不同所导致(目前没做修改验证同改为linux-4.19是否可行)。对现有问题求助客户,并在OpenHarmony上寻找有无相似问题。
找到了相似的issuse:https://gitee.com/openharmony/drivers_peripheral/issues/I4NI4M?from=project-issue
接下来,调试思路分为2路:
-
同意用户态和内核态的VIDIOC_QUERYBUF
方案:把用户态的0xc0505609改为内核态的0xc0585609
修改点:drivers/peripheral
<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" /><span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span> <span style="color:green">index d7dd15f..f7254b4 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp</span> buf.m.planes = planes; buf.length = 1; } <span style="color:green">- CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index);</span> <span style="color:green">-</span> <span style="color:green">- if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {</span> <span style="color:green">- CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno));</span> <span style="color:green">+ if (ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) < 0) {</span> <span style="color:green">+ CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed.\n");</span> return RC_ERROR; } <strong>diff</strong></code></span></span>
再次运行v4l2_main结果:Signal 4报错
原因分析:此时ioctl(fd, /VIDIOC_QUERYBUF/0xc0585609, &buf) 已经成功了。Signal 4分析可能又是cmd命令码的问题。
此路虽然解决了当前问题,但会有新问题Signal 4,故先暂停。
<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" />INFO:main test:allocating display buffer memory INFO:main test:do_mmap: pmem mmap fd 5 ptr 0xf7508000 len 2457600 INFO:V4L2OpenDevice /dev/video10 INFO:V4L2ReqBuffers buffCont 4 INFO:Creatbuffer frameSpec->buffer index == 0 INFO:V4L2AllocBuffer Signal 4 </code></span></span>
-
分析命令码不一致的根因,并做修改
根因分析:结构体的大小根据32位与64位编译会产生差异,从而影响VIDIOC_QUERYBUF的值。
修改点:修改用户态编译v4l2_buffer结构体的timestamp定义,并将用户态编译的头文件#include <linux/videodev2.h>替换成
修改过的videodev2.h,即#include ”videodev2.h“。(实际操作为把修改后的videodev2.h拷贝到v4l2_main编译目录中)
kernel/linux/linux-4.19/
<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" /><span style="color:green">diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h</span> <span style="color:green">index ba548d7f0..b0fb48f65 100644</span> <span style="color:green">--- a/include/uapi/linux/videodev2.h</span> <span style="color:green">+++ b/include/uapi/linux/videodev2.h</span> <span style="color:green">@@ -958,13 +958,20 @@</span> struct v4l2_plane { * Contains data exchanged by application and driver using one of the Streaming * I/O methods. */ <span style="color:green">+</span> <span style="color:green">+struct timeval_user {</span> <span style="color:green">+ long tv_sec;</span> <span style="color:green">+ long tv_usec;</span> <span style="color:green">+};</span> <span style="color:green">+</span> struct v4l2_buffer { __u32 index; __u32 type; __u32 bytesused; __u32 flags; __u32 field; <span style="color:green">- struct timeval timestamp;</span> <span style="color:green">+ //struct timeval timestamp;</span> <span style="color:green">+ struct timeval_user timestamp;</span> struct v4l2_timecode timecode; __u32 sequence; <strong>diff</strong></code></span></span>
drivers/peripheral
<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" /><span style="color:green">diff --git a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h</span> <span style="color:green">index 00ddea7..962ebc3 100755</span> <span style="color:green">--- a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h</span> <span style="color:green">+++ b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h</span> <span style="color:green">@@ -15,7 +15,8 @@</span> #ifndef HOS_CAMERA_PROJET_HARDWARE_H #define HOS_CAMERA_PROJET_HARDWARE_H <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> namespace OHOS::Camera { #define PREVIEW_PIXEL_FORMAT V4L2_PIX_FMT_YUV420 <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h</span> <span style="color:green">index 6f45882..a8d6819 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h</span> <span style="color:green">@@ -19,7 +19,8 @@</span> #include <mutex> #include <map> #include <cstring> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <sys/ioctl.h> #include "v4l2_common.h" #if defined(V4L2_UTEST) || defined (V4L2_MAIN_TEST) <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h</span> <span style="color:green">index 5b93f36..05191a7 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h</span> <span style="color:green">@@ -16,7 +16,8 @@</span> #ifndef HOS_CAMERA_V4L2_CONTROL_H #define HOS_CAMERA_V4L2_CONTROL_H <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <errno.h> #include <sys/ioctl.h> #include "v4l2_common.h" <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h</span> <span style="color:green">index 10dc9b4..e3b3056 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h</span> <span style="color:green">@@ -19,7 +19,8 @@</span> #include <mutex> #include <thread> #include <vector> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <sys/epoll.h> #include <sys/ioctl.h> #include <sys/types.h> <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h</span> <span style="color:green">index de892e9..44bb1b4 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h</span> <span style="color:green">@@ -19,7 +19,8 @@</span> #include <vector> #include <cstring> #include <fcntl.h> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <sys/ioctl.h> #include <sys/stat.h> #include <unistd.h> <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h</span> <span style="color:green">index 1a62f37..96c70aa 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h</span> <span style="color:green">@@ -18,7 +18,8 @@</span> #include <thread> #include <fcntl.h> #include <linux/netlink.h> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <sys/ioctl.h> #include <sys/select.h> #include <linux/netlink.h>zz <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span> <span style="color:green">index b351f49..5483b85 100755</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span> <span style="color:green">@@ -22,7 +22,8 @@</span> #include <sys/mman.h> #include <sys/time.h> #include <linux/fb.h> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include "securec.h" #include "v4l2_uvc.h" #include "v4l2_dev.h" <span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp</span> <span style="color:green">index 778cf05..96618be 100644</span> <span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp</span> <span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp</span> <span style="color:green">@@ -14,7 +14,8 @@</span> */ #include <cstring> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <sys/ioctl.h> #include "v4l2_stream.h" <span style="color:green">diff --git a/camera/hal/test/v4l2/include/test_display.h b/camera/hal/test/v4l2/include/test_display.h</span> <span style="color:green">index d437e26..8e5205e 100644</span> <span style="color:green">--- a/camera/hal/test/v4l2/include/test_display.h</span> <span style="color:green">+++ b/camera/hal/test/v4l2/include/test_display.h</span> <span style="color:green">@@ -44,7 +44,8 @@</span> #include <errno.h> #include <getopt.h> #include <linux/fb.h> <span style="color:green">-#include <linux/videodev2.h></span> <span style="color:green">+//#include <linux/videodev2.h></span> <span style="color:green">+#include "videodev2.h"</span> #include <mutex> #include <pthread.h> #include <stdlib.h> <span style="color:green">@@ -138,4 +139,4 @@</span> public: void StartCapture(int streamId, int captureId, bool shutterCallback, bool isStreaming); float calTime(struct timeval start, struct timeval end); }; <span style="color:green">-#endif</span> \ No newline at end of file <span style="color:green">+#endif</span> <strong>diff</strong></code></span></span>
再次编译v4l2_main后执行,log无报错。HDMI屏上就显示预览画面。
回过头来再看整个调试流程,发现该摄像头支持YUYV格式和MJPEG格式,查看v4l2_main中默认预览用的是YUYV,改为MJPEG再次调试。
修改点:
<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" /><span style="color:green">diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">index b351f49..5483b85 100755</span>
<span style="color:green">--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp</span>
<span style="color:green">@@ -394,7 +395,9 @@</span> void V4L2SetDeviceFormat(DeviceFormat& format, const std::string devname)
if (devname == "uvcvideo" || devname == "uvcvideo1") {
if (g_isPreviewOnUvc) {
<span style="color:green">- format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;</span>
<span style="color:green">+ CAMERA_LOGD("cyyanl enter V4L2SetDeviceFormat : g_isPreviewOnUvc\n");</span>
<span style="color:green">+ //format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;</span>
<span style="color:green">+ format.fmtdesc.pixelformat = V4L2_PIX_FMT_MJPEG;</span>
format.fmtdesc.width = width;
format.fmtdesc.height = height;
}
<strong>diff</strong></code></span></span>
v4l2_main执行结果:该显示异常,目前先不分析MJPEG格式。
到此为止,v4l2_main运行正常,可以预览(预览画面色彩异常与屏幕显示格式有关,暂不做修改)。接下来调试拍照和录像。
执行v4l2_main后,先输入u进行uvc预览,再运行c进行拍照,再运行v进行录像,照片和录像文件生成到当前执行的路径下:
<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" /># ls -l *.jpeg
-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC0.jpeg
-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC1.jpeg
-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC2.jpeg
-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC3.jpeg
#
# ls -l *.h264
-rwxrw-rw- 1 root 29034400 85401600 2013-01-18 15:20 uvc.h264
</code></span></span>
将如上文件导出到电脑端查看:
照片uvc0.jpeg
录像uvc.h264在手机端可查看,播放正常。
附录
<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" /><span style="color:green">/**
* struct v4l2_buffer - video buffer info
* @index: id number of the buffer
* @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for
* multiplanar buffers);
* @bytesused: number of bytes occupied by data in the buffer (payload);
* unused (set to 0) for multiplanar buffers
* @flags: buffer informational flags
* @field: enum v4l2_field; field order of the image in the buffer
* @timestamp: frame timestamp
* @timecode: frame timecode
* @sequence: sequence count of this frame
* @memory: enum v4l2_memory; the method, in which the actual video data is
* passed
* @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
* offset from the start of the device memory for this plane,
* (or a "cookie" that should be passed to mmap() as offset)
* @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
* a userspace pointer pointing to this buffer
* @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF;
* a userspace file descriptor associated with this buffer
* @planes: for multiplanar buffers; userspace pointer to the array of plane
* info structs for this buffer
* @length: size in bytes of the buffer (NOT its payload) for single-plane
* buffers (when type != *_MPLANE); number of elements in the
* planes array for multi-plane buffers
*
* Contains data exchanged by application and driver using one of the Streaming
* I/O methods.
*/</span>
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_buffer</span> {
__u32 index;
__u32 type;
__u32 bytesused;
__u32 flags;
__u32 field;
<span style="color:olive">struct</span> <span style="color:#0055af">timeval</span> <span style="color:#0055af">timestamp</span>;
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_timecode</span> <span style="color:#0055af">timecode</span>;
__u32 sequence;
<span style="color:green">/* memory location */</span>
__u32 memory;
<span style="color:olive">union</span> {
__u32 offset;
<em><span style="color:green">unsigned</span></em> <em><span style="color:green">long</span></em> userptr;
<span style="color:olive">struct</span> <span style="color:#0055af">v4l2_plane</span> *<span style="color:#0055af">planes</span>;
__s32 fd;
} m;
__u32 length;
__u32 reserved2;
__u32 reserved;
}
<strong>c</strong></code></span></span>
TP
TP驱动模型
主要包含Input模块HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作input设备的驱动能力接口,HDI接口主要包括如下三大类:
- InputManager:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;
- InputReporter:负责输入事件的上报,包括注册、注销数据上报回调函数等;
- InputController:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。
图 1 INPUT模块HDI接口层框架图
相关目录下源代码目录结构如下所示
<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/peripheral/input
├── hal # input模块的hal层代码
│ └── include # input模块hal层内部的头文件
│ └── src # input模块hal层代码的具体实现
├── interfaces # input模块对上层服务提供的驱动能力接口
│ └── include # input模块对外提供的接口定义
├── test # input模块的测试代码
│ └── unittest # input模块的单元测试代码
</code></span></span>
TP HDF驱动适配
配置适配
关闭linux tp驱动开关,在<rockchip_linux_defconfig>文件中关闭TOUCHSCREEN_GT9XX,具体操作如下,将CONFIG_TOUCHSCREEN_GT9XX=y修改为
<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" /><span style="color:green"># CONFIG_TOUCHSCREEN_GT9XX is not set</span>
<strong>makefile</strong></code></span></span>
驱动适配
-
在vendor/rockchip/rk3399/hdf_config/khdf/device_info/device_info.hcs修改或增加如下内容
<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_touch_chip :: device { device0 :: deviceNode { policy = <span style="color:navy">0</span>; priority = <span style="color:navy">130</span>; preload = <span style="color:navy">0</span>; permission = <span style="color:navy">0660</span>; moduleName = <span style="color:green">"HDF_TOUCH_GT911"</span>; serviceName = <span style="color:green">"hdf_touch_gt911_service"</span>; deviceMatchAttr = <span style="color:green">"zsj_gt911_5p5"</span>; } } <strong>json</strong></code></span></span>
-
在vendor/rockchip/rk3399/hdf_config/khdf/input/input_config.hcs修改或增加如下内容
<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" /> busConfig { <span style="color:green">// 0:i2c 1:spi</span> busType = <span style="color:navy">0</span>; <span style="color:green">// I2C通信方式</span> busNum = <span style="color:navy">7</span>; <span style="color:green">// cpu对应touch芯片的I2C编号</span> clkGpio = <span style="color:navy">72</span>; <span style="color:green">// I2C clk对应的IO引脚编号</span> dataGpio = <span style="color:navy">71</span>; <span style="color:green">// I2C data对应的IO引脚标号</span> i2cClkIomux = [<span style="color:navy">0xFF77E004</span>, <span style="color:navy">0x2</span>]; <span style="color:green">// I2C [clk引脚对应的复用寄存器,配置为I2C7_clk功能]</span> i2cDataIomux = [<span style="color:navy">0xFF77E000</span>, <span style="color:navy">0x8000</span>]; <span style="color:green">// I2C [data引脚对应的复用寄存器,配置为I2C7_DATA功能]</span> } pinConfig { rstGpio = <span style="color:navy">150</span>; <span style="color:green">// touch芯片rst对应的IO引脚编号</span> intGpio = <span style="color:navy">52</span>; <span style="color:green">// touch芯片int对应的IO引脚编号</span> rstRegCfg = [<span style="color:navy">0xFF77E028</span>, <span style="color:navy">0x00000000</span>]; <span style="color:green">// rst引脚对应的复用功能寄存器,配置为普通io口</span> intRegCfg = [<span style="color:navy">0xFF770018</span>, <span style="color:navy">0x00000000</span>]; <span style="color:green">// int引脚对应的复用功能寄存器,配置为普通io口</span> } <strong>json</strong></code></span></span>
FAQ问题
解决触屏横竖反转问题
修改drivers/framework/model/input/driver/touchscreen/touch_gt911.c中的ParsePointData函数,x和y对调即可
InputPinMuxCfg函数修改
<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">InputPinMuxCfg</span><span style="color:#0055af">(<em><span style="color:green">uint32_t</span></em> regAddr, <em><span style="color:green">int32_t</span></em> regSize, <em><span style="color:green">uint32_t</span></em> regValue)</span>
{
<em><span style="color:green">uint8_t</span></em> *base = <span style="color:navy">NULL</span>;
<em><span style="color:green">uint32_t</span></em> data = <span style="color:navy">0</span>;
<span style="color:olive">if</span> (regAddr == <span style="color:navy">0</span>) {
HDF_LOGE(<span style="color:green">"%s: regAddr invalid"</span>, __func__);
<span style="color:olive">return</span> HDF_FAILURE;
}
HDF_LOGE(<span style="color:green">"regAddr = 0x%x, regSize = 0x%x"</span>, regAddr, regSize);
base = OsalIoRemap(regAddr, regSize);
<span style="color:olive">if</span> (base == <span style="color:navy">NULL</span>) {
HDF_LOGE(<span style="color:green">"%s: ioremap failed"</span>, __func__);
<span style="color:olive">return</span> HDF_FAILURE;
}
<span style="color:green">// 写入寄存器前先读出数据,保存原有的配置,方式修改原有的其他引脚功能配置。</span>
data = OSAL_READL(base);
data |= regValue;
OSAL_WRITEL(data, base);
OsalIoUnmap((<em><span style="color:green">void</span></em> *)base);
<span style="color:olive">return</span> HDF_SUCCESS;
}
<strong>c</strong></code></span></span>
RK3399引脚GPIOn_KX 编号号确认方法
<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" />num = 32n+(K-A)8+X
<span style="color:green"># GPIO1_B2 = 1 * 32 + (B-A)*8 + 2 = 32 + 8 + 1 = 42</span>
<strong>shell</strong></code></span></span>
功能 | IO名称 | IO编号 |
---|---|---|
TP_INT | GPIO1_C4 | 52 |
TP_RST | GPIO4_C6 | 150 |
TP_SDA | GPIO2_A7 | 71 |
TP_SCL | GPIO2_B0 | 72 |
RK3399引脚GPIOn_KX复用功能配置
- GPIO复用功能寄存器基地址FF77_0000
- 找到GPIOn_K复用功能寄存器偏移量
- 确定GPIOn_K复用功能寄存器,基地址+偏移量
- 根据复用功能寄存器介绍,写入对应引脚的复用功能
功能 | IO名称 | 复用寄存器地址 | 控制位 | 对应数值 |
---|---|---|---|---|
TP_INT | GPIO1_C4 | 0xFF770018 | 9:8 | 2'b00:GPIO |
TP_RST | GPIO4_C6 | 0xFF77E028 | 13:12 | 2'b00:GPIO |
TP_SDA | GPIO2_A7 | 0xFF77E000 | 15:14 | 2'b10: i2c7nfc_sda |
TP_SCL | GPIO2_B0 | 0xFF77E004 | 1:0 | 2'b10: i2c7nfc_scl |