1# **标准系统方案之扬帆移植案例** 2 3 本文章是基于瑞芯微RK3399芯片的yangfan开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。 4开发板系统移植采用Board仓和SoC代码分离方案,Board仓保存板载驱动的模块,例如音频,Camera,TP,WIFI等驱动模块的适配代码。在SoC仓保存与SoC驱动相关模块,例如I2C,ISP,RGA等驱动模块的适配代码。 5 6## 产品配置和目录规划 7 8### 产品配置 9 10在产品`//vendor/yangfan`目录下创建config.json文件,并指定CPU的架构。`//vendor/yangfan/rk3399.json`配置如下: 11 12``` 13{ 14 "product_name": "yangfan",---产品名:yangfan 15 "device_company": "rockchip",---单板厂商:rockchip 16 "device_build_path": "device/board/isoftstone/yangfan",---设备构建路径:device/board/isoftstone/yangfan 17 "target_cpu": "arm",---目标cpu:arm 18 "type": "standard",---配置系统的级别:standard 19 "version": "3.0",---版本:3.0 20 "board": "yangfan",---单板名:yangfan 21 "enable_ramdisk": true,---启用内存虚拟盘:true 22 "build_selinux": true,---构建selinux:true 23 "inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ], 24 "subsystems": [ 25 { 26 "subsystem": "security", 27 "components": [ 28 { 29 "component": "selinux", 30 "features": [] 31 } 32 ] 33 }, 34 { 35 "subsystem": "communication", 36 "components": [ 37 { 38 "component": "netmanager_ext", 39 "features": [] 40 } 41 ] 42 }, 43 ... 44} 45``` 46 47 48主要的配置内容包括: 49 501. "product_name": "yangfan",---产品名:yangfan 512. "device_company": "rockchip",---单板厂商:rockchip 523. "device_build_path": "device/board/isoftstone/yangfan",---设备构建路径:device/board/isoftstone/yangfan 534. "target_cpu": "arm",---目标cpu:arm 545. "type": "standard",---配置系统的级别:standard 556. "version": "3.0",---版本:3.0 567. "board": "yangfan",---单板名:yangfan 578. "enable_ramdisk": true,---启用内存虚拟盘:true 58 59已定义的子系统可以在`//build/subsystem_config.json`中找到。当然也可以定制子系统。 60 61建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products子系统。该子系统为Hi3516DV300 SOC编译内核,不适合RK3568。 62 63### 目录规划 64 65参考[Board和SoC解耦的设计思路](https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md),并把芯片适配目录规划为: 66 67``` 68device 69├── board --- 单板厂商目录 70│ └── isoftstone --- 单板厂商名字: 71│ └── yangfan --- 单板名:扬帆,主要放置开发板相关的驱动业务代码 72└── soc --- SoC厂商目录 73 └── rockchip --- SoC厂商名字:rockchip 74 └── rk3399 --- SoC Series名:rk3399,主要为芯片原厂提供的一些方案,以及闭源库等 75 76 77``` 78产品样例目录规划为: 79``` 80vendor 81└── isoftstone 82 └── yangfan --- 产品名字:产品、hcs以及demo相关 83``` 84 85## **内核启动** 86 87### 二级启动 88 89二级启动简单来说就是将之前直接挂载system,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init 。 90 91RK3399适配主要是将主线编译出来的ramdisk 打包到boot_linux.img中,主要有以下工作: 92 931. 使能二级启动 94 95在//vendor/yangfan/rk3399.json中使能enable_ramdisk。 96 97 ``` 98 { 99 "product_name": "yangfan", 100 "device_company": "rockchip", 101 "device_build_path": "device/board/isoftstone/yangfan", 102 "target_cpu": "arm", 103 "type": "standard", 104 "version": "3.0", 105 "board": "yangfan", 106 "enable_ramdisk": true, 107 "build_selinux": true, 108 ... 109 } 110 ``` 111 1122. 将主线编译出来的ramdsik.img 打包到boot_linux.img 113 114配置: 115 116由于rk 启动uboot 支持从ramdisk 启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img ,因此没有使用主线的its格式,具体配置就是在内核编译脚本make-ohos.sh 中增加: 117 118``` 119function make_extlinux_conf() 120{ 121 dtb_path=$1 122 uart=$2 123 image=$3 124 125 echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF} 126 echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF} 127 echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF} 128 if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then 129 echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF} 130 fi 131 cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4" 132 echo " ${cmdline}" >> ${EXTLINUX_CONF} 133} 134``` 135 136### 打包 137 138增加了打包boot镜像的脚本make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容: 139 140``` 141genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img 142``` 143 144调用make-boot.sh的修改请参考[RK3568 适配二级启动]( https://gitee.com/openharmony/build/pulls/569/files )。 145 146### INIT配置 147 148init相关配置请参考[启动恢复子系统]( https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%90%AF%E5%8A%A8%E6%81%A2%E5%A4%8D%E5%AD%90%E7%B3%BB%E7%BB%9F.md )即可 149 150## **音频** 151 152### 简介 153 154本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Audio驱动框架,包括Audio驱动的架构组成、功能部件的实现和服务节点详细介绍。 155 156![Audio驱动框架图](figures/isoftstone/yangfan-Audio-ADM.png) 157 1581. ADM(Audio Driver Model) 159 160 音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配HOS系统。 161 1622. Audio Control Dispatch 163 164 接收lib层的控制指令并将控制指令分发到驱动层。 165 1663. Audio Stream Dispatch 167 168 向上通过lib层完成数据流的接收,向下完成数据流对驱动层的分发。 169 1704. Card Manager 171 172 多声卡管理模块。每个声卡含有Dai、Platform、Codec、Accessory、Dsp、Sapm模块。 173 1745. Platform Driver 175 176 驱动适配层。 177 1786. SAPM(Smart Audio Power Manager) 179 180 电源管理模块,对整个ADM电源进行功耗策略优化。 181 182### Audio驱动介绍 183 184#### 代码目录 185 186``` 187drivers 188 ├── framework 189 │ └── model 190 │ │ └── audio #框架代码 191 │ │ ├─── common #公共实现 192 │ │ ├─── core #核心 193 │ │ ├─── dispatch #控制流和数据流实现 194 │ │ └── sapm #电源管理 195 │ └── include 196 │ └── audio #对外接口 197 ├── adapter 198 │ └──khdf 199 │ └── linux 200 │ └── model 201 │ └── audio #编译文件 202 └── peripheral 203 └── audio 204 └── chipsets 205 └── rk3399 #驱动实现 206 ├── accessory #SmartPA驱动 207 ├── dai #I2S驱动 208 └── soc #Dma驱动 209``` 210 211#### Audio流程说明 212 213##### 启动流程 214 215![](figures/isoftstone/yangfan-Audio-start.png) 216 2171. 系统启动时audio模块的Platform、Codec、Accessory、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。 2182. 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。 2193. ADM模块读取hdf_audio_driver_0(音频card_0)和hdf_audio_driver_1(音频card_1)配置信息,加载各模块的具体设备。 2204. ADM模块调用各模块的初始化函数对各模块设备进行初始化。 2215. 将初始化成功的音频设备添加到cardManager链表。 222 223##### 播放流程 224 225![](figures/isoftstone/yangfan-Audio-play.png) 226 2271. 播放音频,首先Interface Lib层通过播放流服务下发Render Open指令,Render Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。 2282. Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。 2293. Interface Lib层通过播放流服务下发硬件参数,Render Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。 2304. Interface Lib层通过播放流服务下发播放启动指令,Render Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。 2315. Interface Lib层通过播放流服务下发音频数据,Render Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。 2326. Interface Lib层通过播放流服务下发播放停止指令,Render Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。 2337. Interface Lib层通过播放流服务下发Render Close指令,Render Stream Dispatch服务收到指令后调用Platform AudioRenderClose接口对已申请资源进行释放。 234 235##### 控制流程 236 237![](figures/isoftstone/yangfan-Audio-commond.png) 238 2391. 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Get函数接口获取可设置音量范围。 2402. Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Set函数接口设置音量。 241 242#### 实现说明 243 2441. 驱动注册 245 246 以codec的注册函数为例,当codec驱动初始化时调用如下codec注册函数,将codec注册到codecController链表中。 247 248 ```c 249 int32_t AudioRegisterCodec(struct HdfDeviceObject *device, struct CodecData *codecData, struct DaiData *daiData) 250 { 251 ... 252 253 codec = (struct CodecDevice *)OsalMemCalloc(sizeof(*codec)); 254 ... 255 256 OsalMutexInit(&codec->mutex); 257 codec->devCodecName = codecData->drvCodecName; 258 codec->devData = codecData; 259 codec->device = device; 260 261 ret = AudioSocRegisterDai(device, daiData); 262 ... 263 DListInsertHead(&codec->list, &codecController); 264 ... 265 } 266 ``` 267 2682. 数据流数据分发 269 270 当录音或者播放时,上层lib层通过dispatch将数据下发或读取数据,此接口接收到lib层的请求后,将数据进行分发或将数据返回。 271 272 ```c 273 static int32_t StreamDispatch(struct HdfDeviceIoClient *client, int cmdId, 274 struct HdfSBuf *data, struct HdfSBuf *reply) 275 { 276 unsigned int count = sizeof(g_streamDispCmdHandle) / sizeof(g_streamDispCmdHandle[0]); 277 for (unsigned int i = 0; i < count; ++i) { 278 if ((cmdId == (int)(g_streamDispCmdHandle[i].cmd)) && (g_streamDispCmdHandle[i].func != NULL)) { 279 return g_streamDispCmdHandle[i].func(client, data, reply); 280 } 281 } 282 ADM_LOG_ERR("invalid [cmdId=%d]", cmdId); 283 return HDF_FAILURE; 284 } 285 ``` 286 2873. 控制功能注册接口 288 289 音量控制、增益控制、通路控制等控制功能都是通过此接口添加到声卡控制列表。 290 291 ```c 292 int32_t AudioAddControls(struct AudioCard *audioCard, const struct AudioKcontrol *controls, int32_t controlMaxNum) 293 { 294 ... 295 296 for (i = 0; i < controlMaxNum; i++) { 297 control = AudioAddControl(audioCard, &controls[i]); 298 if (control == NULL) { 299 ADM_LOG_ERR("Add control fail!"); 300 return HDF_FAILURE; 301 } 302 DListInsertHead(&control->list, &audioCard->controls); 303 } 304 ADM_LOG_DEBUG("Success."); 305 return HDF_SUCCESS; 306 } 307 ``` 308 3094. 电源管理接口 310 311 添加组件实现: 312 313 ```c 314 int32_t AudioSapmNewComponents(struct AudioCard *audioCard, 315 const struct AudioSapmComponent *component, int32_t cptMaxNum) 316 { 317 ... 318 319 for (i = 0; i < cptMaxNum; i++) { 320 ret = AudioSapmNewComponent(audioCard, component); 321 if (ret != HDF_SUCCESS) { 322 ADM_LOG_ERR("AudioSapmNewComponent fail!"); 323 return HDF_FAILURE; 324 } 325 component++; 326 } 327 328 return HDF_SUCCESS; 329 } 330 331 ``` 332 333 添加通路实现: 334 335 ```c 336 337 int32_t AudioSapmAddRoutes(struct AudioCard *audioCard, const struct AudioSapmRoute *route, int32_t routeMaxNum) 338 { 339 ... 340 341 for (i = 0; i < routeMaxNum; i++) { 342 ret = AudioSapmAddRoute(audioCard, route); 343 if (ret != HDF_SUCCESS) { 344 ADM_LOG_ERR("AudioSapmAddRoute failed!"); 345 return HDF_FAILURE; 346 } 347 route++; 348 } 349 return HDF_SUCCESS; 350 } 351 352 ``` 353 354 添加控制功能实现: 355 356 ```c 357 358 int32_t AudioSapmNewControls(struct AudioCard *audioCard) 359 { 360 ... 361 362 DLIST_FOR_EACH_ENTRY(sapmComponent, &audioCard->components, struct AudioSapmComponent, list) { 363 if (sapmComponent->newCpt) { 364 continue; 365 } 366 if (sapmComponent->kcontrolsNum > 0) { 367 sapmComponent->kcontrols = OsalMemCalloc(sizeof(struct AudioKcontrol*) * sapmComponent->kcontrolsNum); 368 if (sapmComponent->kcontrols == NULL) { 369 ADM_LOG_ERR("malloc kcontrols fail!"); 370 return HDF_FAILURE; 371 } 372 } 373 374 switch (sapmComponent->sapmType) { 375 case AUDIO_SAPM_ANALOG_SWITCH: 376 case AUDIO_SAPM_MIXER: 377 case AUDIO_SAPM_MIXER_NAMED_CTRL: 378 case AUDIO_SAPM_SPK: 379 case AUDIO_SAPM_PGA: 380 ret = AudioSapmNewMixerControls(sapmComponent, audioCard); 381 break; 382 case AUDIO_SAPM_MUX: 383 case AUDIO_SAPM_VIRT_MUX: 384 case AUDIO_SAPM_VALUE_MUX: 385 ret = AudioSapmNewMuxControls(sapmComponent, audioCard); 386 break; 387 default: 388 ret = HDF_SUCCESS; 389 break; 390 } 391 ... 392 393 ReadInitComponentPowerStatus(sapmComponent); 394 sapmComponent->newCpt = 1; 395 DListInsertTail(&sapmComponent->dirty, &audioCard->sapmDirty); 396 } 397 398 ret = AudioSapmPowerComponents(audioCard); 399 ... 400 401 return HDF_SUCCESS; 402 } 403 404 ``` 405 4065. 控制流数据分发 407 408 当录音或者播放时,上层lib层通过dispatch将控制指令下发,此接口接收到lib层的控制指令后,将控制指令分发到各驱动模块。 409 410 ```c 411 static int32_t ControlDispatch(struct HdfDeviceIoClient *client, int cmdId, 412 struct HdfSBuf *data, struct HdfSBuf *reply) 413 { 414 ... 415 416 if (cmdId >= AUDIODRV_CTRL_IOCTRL_ELEM_BUTT || cmdId < 0) { 417 ADM_LOG_ERR("Invalid [cmdId=%d].", cmdId); 418 return HDF_FAILURE; 419 } 420 421 for (i = 0; i < HDF_ARRAY_SIZE(g_controlDispCmdHandle); ++i) { 422 if ((cmdId == (int)(g_controlDispCmdHandle[i].cmd)) && (g_controlDispCmdHandle[i].func != NULL)) { 423 return g_controlDispCmdHandle[i].func(client, data, reply); 424 } 425 } 426 return HDF_FAILURE; 427 } 428 ``` 429 430### Audio服务介绍 431 432#### 服务节点 433 434基于ADM框架的audio驱动对HDI层提供三个服务hdf_audio_render、hdf_audio_capture、hdf_audio_control。 435开发板audio驱动服务节点如下: 436 437``` 438console:/dev # ls -al hdf_audio_* 439crw------- 1 system system 249, 5 1970-01-01 00:21 hdf_audio_capture //录音数据流服务。 440crw------- 1 system system 249, 3 1970-01-01 00:21 hdf_audio_codec_dev0 //音频设备名称。 441crw------- 1 system system 249, 4 1970-01-01 00:21 hdf_audio_control //音频控制流服务。 442crw------- 1 system system 249, 6 1970-01-01 00:21 hdf_audio_render //播放数据流务。 443``` 444 4451. 音频控制流服务 446 447 用来接收上层lib层下发的控制指令,包括音量控制、增益控制、通路控制,这些控制指令都是通过控制流服务下发到驱动。 448 4492. 音频数据播放流服务 450 451 用来接收上层lib层下发的音频数据和播放相关的参数,还有播放的启动、暂停、恢复、停止指令,这些指令都是由播放数据流下发到驱动。 452 4533. 音频数据录音流服务 454 455 用来向上层lib层传输音频数据和接收上层lib层下发的录音相关的参数,还有录音的启动、暂停、恢复、停止指令,这些指令都是由录音数据流下发到驱动。 456 457#### 驱动服务 458 459每个audio设备包括如下服务: 460 461| **hdf_audio_codec_dev0** | **音频设备名称** | 462| ------------------------ | ---------------------- | 463| dma_service_0 | dma 驱动服务 | 464| dai_service | cpu dai 驱动服务 | 465| codec_service_0 | codec 驱动服务 | 466| dsp_service_0 | dsp 驱动服务(可选项) | 467 468| **hdf_audio_codec_dev1** | **音频设备名称** | 469| ------------------------ | --------------------------------- | 470| dma_service_0 | dma 驱动服务 | 471| dai_service | cpu dai 驱动服务 | 472| codec_service_1 | accessory 驱动服务(特指smartPA) | 473| dsp_service_0 | dsp 驱动服务(可选项) | 474 475#### 代码路径 476 477``` 478vendor/rockchip/rk3399/hdf_config/khdf 479├── audio #audio私有配置文件 480├── device_info 481| └── device_info.hcs #设备配置文件 482└── hdf.hcs #引用hcs配置文件 483``` 484 485#### 配置节点说明 486 487以codec驱动为例,在device_info.hcs文件中的audio host节点下添加codec节点信息。 488 489``` 490 audio :: host { 491 hostName = "audio_host"; 492 priority = 60; 493... 494 device_codec :: device { 495 device0 :: deviceNode { 496 policy = 1; 497 priority = 50; 498 preload = 0; 499 permission = 0666; 500 moduleName = "CODEC_ES8316"; 501 serviceName = "codec_service_0"; 502 deviceMatchAttr = "hdf_codec_driver"; 503 } 504 } 505... 506 } 507``` 508 509#### 实现驱动 510 511在驱动文件中实现与device_info.hcs配置节点moduleName相同的驱动逻辑。 512 513``` 514/* HdfDriverEntry implementations */ 515static int32_t Es8316DriverBind(struct HdfDeviceObject *device) 516{ 517... 518 return HDF_SUCCESS; 519} 520 521static int32_t Es8316DriverInit(struct HdfDeviceObject *device) 522{ 523... 524 return HDF_SUCCESS; 525} 526 527/* HdfDriverEntry definitions */ 528struct HdfDriverEntry g_es8316DriverEntry = { 529 .moduleVersion = 1, 530 .moduleName = "CODEC_ES8316", 531 .Bind = Es8316DriverBind, 532 .Init = Es8316DriverInit, 533 .Release = NULL, 534}; 535HDF_INIT(g_es8316DriverEntry); 536``` 537 538### 总结 539 540 基于HDF框架的ADM音频框架,为Open Harmony的音频开发提供了统一的架构基础,为各平台音频驱动适配提供了统一的接口。音频驱动可以一平台开发多平台适用,提高了开发效率。此文档对ADM框架进行了简单的介绍,希望有助于开发者开发和应用。 541 542## **Camera** 543 544### 简介 545 546本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Camera驱动框架,包括Camera驱动的架构组成、功能部件的实现和服务节点详细介绍。 547 548### Camera驱动框架图 549 550 OpenHarmony HDF Camera驱动模块架构图 551 552![img](https://gitee.com/openharmony/drivers_peripheral/raw/master/camera/figures/logic-view-of-modules-related-to-this-repository_zh.png) 553 554以Camera Host 部分做如下说明: 555 5561. HDI实现层(HDI Implementation):对上实现Open Harmony OS相机标准南向接口。 5572. 框架层(PipelineCore):对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能。 5583. 适配层(Platform Adaption):屏蔽底层芯片和OS差异,支持多平台适配。 559 560对于rk3399E/T的Usb Camera来分析,内核使用linux-4.19。Usb Camera依赖linux下的V4L2的uvc,从上面的框架图分析HDF Camera已经实现了兼容linux 的 V4L2 uvc,所以调试过程首先要保证uvc所涉及的USB和Camera的驱动正常。 561 562 563### Camera驱动介绍 564 565#### 配置信息 566 567arch/arm64/configs/rockchip_linux_defconfig 568 569```c 570CONFIG_VIDEO_V4L2_SUBDEV_API=y 571CONFIG_MEDIA_USB_SUPPORT=y 572CONFIG_USB_VIDEO_CLASS=y 573``` 574 575#### 节点信息 576 577插入Usb Camera 前 578 579``` 580# ls -l dev/video* 581crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0 582crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1 583crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2 584crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3 585crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4 586crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5 587crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6 588crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7 589crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8 590crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9 591# 592``` 593 594插入Usb Camera后新增节点dev/video10和dev/video11 595 596``` 597# ls -l dev/video* 598crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0 599crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1 600crw------- 1 root root 81, 10 2013-01-18 11:01 dev/video10 601crw------- 1 root root 81, 11 2013-01-18 11:01 dev/video11 602crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2 603crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3 604crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4 605crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5 606crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6 607crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7 608crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8 609crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9 610# 611``` 612 613#### 打开设备节点 614 615在Open Harmony OS的代码环境中,编译如下代码为可执行程序,在开发板测执行,无报错说明该节点open success。 616 617```c 618#include <stdio.h> 619#include <sys/types.h> 620#include <sys/stat.h> 621#include <fcntl.h> 622#include <stdlib.h> 623#include <unistd.h> 624#include <sys/ioctl.h> 625#include <linux/videodev2.h> 626#include <string.h> 627#include <sys/mman.h> 628 629int main(void) 630{ 631 // 1. 打开设备 632 int fd = open("/dev/video10", O_RDWR); 633 if (fd < 0) { 634 printf("open device fail\n"); 635 return -1; 636 } 637 638 close(fd); 639 return 0; 640} 641``` 642 643#### 获取参数 644 645```c 646#include <stdio.h> 647#include <sys/types.h> 648#include <sys/stat.h> 649#include <fcntl.h> 650#include <stdlib.h> 651#include <unistd.h> 652#include <sys/ioctl.h> 653#include <linux/videodev2.h> 654#include <string.h> 655#include <sys/mman.h> 656 657int main(void) 658{ 659 // 1. 打开设备 660 int fd = open("/dev/video10", O_RDWR); 661 if (fd < 0) { 662 printf("open device fail\n"); 663 return -1; 664 } 665 666 // 2. 获取摄像头支持的格式 ioctl(文件描述符, 命令, 与命令对应的结构体) 667 struct v4l2_fmtdesc v4fmt; 668 v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 669 int i = 0; 670 while(1) { 671 v4fmt.index = i++; 672 int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt); 673 if (ret < 0) { 674 printf("get fmt fail\n"); 675 } 676 unsigned char *p = (unsigned char*)&v4fmt.pixelformat; 677 printf("index=%d\n", v4fmt.index); 678 printf("flags=%d\n", v4fmt.flags); 679 printf("description=%s\n", v4fmt.description); 680 printf("pixelformat=%c%c%c%c\n", p[0], p[1], p[2], p[3]); 681 printf("reserved=%d\n", v4fmt.reserved[0]); 682 } 683 684 close(fd); 685 return 0; 686} 687``` 688 689在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。结果显示支持YUYV和MJPEG 2种输出格式。 690 691 index=0 692 flags=0 693 description=YUYV 4:2:2 694 pixelformat=YUYV 695 reserved=0 696 697 index=1 698 flags=1 699 description=Motion-JPEG 700 pixelformat=MJPG 701 reserved=0 702#### 设置缓冲区队列 703 704```c 705#include <stdio.h> 706#include <sys/types.h> 707#include <sys/stat.h> 708#include <fcntl.h> 709#include <stdlib.h> 710#include <unistd.h> 711#include <sys/ioctl.h> 712#include <linux/videodev2.h> 713#include <string.h> 714#include <sys/mman.h> 715 716int main(void) 717{ 718 // 1. 打开设备 719 int fd = open("/dev/video10", O_RDWR); 720 if (fd < 0) { 721 printf("open device fail\n"); 722 return -1; 723 } 724 725 // 2. 设置采集格式 726 struct v4l2_format vfmt; 727 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 728 vfmt.fmt.pix.width = 640; 729 vfmt.fmt.pix.height = 480; 730 vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 设置的视频采集格式(与上面获取的格式一致) 731 int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt); // 设置格式 732 if (ret < 0) { 733 printf("set fmt fail\n"); 734 return -1; 735 } 736 memset(&vfmt, 0, sizeof(vfmt)); 737 vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 738 ret = ioctl(fd, VIDIOC_G_FMT, &vfmt); // 获取格式 739 if (ret < 0) { 740 printf("set->get fmt fail\n"); 741 return -1; 742 } 743 744 // 3. 申请内核缓冲区队列 745 struct v4l2_requestbuffers reqbuffer; 746 reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 747 reqbuffer.count = 4; // 申请4个缓冲区 748 reqbuffer.memory = V4L2_MEMORY_MMAP; // 内存映射方式 MMAP/USERPTR 749 ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); // 分配内存 750 if (ret < 0) { 751 printf("req buffer fail\n"); 752 return -1; 753 } 754 755 // 4. 关闭设备 756 close(fd); 757 return 0; 758} 759``` 760 761在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。 762 763执行结果:req buffer fail 764 765原因分析:ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); 失败 766 767定位方法1:在内核中加LOG定位VIDIOC_REQBUFS失败的地方。发现所有的ioctl命令下发后都会使用drivers/media/v4l2-core/v4l2-ioctl.c中的video_usercopy接口,但还是没有定位到具体的失败原因。 768 769定位方法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屏幕可以出来正常的预览画面。 770 771通过如上的操作后,基本可以确认linux的V4L2 uvc驱动和外设Usb Camera驱动都是正常的。接下来就该调试Open Harmony OS的HDF Camera了。 772 773#### 接口介绍 774 775查看现有Open Harmony OS上的关于camera的可执行程序:ohos_camera_demo、v4l2_main 776 777##### ohos_camera_demo 778 779执行结果:输入o后无预览画面,也无LOG报错信息。 780 781``` 782# ohos_camera_demo 783GetUintParameter debug.bytrace.tags.enableflags error. 784Options: 785-h | --help Print this message 786-o | --offline stream offline test 787-c | --capture capture one picture 788-w | --set WB Set white balance Cloudy 789-v | --video capture Viedeo of 10s 790-a | --Set AE Set Auto exposure 791-f | --Set Flashlight Set flashlight ON 5s OFF 792-q | --quit stop preview and quit this app 793o 794Options: 795-h | --help Print this message 796-o | --offline stream offline test 797-c | --capture capture one picture 798-w | --set WB Set white balance Cloudy 799-v | --video capture Viedeo of 10s 800-a | --Set AE Set Auto exposure 801-f | --Set Flashlight Set flashlight ON 5s OFF 802-q | --quit stop preview and quit this app 803 804``` 805 806原因分析:ohos_camera_demo目前仅支持MPP,不支持V4L2,故先放弃该demo调试。 807 808##### v4l2_main 809 810执行结果:输入u 报错:ERROR:main test:cannot open framebuffer /dev/fb0 file node 811 812``` 813Options: 814-h | --help Print this message 815-p | --preview start preview on platform sensor 816-c | --capture capture one picture 817-w | --set WB Set white balance Cloudy 818-e | --Set AE Set exposure time 819-v | --video capture Viedeo of 10s 820-u | --uvc start preview on uvc preview 821-a | --Set ATE Set Auto exposure 822-q | --quit stop preview and quit this app 823INFO:please input command(input -q exit this app) 824 825u 826ERROR:main test:cannot open framebuffer /dev/fb0 file node 827 828INFO:V4L2OpenDevice /dev/video10 829``` 830 831原因分析:查看dev/fb0节点是否存在:不存在fb0节点。继续查找根目录下有无其他fb0节点,出现了dev/graphics/fb0节点。 832 833``` 834# ls -l dev/fb0 835ls: dev/fb0: No such file or directory 836 837# find -name fb0 838./dev/graphics/fb0 839./sys/class/graphics/fb0 840./sys/devices/platform/display-subsystem/graphics/fb0 841``` 842 843需把v4l2_main可执行程序中的dev/fb0改为dev/graphics/fb0。此处fb0为framebuffer,作用是在屏幕上显示预览画面。 844 845修改点:drivers/peripheral 846 847```diff 848diff --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 849index b351f49..d9c4cb3 100755 850--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 851+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 852@@ -186,9 +186,9 @@ RetCode FBInit() 853 if (g_fbFd) 854 return RC_OK; 855 856- g_fbFd = open("/dev/fb0", O_RDWR); 857+ g_fbFd = open("/dev/graphics/fb0", O_RDWR); 858 if (g_fbFd < 0) { 859- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0"); 860+ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0"); 861 return RC_ERROR; 862 } 863 864diff --git a/camera/hal/test/v4l2/src/test_display.cpp b/camera/hal/test/v4l2/src/test_display.cpp 865index db908e7..7025deb 100644 866--- a/camera/hal/test/v4l2/src/test_display.cpp 867+++ b/camera/hal/test/v4l2/src/test_display.cpp 868@@ -114,9 +114,9 @@ void TestDisplay::FBLog() 869 870 RetCode TestDisplay::FBInit() 871 { 872- fbFd_ = open("/dev/fb0", O_RDWR); 873+ fbFd_ = open("/dev/graphics/fb0", O_RDWR); 874 if (fbFd_ < 0) { 875- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0"); 876+ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0"); 877 return RC_ERROR; 878 } 879 880@@ -439,4 +439,4 @@ void TestDisplay::StopStream(std::vector<int>& captureIds, std::vector<int>& str 881 std::cout << "==========[test log]check Capture: ReleaseStreams fail, rc = " << rc << std::endl; 882 } 883 } 884-} 885\ No newline at end of file 886+} 887diff --git a/display/hal/default/display_layer.c b/display/hal/default/display_layer.c 888index ee7a825..e12a653 100644 889--- a/display/hal/default/display_layer.c 890+++ b/display/hal/default/display_layer.c 891@@ -24,7 +24,7 @@ 892 893 #define DEV_ID 0 894 #define LAYER_ID 0 895-#define FB_PATH "/dev/fb0" 896+#define FB_PATH "/dev/graphics/fb0" 897 #define DISP_WIDTH 800 898 #define DISP_HEIGHT 480 899 #define BITS_PER_PIXEL 32 900``` 901 902修改后重新编译v4l2_main,编译命令:./build.sh --product-name rk3399 --ccache --build-target v4l2_main 903 904编译成功后可执行程序路径:./out/rk3399/hdf/hdf/v4l2_main 905 906将新编译的v4l2_main推送到开发板测的system/bin路径下: 907 908``` 909hdc shell "mount -o rw,remount /" 910hdc file send D:\cyyanl\work\RockChip\bin\v4l2_main /system/bin 911``` 912 913继续执行v4l2_main结果后无framebuffer报错,说明该问题已解决。(另一种思路:分析fb0为何在/dev/graphics/fb0而不是常规的/dev/fb0,然后修改为/dev/fb0。后面有时间再调试该思路) 914 915新报错:ERROR:error: ioctl VIDIOC_QUERYBUF failed. 916 917``` 918Options: 919-h | --help Print this message 920-p | --preview start preview on platform sensor 921-c | --capture capture one picture 922-w | --set WB Set white balance Cloudy 923-e | --Set AE Set exposure time 924-v | --video capture Viedeo of 10s 925-u | --uvc start preview on uvc preview 926-a | --Set ATE Set Auto exposure 927-q | --quit stop preview and quit this app 928INFO:please input command(input -q exit this app) 929u 930INFO:the fixed information is as follow: 931INFO:id= 932INFO:sem_start=0 933INFO:smem_len=2457600 934... 935INFO:V4L2AllocBuffer 936INFO:V4L2AllocBuffer:memoryType_ = 2 937INFO:V4L2AllocBuffer:V4L2_MEMORY_USERPTR = 2 938INFO:V4L2AllocBuffer:VIDIOC_QUERYBUF = 3226490377 939ERROR:error: ioctl VIDIOC_QUERYBUF failed. 940ERROR:error: Creatbuffer: V4L2AllocBuffer error 941ERROR:main test:V4L2PreviewThread CreatBuffer fail i = 0 942``` 943 944原因分析:ioctl(fd, VIDIOC_QUERYBUF, &buf)失败。回过头再看"调试linux L4V2 uvc驱动章节->设置格式申请缓冲区队列"中的报错也是 945 946ioctl(fd, VIDIOC_REQBUFS, &reqbuffer)。由此分析出Open Harmony OS上的ioctl VIDIOC_REQBUFS都会报错。再看两次失败的差异点: 947 948内存映射方式不同: V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR 949 950从OpenHarmony的issuse得知暂不支持V4L2_MEMORY_MMAP内存映射,映射方式就分析到这里,接下来还是用v4l2_main的V4L2_MEMORY_USERPTR进行调试分析。 951 952参考:[V4L2设备增加MMAP申请内存的方式](https://gitee.com/openharmony/drivers_peripheral/issues/I4EFWP)和下图 953 954![](figures/isoftstone/yangfan-ioctl.png) 955 956接着再分析ioctl(fd, VIDIOC_QUERYBUF, &buf)失败,查看VIDIOC_QUERYBUF的定义:videodev2.h 957 958```c 959#define VIDIOC_QUERYBUF_IOWR('V', 9, struct v4l2_buffer) 960``` 961 962此处插入ioctl的定义:int ioctl(int fd, int cmd, …); VIDIOC_QUERYBUF作为cmd的入参,是int类型。也就是一个数字命令码,该命令码通过ioctl发送给内核后,会有与之对应的函数操作,故用户态下发的命令码应和内核接受的命令码一致。下面验证命令码一致性。 963 964videodev2.h中一共有77个和内核交互的命令码,把内核态和用户态的都打印出来做对比: 965 966用户态加打印:drivers/peripheral 967 968```diff 969diff --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 970index d7dd15f..f7254b4 100644 971--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp 972+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp 973@@ -162,37 +162,119 @@ RetCode HosV4L2Buffers::V4L2DequeueBuffer(int fd) 974 return RC_OK; 975 } 976 977+static void cyyanl_printf_cmd(void) 978+{ 979+#if 1 980+ CAMERA_LOGD("*************************************************************************************"); 981+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = 0x%x\n", VIDIOC_QUERYCAP ); 982+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = 0x%x\n", VIDIOC_ENUM_FMT ); 983+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = 0x%x\n", VIDIOC_G_FMT ); 984+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = 0x%x\n", VIDIOC_S_FMT ); 985+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = 0x%x\n", VIDIOC_REQBUFS ); 986+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = 0x%x\n", VIDIOC_QUERYBUF ); 987+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = 0x%x\n", VIDIOC_G_FBUF ); 988+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = 0x%x\n", VIDIOC_S_FBUF ); 989+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = 0x%x\n", VIDIOC_OVERLAY ); 990+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = 0x%x\n", VIDIOC_QBUF ); 991+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = 0x%x\n", VIDIOC_EXPBUF ); 992+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = 0x%x\n", VIDIOC_DQBUF ); 993+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = 0x%x\n", VIDIOC_STREAMON ); 994+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = 0x%x\n", VIDIOC_STREAMOFF ); 995+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = 0x%x\n", VIDIOC_G_PARM ); 996+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = 0x%x\n", VIDIOC_S_PARM ); 997+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = 0x%x\n", VIDIOC_G_STD ); 998+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = 0x%x\n", VIDIOC_S_STD ); 999+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = 0x%x\n", VIDIOC_ENUMSTD ); 1000+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = 0x%x\n", VIDIOC_ENUMINPUT ); 1001+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = 0x%x\n", VIDIOC_G_CTRL ); 1002+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = 0x%x\n", VIDIOC_S_CTRL ); 1003+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = 0x%x\n", VIDIOC_G_TUNER ); 1004+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = 0x%x\n", VIDIOC_S_TUNER ); 1005+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = 0x%x\n", VIDIOC_G_AUDIO ); 1006+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = 0x%x\n", VIDIOC_S_AUDIO ); 1007+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = 0x%x\n", VIDIOC_QUERYCTRL ); 1008+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = 0x%x\n", VIDIOC_QUERYMENU ); 1009+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = 0x%x\n", VIDIOC_G_INPUT ); 1010+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = 0x%x\n", VIDIOC_S_INPUT ); 1011+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = 0x%x\n", VIDIOC_G_EDID ); 1012+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = 0x%x\n", VIDIOC_S_EDID ); 1013+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = 0x%x\n", VIDIOC_G_OUTPUT ); 1014+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = 0x%x\n", VIDIOC_S_OUTPUT ); 1015+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = 0x%x\n", VIDIOC_ENUMOUTPUT ); 1016+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = 0x%x\n", VIDIOC_G_AUDOUT ); 1017+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = 0x%x\n", VIDIOC_S_AUDOUT ); 1018+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = 0x%x\n", VIDIOC_G_MODULATOR ); 1019+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = 0x%x\n", VIDIOC_S_MODULATOR ); 1020+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = 0x%x\n", VIDIOC_G_FREQUENCY ); 1021+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = 0x%x\n", VIDIOC_S_FREQUENCY ); 1022+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = 0x%x\n", VIDIOC_CROPCAP ); 1023+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = 0x%x\n", VIDIOC_G_CROP ); 1024+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = 0x%x\n", VIDIOC_S_CROP ); 1025+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = 0x%x\n", VIDIOC_G_JPEGCOMP ); 1026+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = 0x%x\n", VIDIOC_S_JPEGCOMP ); 1027+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = 0x%x\n", VIDIOC_QUERYSTD ); 1028+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = 0x%x\n", VIDIOC_TRY_FMT ); 1029+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = 0x%x\n", VIDIOC_ENUMAUDIO ); 1030+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = 0x%x\n", VIDIOC_ENUMAUDOUT ); 1031+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = 0x%x\n", VIDIOC_G_PRIORITY ); 1032+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = 0x%x\n", VIDIOC_S_PRIORITY ); 1033+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = 0x%x\n", VIDIOC_G_SLICED_VBI_CAP ); 1034+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = 0x%x\n", VIDIOC_LOG_STATUS ); 1035+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = 0x%x\n", VIDIOC_G_EXT_CTRLS ); 1036+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = 0x%x\n", VIDIOC_S_EXT_CTRLS ); 1037+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = 0x%x\n", VIDIOC_TRY_EXT_CTRLS ); 1038+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = 0x%x\n", VIDIOC_ENUM_FRAMESIZES ); 1039+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = 0x%x\n", VIDIOC_ENUM_FRAMEINTERVALS); 1040+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = 0x%x\n", VIDIOC_G_ENC_INDEX ); 1041+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = 0x%x\n", VIDIOC_ENCODER_CMD ); 1042+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = 0x%x\n", VIDIOC_TRY_ENCODER_CMD ); 1043+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = 0x%x\n", VIDIOC_S_HW_FREQ_SEEK ); 1044+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = 0x%x\n", VIDIOC_CREATE_BUFS ); 1045+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = 0x%x\n", VIDIOC_PREPARE_BUF ); 1046+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = 0x%x\n", VIDIOC_G_SELECTION ); 1047+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = 0x%x\n", VIDIOC_S_SELECTION ); 1048+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = 0x%x\n", VIDIOC_DECODER_CMD ); 1049+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = 0x%x\n", VIDIOC_TRY_DECODER_CMD ); 1050+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = 0x%x\n", VIDIOC_ENUM_DV_TIMINGS ); 1051+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = 0x%x\n", VIDIOC_QUERY_DV_TIMINGS ); 1052+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = 0x%x\n", VIDIOC_DV_TIMINGS_CAP ); 1053+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = 0x%x\n", VIDIOC_ENUM_FREQ_BANDS ); 1054+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = 0x%x\n", VIDIOC_DBG_G_CHIP_INFO ); 1055+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = 0x%x\n", VIDIOC_QUERY_EXT_CTRL ); 1056+ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = 0x%x\n", BASE_VIDIOC_PRIVATE ); 1057+ CAMERA_LOGD("*************************************************************************************"); 1058+#endif 1059+} 1060+ 1061 RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr<FrameSpec>& frameSpec) 1062 { 1063 struct v4l2_buffer buf = {}; 1064 struct v4l2_plane planes[1] = {}; 1065- CAMERA_LOGD("V4L2AllocBuffer\n"); 1066+ CAMERA_LOGD("V4L2AllocBuffer enter\n"); 1067 1068+ cyyanl_printf_cmd(); 1069 if (frameSpec == nullptr) { 1070 CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n"); 1071 return RC_ERROR; 1072 } 1073- 1074 switch (memoryType_) { 1075 case V4L2_MEMORY_MMAP: 1076 // to do something 1077 break; 1078 case V4L2_MEMORY_USERPTR: 1079+ CAMERA_LOGD("V4L2AllocBuffer:V4L2_MEMORY_USERPTR = %d\n", V4L2_MEMORY_USERPTR); 1080 buf.type = bufferType_; 1081 buf.memory = memoryType_; 1082 buf.index = (uint32_t)frameSpec->buffer_->GetIndex(); 1083- 1084 if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { 1085+ CAMERA_LOGD("V4L2AllocBuffer:V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = %d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 1086 buf.m.planes = planes; 1087 buf.length = 1; 1088 } 1089``` 1090 1091内核态打印:kernel/linux/linux-4.19/ 1092 1093```diff 1094diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c 1095index 0842a47c6..8aa60407f 100644 1096--- a/drivers/media/usb/uvc/uvc_driver.c 1097+++ b/drivers/media/usb/uvc/uvc_driver.c 1098@@ -2902,10 +2902,93 @@ struct uvc_driver uvc_driver = { 1099 }, 1100 }; 1101 1102+static void cyyanl_printk_cmd(void) 1103+{ 1104+ printk("*************************************************************************************"); 1105+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = %ld\n", VIDIOC_QUERYCAP ); 1106+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = %ld\n", VIDIOC_ENUM_FMT ); 1107+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = %ld\n", VIDIOC_G_FMT ); 1108+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = %ld\n", VIDIOC_S_FMT ); 1109+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = %ld\n", VIDIOC_REQBUFS ); 1110+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = %ld\n", VIDIOC_QUERYBUF ); 1111+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = %ld\n", VIDIOC_G_FBUF ); 1112+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = %ld\n", VIDIOC_S_FBUF ); 1113+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = %ld\n", VIDIOC_OVERLAY ); 1114+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = %ld\n", VIDIOC_QBUF ); 1115+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = %ld\n", VIDIOC_EXPBUF ); 1116+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = %ld\n", VIDIOC_DQBUF ); 1117+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = %ld\n", VIDIOC_STREAMON ); 1118+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = %ld\n", VIDIOC_STREAMOFF ); 1119+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = %ld\n", VIDIOC_G_PARM ); 1120+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = %ld\n", VIDIOC_S_PARM ); 1121+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = %ld\n", VIDIOC_G_STD ); 1122+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = %ld\n", VIDIOC_S_STD ); 1123+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = %ld\n", VIDIOC_ENUMSTD ); 1124+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = %ld\n", VIDIOC_ENUMINPUT ); 1125+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = %ld\n", VIDIOC_G_CTRL ); 1126+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = %ld\n", VIDIOC_S_CTRL ); 1127+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = %ld\n", VIDIOC_G_TUNER ); 1128+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = %ld\n", VIDIOC_S_TUNER ); 1129+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = %ld\n", VIDIOC_G_AUDIO ); 1130+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = %ld\n", VIDIOC_S_AUDIO ); 1131+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = %ld\n", VIDIOC_QUERYCTRL ); 1132+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = %ld\n", VIDIOC_QUERYMENU ); 1133+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = %ld\n", VIDIOC_G_INPUT ); 1134+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = %ld\n", VIDIOC_S_INPUT ); 1135+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = %ld\n", VIDIOC_G_EDID ); 1136+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = %ld\n", VIDIOC_S_EDID ); 1137+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = %ld\n", VIDIOC_G_OUTPUT ); 1138+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = %ld\n", VIDIOC_S_OUTPUT ); 1139+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = %ld\n", VIDIOC_ENUMOUTPUT ); 1140+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = %ld\n", VIDIOC_G_AUDOUT ); 1141+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = %ld\n", VIDIOC_S_AUDOUT ); 1142+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = %ld\n", VIDIOC_G_MODULATOR ); 1143+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = %ld\n", VIDIOC_S_MODULATOR ); 1144+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = %ld\n", VIDIOC_G_FREQUENCY ); 1145+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = %ld\n", VIDIOC_S_FREQUENCY ); 1146+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = %ld\n", VIDIOC_CROPCAP ); 1147+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = %ld\n", VIDIOC_G_CROP ); 1148+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = %ld\n", VIDIOC_S_CROP ); 1149+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = %ld\n", VIDIOC_G_JPEGCOMP ); 1150+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = %ld\n", VIDIOC_S_JPEGCOMP ); 1151+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = %ld\n", VIDIOC_QUERYSTD ); 1152+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = %ld\n", VIDIOC_TRY_FMT ); 1153+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = %ld\n", VIDIOC_ENUMAUDIO ); 1154+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = %ld\n", VIDIOC_ENUMAUDOUT ); 1155+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = %ld\n", VIDIOC_G_PRIORITY ); 1156+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = %ld\n", VIDIOC_S_PRIORITY ); 1157+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = %ld\n", VIDIOC_G_SLICED_VBI_CAP ); 1158+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = %ld\n", VIDIOC_LOG_STATUS ); 1159+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = %ld\n", VIDIOC_G_EXT_CTRLS ); 1160+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = %ld\n", VIDIOC_S_EXT_CTRLS ); 1161+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = %ld\n", VIDIOC_TRY_EXT_CTRLS ); 1162+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = %ld\n", VIDIOC_ENUM_FRAMESIZES ); 1163+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = %ld\n", VIDIOC_ENUM_FRAMEINTERVALS); 1164+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = %ld\n", VIDIOC_G_ENC_INDEX ); 1165+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = %ld\n", VIDIOC_ENCODER_CMD ); 1166+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = %ld\n", VIDIOC_TRY_ENCODER_CMD ); 1167+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = %ld\n", VIDIOC_S_HW_FREQ_SEEK ); 1168+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = %ld\n", VIDIOC_CREATE_BUFS ); 1169+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = %ld\n", VIDIOC_PREPARE_BUF ); 1170+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = %ld\n", VIDIOC_G_SELECTION ); 1171+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = %ld\n", VIDIOC_S_SELECTION ); 1172+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = %ld\n", VIDIOC_DECODER_CMD ); 1173+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = %ld\n", VIDIOC_TRY_DECODER_CMD ); 1174+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = %ld\n", VIDIOC_ENUM_DV_TIMINGS ); 1175+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = %ld\n", VIDIOC_QUERY_DV_TIMINGS ); 1176+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = %ld\n", VIDIOC_DV_TIMINGS_CAP ); 1177+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = %ld\n", VIDIOC_ENUM_FREQ_BANDS ); 1178+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = %ld\n", VIDIOC_DBG_G_CHIP_INFO ); 1179+ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = %ld\n", VIDIOC_QUERY_EXT_CTRL ); 1180+ printk("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = %ld\n", BASE_VIDIOC_PRIVATE ); 1181+ printk("*************************************************************************************"); 1182+} 1183+ 1184 static int __init uvc_init(void) 1185 { 1186 int ret; 1187- 1188+ printk("cyyanl enter uvc_init\n"); 1189+ cyyanl_printk_cmd(); 1190 uvc_debugfs_init(); 1191 1192 ret = usb_register(&uvc_driver.driver); 1193``` 1194 1195打印结果对比:VIDIOC_QUERYBUF用户态为0xc0505609,内核态为0xc0585609 还有其他几处命令码也不同。 1196 1197![](figures/isoftstone/yangfan-print-01.png) 1198 1199分析命令码不一致问题,先找出VIDIOC_QUERYBUF用户态和内核态实际编译时所定义的地方差异: 1200 1201用户态:kernel/linux/patches/linux-5.10/prebuilts/usr/include/linux/videodev2.h +1358 1202 1203内核态:kernel/linux/linux-4.19/include/uapi/linux/videodev2.h +2361 1204 1205此时怀疑VIDIOC_QUERYBUF不一致是头文件不同所导致(目前没做修改验证同改为linux-4.19是否可行)。对现有问题求助客户,并在OpenHarmony上寻找有无相似问题。 1206 1207找到了相似的issuse:https://gitee.com/openharmony/drivers_peripheral/issues/I4NI4M?from=project-issue 1208 1209![](figures/isoftstone/yangfan-picture-02.png) 1210 1211接下来,调试思路分为2路: 1212 12131. 同意用户态和内核态的VIDIOC_QUERYBUF 1214 1215 方案:把用户态的0xc0505609改为内核态的0xc0585609 1216 1217 修改点:drivers/peripheral 1218 1219 ```diff 1220 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 1221 index d7dd15f..f7254b4 100644 1222 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp 1223 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp 1224 buf.m.planes = planes; 1225 buf.length = 1; 1226 } 1227 - CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index); 1228 - 1229 - if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { 1230 - CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno)); 1231 + if (ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) < 0) { 1232 + CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed.\n"); 1233 return RC_ERROR; 1234 } 1235 ``` 1236 1237 再次运行v4l2_main结果:Signal 4报错 1238 1239 原因分析:此时ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) 已经成功了。Signal 4分析可能又是cmd命令码的问题。 1240 1241 此路虽然解决了当前问题,但会有新问题Signal 4,故先暂停。 1242 1243 ``` 1244 INFO:main test:allocating display buffer memory 1245 INFO:main test:do_mmap: pmem mmap fd 5 ptr 0xf7508000 len 2457600 1246 INFO:V4L2OpenDevice /dev/video10 1247 INFO:V4L2ReqBuffers buffCont 4 1248 INFO:Creatbuffer frameSpec->buffer index == 0 1249 INFO:V4L2AllocBuffer 1250 Signal 4 1251 ``` 1252 12532. 分析命令码不一致的根因,并做修改 1254 1255 根因分析:结构体的大小根据32位与64位编译会产生差异,从而影响VIDIOC_QUERYBUF的值。 1256 1257 修改点:修改用户态编译v4l2_buffer结构体的timestamp定义,并将用户态编译的头文件#include <linux/videodev2.h>替换成 1258 1259 修改过的videodev2.h,即#include ”videodev2.h“。(实际操作为把修改后的videodev2.h拷贝到v4l2_main编译目录中) 1260 1261 kernel/linux/linux-4.19/ 1262 1263 ```diff 1264 diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h 1265 index ba548d7f0..b0fb48f65 100644 1266 --- a/include/uapi/linux/videodev2.h 1267 +++ b/include/uapi/linux/videodev2.h 1268 @@ -958,13 +958,20 @@ struct v4l2_plane { 1269 * Contains data exchanged by application and driver using one of the Streaming 1270 * I/O methods. 1271 */ 1272 + 1273 +struct timeval_user { 1274 + long tv_sec; 1275 + long tv_usec; 1276 +}; 1277 + 1278 struct v4l2_buffer { 1279 __u32 index; 1280 __u32 type; 1281 __u32 bytesused; 1282 __u32 flags; 1283 __u32 field; 1284 - struct timeval timestamp; 1285 + //struct timeval timestamp; 1286 + struct timeval_user timestamp; 1287 struct v4l2_timecode timecode; 1288 __u32 sequence; 1289 1290 ``` 1291 1292 drivers/peripheral 1293 1294 ```diff 1295 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 1296 index 00ddea7..962ebc3 100755 1297 --- a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h 1298 +++ b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h 1299 @@ -15,7 +15,8 @@ 1300 1301 #ifndef HOS_CAMERA_PROJET_HARDWARE_H 1302 #define HOS_CAMERA_PROJET_HARDWARE_H 1303 -#include <linux/videodev2.h> 1304 +//#include <linux/videodev2.h> 1305 +#include "videodev2.h" 1306 1307 namespace OHOS::Camera { 1308 #define PREVIEW_PIXEL_FORMAT V4L2_PIX_FMT_YUV420 1309 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 1310 index 6f45882..a8d6819 100644 1311 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h 1312 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h 1313 @@ -19,7 +19,8 @@ 1314 #include <mutex> 1315 #include <map> 1316 #include <cstring> 1317 -#include <linux/videodev2.h> 1318 +//#include <linux/videodev2.h> 1319 +#include "videodev2.h" 1320 #include <sys/ioctl.h> 1321 #include "v4l2_common.h" 1322 #if defined(V4L2_UTEST) || defined (V4L2_MAIN_TEST) 1323 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 1324 index 5b93f36..05191a7 100644 1325 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h 1326 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h 1327 @@ -16,7 +16,8 @@ 1328 #ifndef HOS_CAMERA_V4L2_CONTROL_H 1329 #define HOS_CAMERA_V4L2_CONTROL_H 1330 1331 -#include <linux/videodev2.h> 1332 +//#include <linux/videodev2.h> 1333 +#include "videodev2.h" 1334 #include <errno.h> 1335 #include <sys/ioctl.h> 1336 #include "v4l2_common.h" 1337 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 1338 index 10dc9b4..e3b3056 100644 1339 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h 1340 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h 1341 @@ -19,7 +19,8 @@ 1342 #include <mutex> 1343 #include <thread> 1344 #include <vector> 1345 -#include <linux/videodev2.h> 1346 +//#include <linux/videodev2.h> 1347 +#include "videodev2.h" 1348 #include <sys/epoll.h> 1349 #include <sys/ioctl.h> 1350 #include <sys/types.h> 1351 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 1352 index de892e9..44bb1b4 100644 1353 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h 1354 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h 1355 @@ -19,7 +19,8 @@ 1356 #include <vector> 1357 #include <cstring> 1358 #include <fcntl.h> 1359 -#include <linux/videodev2.h> 1360 +//#include <linux/videodev2.h> 1361 +#include "videodev2.h" 1362 #include <sys/ioctl.h> 1363 #include <sys/stat.h> 1364 #include <unistd.h> 1365 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 1366 index 1a62f37..96c70aa 100644 1367 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h 1368 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h 1369 @@ -18,7 +18,8 @@ 1370 #include <thread> 1371 #include <fcntl.h> 1372 #include <linux/netlink.h> 1373 -#include <linux/videodev2.h> 1374 +//#include <linux/videodev2.h> 1375 +#include "videodev2.h" 1376 #include <sys/ioctl.h> 1377 #include <sys/select.h> 1378 #include <linux/netlink.h>zz 1379 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 1380 index b351f49..5483b85 100755 1381 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 1382 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 1383 @@ -22,7 +22,8 @@ 1384 #include <sys/mman.h> 1385 #include <sys/time.h> 1386 #include <linux/fb.h> 1387 -#include <linux/videodev2.h> 1388 +//#include <linux/videodev2.h> 1389 +#include "videodev2.h" 1390 #include "securec.h" 1391 #include "v4l2_uvc.h" 1392 #include "v4l2_dev.h" 1393 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 1394 index 778cf05..96618be 100644 1395 --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp 1396 +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp 1397 @@ -14,7 +14,8 @@ 1398 */ 1399 1400 #include <cstring> 1401 -#include <linux/videodev2.h> 1402 +//#include <linux/videodev2.h> 1403 +#include "videodev2.h" 1404 #include <sys/ioctl.h> 1405 1406 #include "v4l2_stream.h" 1407 diff --git a/camera/hal/test/v4l2/include/test_display.h b/camera/hal/test/v4l2/include/test_display.h 1408 index d437e26..8e5205e 100644 1409 --- a/camera/hal/test/v4l2/include/test_display.h 1410 +++ b/camera/hal/test/v4l2/include/test_display.h 1411 @@ -44,7 +44,8 @@ 1412 #include <errno.h> 1413 #include <getopt.h> 1414 #include <linux/fb.h> 1415 -#include <linux/videodev2.h> 1416 +//#include <linux/videodev2.h> 1417 +#include "videodev2.h" 1418 #include <mutex> 1419 #include <pthread.h> 1420 #include <stdlib.h> 1421 @@ -138,4 +139,4 @@ public: 1422 void StartCapture(int streamId, int captureId, bool shutterCallback, bool isStreaming); 1423 float calTime(struct timeval start, struct timeval end); 1424 }; 1425 -#endif 1426 \ No newline at end of file 1427 +#endif 1428 ``` 1429 1430 再次编译v4l2_main后执行,log无报错。HDMI屏上就显示预览画面。![](figures/isoftstone/yangfan-picture-hdmi.png) 1431 1432 1433 1434回过头来再看整个调试流程,发现该摄像头支持YUYV格式和MJPEG格式,查看v4l2_main中默认预览用的是YUYV,改为MJPEG再次调试。 1435 1436修改点: 1437 1438```diff 1439diff --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 1440index b351f49..5483b85 100755 1441--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 1442+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp 1443@@ -394,7 +395,9 @@ void V4L2SetDeviceFormat(DeviceFormat& format, const std::string devname) 1444 1445 if (devname == "uvcvideo" || devname == "uvcvideo1") { 1446 if (g_isPreviewOnUvc) { 1447- format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; 1448+ CAMERA_LOGD("cyyanl enter V4L2SetDeviceFormat : g_isPreviewOnUvc\n"); 1449+ //format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; 1450+ format.fmtdesc.pixelformat = V4L2_PIX_FMT_MJPEG; 1451 format.fmtdesc.width = width; 1452 format.fmtdesc.height = height; 1453 } 1454``` 1455 1456v4l2_main执行结果:该显示异常,目前先不分析MJPEG格式。 1457 1458![](figures/isoftstone/yangfan-picture-v4l2_main.png) 1459 1460 1461 1462到此为止,v4l2_main运行正常,可以预览(预览画面色彩异常与屏幕显示格式有关,暂不做修改)。接下来调试拍照和录像。 1463 1464执行v4l2_main后,先输入u进行uvc预览,再运行c进行拍照,再运行v进行录像,照片和录像文件生成到当前执行的路径下: 1465 1466``` 1467# ls -l *.jpeg 1468-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC0.jpeg 1469-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC1.jpeg 1470-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC2.jpeg 1471-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC3.jpeg 1472# 1473# ls -l *.h264 1474-rwxrw-rw- 1 root 29034400 85401600 2013-01-18 15:20 uvc.h264 1475``` 1476 1477将如上文件导出到电脑端查看: 1478 1479照片uvc0.jpeg 1480 1481![](figures/isoftstone/yangfan-picture-uvc0.png) 1482 1483录像uvc.h264在手机端可查看,播放正常。 1484 1485### 附录 1486 1487```c 1488/** 1489 * struct v4l2_buffer - video buffer info 1490 * @index: id number of the buffer 1491 * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for 1492 * multiplanar buffers); 1493 * @bytesused: number of bytes occupied by data in the buffer (payload); 1494 * unused (set to 0) for multiplanar buffers 1495 * @flags: buffer informational flags 1496 * @field: enum v4l2_field; field order of the image in the buffer 1497 * @timestamp: frame timestamp 1498 * @timecode: frame timecode 1499 * @sequence: sequence count of this frame 1500 * @memory: enum v4l2_memory; the method, in which the actual video data is 1501 * passed 1502 * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; 1503 * offset from the start of the device memory for this plane, 1504 * (or a "cookie" that should be passed to mmap() as offset) 1505 * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; 1506 * a userspace pointer pointing to this buffer 1507 * @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF; 1508 * a userspace file descriptor associated with this buffer 1509 * @planes: for multiplanar buffers; userspace pointer to the array of plane 1510 * info structs for this buffer 1511 * @length: size in bytes of the buffer (NOT its payload) for single-plane 1512 * buffers (when type != *_MPLANE); number of elements in the 1513 * planes array for multi-plane buffers 1514 * 1515 * Contains data exchanged by application and driver using one of the Streaming 1516 * I/O methods. 1517 */ 1518 1519struct v4l2_buffer { 1520 __u32 index; 1521 __u32 type; 1522 __u32 bytesused; 1523 __u32 flags; 1524 __u32 field; 1525 struct timeval timestamp; 1526 struct v4l2_timecode timecode; 1527 __u32 sequence; 1528 1529 /* memory location */ 1530 __u32 memory; 1531 union { 1532 __u32 offset; 1533 unsigned long userptr; 1534 struct v4l2_plane *planes; 1535 __s32 fd; 1536 } m; 1537 __u32 length; 1538 __u32 reserved2; 1539 __u32 reserved; 1540} 1541``` 1542 1543## TP 1544 1545### TP驱动模型 1546 1547主要包含Input模块HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作input设备的驱动能力接口,HDI接口主要包括如下三大类: 1548 1549- InputManager:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等; 1550- InputReporter:负责输入事件的上报,包括注册、注销数据上报回调函数等; 1551- InputController:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。 1552 1553**图 1** INPUT模块HDI接口层框架图 1554 1555![dayu200-tp-01.png](figures/dayu200/dayu200-tp-01.png) 1556 1557相关目录下源代码目录结构如下所示 1558 1559``` 1560/drivers/peripheral/input 1561├── hal # input模块的hal层代码 1562│ └── include # input模块hal层内部的头文件 1563│ └── src # input模块hal层代码的具体实现 1564├── interfaces # input模块对上层服务提供的驱动能力接口 1565│ └── include # input模块对外提供的接口定义 1566├── test # input模块的测试代码 1567│ └── unittest # input模块的单元测试代码 1568``` 1569 1570详细请参考input子系统[README](https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md) 1571 1572### TP HDF驱动适配 1573 1574#### 配置适配 1575 1576关闭linux tp驱动开关,在<rockchip_linux_defconfig>文件中关闭TOUCHSCREEN_GT9XX,具体操作如下,将**CONFIG_TOUCHSCREEN_GT9XX=y**修改为 1577```makefile 1578# CONFIG_TOUCHSCREEN_GT9XX is not set 1579``` 1580#### 驱动适配 1581 15821. 在**vendor/rockchip/rk3399/hdf_config/khdf/device_info/device_info.hcs**修改或增加如下内容 1583 1584 ```json 1585 device_touch_chip :: device { 1586 device0 :: deviceNode { 1587 policy = 0; 1588 priority = 130; 1589 preload = 0; 1590 permission = 0660; 1591 moduleName = "HDF_TOUCH_GT911"; 1592 serviceName = "hdf_touch_gt911_service"; 1593 deviceMatchAttr = "zsj_gt911_5p5"; 1594 } 1595 } 1596 ``` 1597 1598 1599 16002. 在**vendor/rockchip/rk3399/hdf_config/khdf/input/input_config.hcs**修改或增加如下内容 1601 1602 ```json 1603 busConfig { 1604 // 0:i2c 1:spi 1605 busType = 0; // I2C通信方式 1606 busNum = 7; // cpu对应touch芯片的I2C编号 1607 clkGpio = 72; // I2C clk对应的IO引脚编号 1608 dataGpio = 71; // I2C data对应的IO引脚标号 1609 i2cClkIomux = [0xFF77E004, 0x2]; // I2C [clk引脚对应的复用寄存器,配置为I2C7_clk功能] 1610 i2cDataIomux = [0xFF77E000, 0x8000]; // I2C [data引脚对应的复用寄存器,配置为I2C7_DATA功能] 1611 } 1612 1613 pinConfig { 1614 rstGpio = 150; // touch芯片rst对应的IO引脚编号 1615 intGpio = 52; // touch芯片int对应的IO引脚编号 1616 rstRegCfg = [0xFF77E028, 0x00000000]; // rst引脚对应的复用功能寄存器,配置为普通io口 1617 intRegCfg = [0xFF770018, 0x00000000]; // int引脚对应的复用功能寄存器,配置为普通io口 1618 } 1619 ``` 1620#### FAQ问题 1621 1622##### 解决触屏横竖反转问题 1623 1624修改**drivers/framework/model/input/driver/touchscreen/touch_gt911.c**中的**ParsePointData**函数,x和y对调即可 1625 1626##### InputPinMuxCfg函数修改 1627 1628```c 1629static int32_t InputPinMuxCfg(uint32_t regAddr, int32_t regSize, uint32_t regValue) 1630{ 1631 uint8_t *base = NULL; 1632 uint32_t data = 0; 1633 if (regAddr == 0) { 1634 HDF_LOGE("%s: regAddr invalid", __func__); 1635 return HDF_FAILURE; 1636 } 1637 1638 HDF_LOGE("regAddr = 0x%x, regSize = 0x%x", regAddr, regSize); 1639 base = OsalIoRemap(regAddr, regSize); 1640 if (base == NULL) { 1641 HDF_LOGE("%s: ioremap failed", __func__); 1642 return HDF_FAILURE; 1643 } 1644 // 写入寄存器前先读出数据,保存原有的配置,方式修改原有的其他引脚功能配置。 1645 data = OSAL_READL(base); 1646 data |= regValue; 1647 OSAL_WRITEL(data, base); 1648 OsalIoUnmap((void *)base); 1649 return HDF_SUCCESS; 1650} 1651``` 1652 1653##### RK3399引脚GPIOn_KX 编号号确认方法 1654 1655```shell 1656num = 32n+(K-A)8+X 1657# GPIO1_B2 = 1 * 32 + (B-A)*8 + 2 = 32 + 8 + 1 = 42 1658``` 1659 1660| 功能 | IO名称 | IO编号 | 1661| ------ | -------- | ------ | 1662| TP_INT | GPIO1_C4 | 52 | 1663| TP_RST | GPIO4_C6 | 150 | 1664| TP_SDA | GPIO2_A7 | 71 | 1665| TP_SCL | GPIO2_B0 | 72 | 1666 1667 1668 1669##### RK3399引脚GPIOn_KX复用功能配置 1670 16711. GPIO复用功能寄存器基地址**FF77_0000** 16722. 找到GPIOn_K复用功能寄存器偏移量 16733. 确定GPIOn_K复用功能寄存器,**基地址+偏移量** 16744. 根据复用功能寄存器介绍,写入对应引脚的复用功能 1675 1676| 功能 | IO名称 | 复用寄存器地址 | 控制位 | 对应数值 | 1677| ------ | -------- | -------------- | ------ | ------------------- | 1678| TP_INT | GPIO1_C4 | 0xFF770018 | 9:8 | 2'b00:GPIO | 1679| TP_RST | GPIO4_C6 | 0xFF77E028 | 13:12 | 2'b00:GPIO | 1680| TP_SDA | GPIO2_A7 | 0xFF77E000 | 15:14 | 2'b10: i2c7nfc_sda | 1681| TP_SCL | GPIO2_B0 | 0xFF77E004 | 1:0 | 2'b10: i2c7nfc_scl | 1682 1683 1684 1685 1686