OpenHarmony南向设备开发:标准系统方案之扬帆移植案例

标准系统方案之扬帆移植案例

本文章是基于瑞芯微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>

主要的配置内容包括:

  1. “product_name”: “yangfan”,—产品名:yangfan
  2. “device_company”: “rockchip”,—单板厂商:rockchip
  3. “device_build_path”: “device/board/isoftstone/yangfan”,—设备构建路径:device/board/isoftstone/yangfan
  4. “target_cpu”: “arm”,—目标cpu:arm
  5. “type”: “standard”,—配置系统的级别:standard
  6. “version”: “3.0”,—版本:3.0
  7. “board”: “yangfan”,—单板名:yangfan
  8. “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中,主要有以下工作:

  1. 使能二级启动

在//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>
  1. 将主线编译出来的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驱动的架构组成、功能部件的实现和服务节点详细介绍。

  1. ADM(Audio Driver Model)

    音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配HOS系统。

  2. Audio Control Dispatch

    接收lib层的控制指令并将控制指令分发到驱动层。

  3. Audio Stream Dispatch

    向上通过lib层完成数据流的接收,向下完成数据流对驱动层的分发。

  4. Card Manager

    多声卡管理模块。每个声卡含有Dai、Platform、Codec、Accessory、Dsp、Sapm模块。

  5. Platform Driver

    驱动适配层。

  6. 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流程说明
启动流程

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

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

  1. 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Get函数接口获取可设置音量范围。
  2. Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Set函数接口设置音量。
实现说明
  1. 驱动注册

    以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>
  2. 数据流数据分发

    当录音或者播放时,上层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>
  3. 控制功能注册接口

    音量控制、增益控制、通路控制等控制功能都是通过此接口添加到声卡控制列表。

    <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>
  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" /><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>
  5. 控制流数据分发

    当录音或者播放时,上层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>
  1. 音频控制流服务

    用来接收上层lib层下发的控制指令,包括音量控制、增益控制、通路控制,这些控制指令都是通过控制流服务下发到驱动。

  2. 音频数据播放流服务

    用来接收上层lib层下发的音频数据和播放相关的参数,还有播放的启动、暂停、恢复、停止指令,这些指令都是由播放数据流下发到驱动。

  3. 音频数据录音流服务

    用来向上层lib层传输音频数据和接收上层lib层下发的录音相关的参数,还有录音的启动、暂停、恢复、停止指令,这些指令都是由录音数据流下发到驱动。

驱动服务

每个audio设备包括如下服务:

hdf_audio_codec_dev0音频设备名称
dma_service_0dma 驱动服务
dai_servicecpu dai 驱动服务
codec_service_0codec 驱动服务
dsp_service_0dsp 驱动服务(可选项)
hdf_audio_codec_dev1音频设备名称
dma_service_0dma 驱动服务
dai_servicecpu dai 驱动服务
codec_service_1accessory 驱动服务(特指smartPA)
dsp_service_0dsp 驱动服务(可选项)
代码路径
<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驱动模块架构图

img

以Camera Host 部分做如下说明:

  1. HDI实现层(HDI Implementation):对上实现Open Harmony OS相机标准南向接口。
  2. 框架层(PipelineCore):对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能。
  3. 适配层(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路:

  1. 同意用户态和内核态的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>
  2. 分析命令码不一致的根因,并做修改

    根因分析:结构体的大小根据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>
驱动适配
  1. 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>
  2. 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_INTGPIO1_C452
TP_RSTGPIO4_C6150
TP_SDAGPIO2_A771
TP_SCLGPIO2_B072
RK3399引脚GPIOn_KX复用功能配置
  1. GPIO复用功能寄存器基地址FF77_0000
  2. 找到GPIOn_K复用功能寄存器偏移量
  3. 确定GPIOn_K复用功能寄存器,基地址+偏移量
  4. 根据复用功能寄存器介绍,写入对应引脚的复用功能
功能IO名称复用寄存器地址控制位对应数值
TP_INTGPIO1_C40xFF7700189:82'b00:GPIO
TP_RSTGPIO4_C60xFF77E02813:122'b00:GPIO
TP_SDAGPIO2_A70xFF77E00015:142'b10: i2c7nfc_sda
TP_SCLGPIO2_B00xFF77E0041:02'b10: i2c7nfc_scl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值