1# 标准系统方案之瑞芯微RK3566移植案例 2 3本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。 4 5## 产品配置和目录规划 6 7### 产品配置 8 9在产品`//vendor/`目录下创建以kaihong名字命名的文件夹,并在kaihong文件夹下面新建产品命的文件夹khdvk_3566b。 10 11在`//vendor/kaihong/khdvk_3566b`目录下创建config.json文件。该文件用于描述产品所使用的SOC以及所需的子系统。配置如下 12 13``` 14{ 15 "product_name": "khdvk_3566b", 16 "device_company": "kaihong", 17 "device_build_path": "device/board/kaihong/build", 18 "target_cpu": "arm", 19 "type": "standard", 20 "version": "3.0", 21 "board": "khdvk_3566b", 22 "enable_ramdisk": true,//是否支持ramdisk二级启动 23 "build_selinux": true,// 是否支持selinux权限管理 24 "subsystems": [ 25 { 26 "subsystem": "arkui", 27 "components": [ 28 { 29 "component": "ace_engine_standard", 30 "features": [] 31 }, 32 { 33 "component": "napi", 34 "features": [] 35 } 36 ] 37 }, 38 . 39 . 40 . 41 { 42 "subsystem": "thirdparty", 43 "components": [ 44 { 45 "component": "musl", 46 "features": [] 47 } 48 ] 49 } 50 ] 51} 52``` 53 54主要的配置内容包括: 55 561. product_device:配置所使用的SOC。 572. type:配置系统的级别,这里直接standard即可。 583. subsystems:系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。 59 60已定义的子系统可以在`//build/subsystem_config.json`中找到。当然你也可以定制子系统。 61 62这里建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products这个子系统。这个子系统为Hi3516DV300 SOC编译内核,不适合rk3566 63 64### 目录规划 65 66参考https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md,并把芯片适配目录规划为: 67 68``` 69device 70├── board --- 单板厂商目录 71│ └── kaihong --- 单板厂商名字: 72│ └── khdvk_3566b --- 单板名:khdvk_3566b,主要放置开发板相关的驱动业务代码 73└── soc --- SoC厂商目录 74 └── rockchip --- SoC厂商名字:rockchip 75 └── rk3566 --- SoC Series名:rk3566,主要为芯片原厂提供的一些方案,以及闭源库等 76 77 78vendor 79└── kaihong --- 开发产品样例厂商目录 80 └── khdvk_3566b --- 产品名字:产品、hcs以及demo相关 81``` 82 83## 内核启动 84 85### 二级启动 86 87二级启动简单来说就是将之前直接挂载sytem,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init。 88 89Rk3566适配主要是将主线编译出来的ramdisk打包到boot.img中,主要有以下工作: 90 911.使能二级启动 92 93在//vendor/kaihong/khdvk_3566b/config.json中使能enable_ramdisk。 94 95``` 96{ 97 "product_name": "khdvk_3566b", 98 "device_company": "kaihong", 99 "device_build_path": "device/board/kaihong/build", 100 "target_cpu": "arm", 101 "type": "standard", 102 "version": "3.0", 103 "board": "khdvk_3566b", 104 "enable_ramdisk": true,//是否支持ramdisk二级启动 105 "build_selinux": true,// 是否支持selinux权限管理 106``` 107 1082.把主线编译出来的ramdsik.img 打包到boot.img 109 110配置: 111 112由于rk 启动uboot 支持从ramdisk 启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img,因此没有使用主线的its格式,具体配置就是在内核编译脚本make-ohos.sh中增加: 113 114``` 115function make_extlinux_conf() 116{ 117 dtb_path=$1 118 uart=$2 119 image=$3 120 121 echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF} 122 echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF} 123 echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF} 124 if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then 125 echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF} 126 fi 127 cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4" 128 echo " ${cmdline}" >> ${EXTLINUX_CONF} 129} 130``` 131 132### 打包 133 134增加了打包boot镜像的脚本make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容: 135 136``` 137genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img 138``` 139 140调用make-boot.sh的修改可以参考如下pr: 141 142https://gitee.com/openharmony/build/pulls/569/files 143 144### INIT配置 145 146init相关配置请参考[启动子系统的规范要求](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/启动恢复子系统.md)即可 147 148## **音频** 149 150##### khdvk_3566b Audio硬件结构图 151 152![](../figures/khdvk_3566b_audio_01.png) 153 154##### khdvk_3566b平台Audio驱动框架图 155 156![](../figures/khdvk_3566b_audio_02.png) 157 1581. HDI adapter 159 160实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。 161 1622. Audio Interface Lib 163 164配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入。它里面包括Stream_ctrl_common 通用层,主要是为了和上层的audio HDI adapter层进行对接。 165 1663. ADM(Audio Driver Model) 167 168音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配OpenHarmony系统。 169 1704. Audio Control Dispatch 171 172接收lib层的控制指令并将控制指令分发到驱动层。 173 1745. Audio Stream Dispatch 175 176接收lib层的数据并将数据分发到驱动层 177 1786. Card Manager 179 180多声卡管理模块,每个声卡含有Dai、Platform、Codec、Accessory、Dsp、SAPM模块。 181 1827. Platform Drivers 183 184驱动适配层。 185 1868. SAPM(Smart Audio Power Manager) 187 188电源管理模块,对整个ADM电源进行功耗策略优化。 189 190### Audio 驱动开发 191 192这里以khdvk_3566b为例,讲述Audio驱动开发,其涉及到的模块驱动主要有:Codec驱动、platform驱动、Dai驱动。 193相关代码路径如下: 194 195 device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/ 196 device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/ 197 device/board/kaihong/khdvk_3566b/audio_drivers/codec/soc/ 198 199HDF HCS配置路径如下: 200 201 vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/ 202 vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/ 203 204Audio 驱动开发流程: 205 206 step1:配置各个模块的HCS 207 step2:修改各个模块的编译文件 208 step3:配置各个模块的函数操作集 209 step4:进行功能调试 210 211#### Audio驱动开发实例 212 213##### codec驱动开发实例 214 215代码路径: 216 device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/ 217 2181. 将codec注册绑定到HDF框架中,moduleName与device_info.hcs中的moduleName匹配 219 220 struct HdfDriverEntry g_Rk809DriverEntry = { 221 222 .moduleVersion = 1, 223 .moduleName = "CODEC_RK809", 224 .Bind = Rk809DriverBind, 225 .Init = Rk809DriverInit, 226 .Release = RK809DriverRelease, 227 228 }; 229 230 HDF_INIT(g_Rk809DriverEntry); 231 2322. Codec模块需要填充下面三个结构体: 233 234g_codecData:codec设备的操作函数集和私有数据集。 235 236g_codecDaiDeviceOps:codecDai的操作函数集,包括启动传输和参数配置等函数接口。 237 238g_codecDaiData:codec的数字音频接口的操作函数集和私有数据集。 239 240 struct CodecData g_rk809Data = { 241 .Init = Rk809DeviceInit, 242 .Read = RK809CodecReadReg, 243 .Write = Rk809CodecWriteReg, 244 }; 245 246 struct AudioDaiOps g_rk809DaiDeviceOps = { 247 .Startup = Rk809DaiStartup, 248 .HwParams = Rk809DaiHwParams, 249 .Trigger = Rk809NormalTrigger, 250 }; 251 252 struct DaiData g_rk809DaiData = { 253 .DaiInit = Rk809DaiDeviceInit, 254 .ops = &g_rk809DaiDeviceOps, 255 }; 256 2571> CodecData结构体操作函数的实现 258 259 int32_t Rk809DeviceInit(struct AudioCard *audioCard, const struct CodecDevice *device) 260 { 261 ...... 262 //get和set功能注册 263 if (CodecSetCtlFunc(device->devData, RK809GetCtrlOps, RK809SetCtrlOps) != HDF_SUCCESS) { 264 AUDIO_DRIVER_LOG_ERR("AudioCodecSetCtlFunc failed."); 265 return HDF_FAILURE; 266 } 267 //codec默认寄存器的初始化 268 ret = RK809RegDefaultInit(device->devData->regCfgGroup); 269 ...... 270 if (AudioAddControls(audioCard, device->devData->controls, device->devData->numControls) != HDF_SUCCESS) { 271 AUDIO_DRIVER_LOG_ERR("add controls failed."); 272 return HDF_FAILURE; 273 } 274 ...... 275 } 276 /*读寄存器接口*/ 277 int32_t RK809CodecReadReg(const struct CodecDevice *codec, uint32_t reg, uint32_t *val) 278 { 279 ...... 280 if (Rk809DeviceRegRead(reg, val)) { 281 AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg); 282 return HDF_FAILURE; 283 } 284 285 return HDF_SUCCESS; 286 } 287 /*写寄存器接口*/ 288 int32_t Rk809CodecWriteReg(const struct CodecDevice *codec, uint32_t reg, uint32_t value) 289 { 290 if (Rk809DeviceRegWrite(reg, value)) { 291 AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value); 292 return HDF_FAILURE; 293 } 294 295 return HDF_SUCCESS; 296 } 297 2982> g_rk809DaiDeviceOps结构体的具体实现 299 300 /*Rk809DaiStartup为启动时的一些设置*/ 301 int32_t Rk809DaiStartup(const struct AudioCard *card, const struct DaiDevice *device) 302 { 303 ...... 304 ret = RK809WorkStatusEnable(device->devData->regCfgGroup); 305 ...... 306 } 307 /*Rk809DaiHwParams为参数配置,包括采样率、位宽等。*/ 308 int32_t Rk809DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param) 309 { 310 ...... 311 ret = AudioFormatToBitWidth(param->format, &bitWidth); 312 codecDaiParamsVal.frequencyVal = param->rate; 313 codecDaiParamsVal.DataWidthVal = bitWidth; 314 315 ret = RK809DaiParamsUpdate(card->rtd->codecDai->devData->regCfgGroup, codecDaiParamsVal); 316 ...... 317 } 318 /*PCM流控制寄存器相关配置*/ 319 int32_t Rk809NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device) 320 { 321 g_cuurentcmd = cmd; 322 switch (cmd) { 323 case AUDIO_DRV_PCM_IOCTL_RENDER_START: 324 case AUDIO_DRV_PCM_IOCTL_RENDER_RESUME: 325 RK809DeviceRegConfig(rk817_render_start_regmap_config); 326 break; 327 328 case AUDIO_DRV_PCM_IOCTL_RENDER_STOP: 329 case AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE: 330 RK809DeviceRegConfig(rk817_render_stop_regmap_config); 331 break; 332 333 case AUDIO_DRV_PCM_IOCTL_CAPTURE_START: 334 case AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME: 335 RK809DeviceRegConfig(rk817_capture_start_regmap_config); 336 break; 337 338 case AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP: 339 case AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE: 340 RK809DeviceRegConfig(rk817_capture_stop_regmap_config); 341 break; 342 343 default: 344 break; 345 } 346 347 return HDF_SUCCESS; 348 } 349 3503. 完成 bind、init和release函数的实现 351 352HdfDriverEntry结构体的具体填充: 353 354 /*获取codec service,以及注册codec*/ 355 static int32_t Rk809DriverInit(struct HdfDeviceObject *device) 356 { 357 ...... 358 CodecGetConfigInfo(device, &(g_chip->codec)) 359 CodecSetConfigInfo(&(g_chip->codec), &(g_chip->dai) 360 GetServiceName(device) 361 CodecGetDaiName(device, &(g_chip->dai.drvDaiName) 362 OsalMutexInit(&g_rk809Data.mutex); 363 AudioRegisterCodec(device, &(g_chip->codec), &(g_chip->dai) 364 ...... 365 } 366 /*将codec service绑定到HDF*/ 367 static int32_t Rk809DriverBind(struct HdfDeviceObject *device) 368 { 369 struct CodecHost *codecHost; 370 ...... 371 codecHost = (struct CodecHost *)OsalMemCalloc(sizeof(*codecHost)); 372 ...... 373 codecHost->device = device; 374 device->service = &codecHost->service; 375 return HDF_SUCCESS; 376 } 377 /*释放资源*/ 378 static void RK809DriverRelease(struct HdfDeviceObject *device) 379 { 380 struct CodecHost *codecHost; 381 ...... 382 codecHost = (struct CodecHost *)device->service; 383 if (codecHost == NULL) { 384 HDF_LOGE("CodecDriverRelease: codecHost is NULL"); 385 return; 386 } 387 OsalMemFree(codecHost); 388 } 389 3904. 配置codec hcs文件 391 392 1> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs 393 394相关配置如下: 395 396 device_codec :: device { 397 device0 :: deviceNode { 398 policy = 1; 399 priority = 50; 400 preload = 0; 401 permission = 0666; 402 moduleName = "CODEC_RK809"; 403 serviceName = "codec_service_0"; 404 deviceMatchAttr = "hdf_codec_driver"; 405 } 406 } 407 4082> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/codec_config.hcs 409 410该文件涉及音量、静音模式、mic、通道模式等相关寄存器配置 411 412##### DAI驱动开发实例 413 414 代码路径: 415 416 device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/ 417 4181. 将I2S驱动注册绑定到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致 419 420 struct HdfDriverEntry g_daiDriverEntry = { 421 422 .moduleVersion = 1, 423 .moduleName = "DAI_RK3568", 424 .Bind = DaiDriverBind, 425 .Init = DaiDriverInit, 426 .Release = DaiDriverRelease, 427 428 }; 429 HDF_INIT(g_daiDriverEntry); 430 4312. DAI模块需要填充下面两个结构体 432 433g_daiData:dai设备私有配置,其中包含dai设备的初始化、读写寄存器、操作函数。 434 435g_daiDeviceOps:dai设备操作函数集,包含了dai的参数设置、触发、启动。 436 437 struct AudioDaiOps g_daiDeviceOps = { 438 .Startup = Rk3568DaiStartup, 439 .HwParams = Rk3568DaiHwParams, 440 .Trigger = Rk3568NormalTrigger, 441 }; 442 443 struct DaiData g_daiData = { 444 .Read = Rk3568DeviceReadReg, 445 .Write = Rk3568DeviceWriteReg, 446 .DaiInit = Rk3568DaiDeviceInit, 447 .ops = &g_daiDeviceOps, 448 }; 449 4501> AudioDaiOps结构体的具体填充 451 452 /*Rk3568DaiHwParams中主要完成一些pcm流信息的设置*/ 453 int32_t Rk3568DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param) 454 { 455 ...... 456 data->pcmInfo.channels = param->channels; 457 458 if (AudioFormatToBitWidth(param->format, &bitWidth) != HDF_SUCCESS) { 459 AUDIO_DEVICE_LOG_ERR("AudioFormatToBitWidth error"); 460 return HDF_FAILURE; 461 } 462 463 data->pcmInfo.bitWidth = bitWidth; 464 data->pcmInfo.rate = param->rate; 465 data->pcmInfo.streamType = param->streamType; 466 467 i2sTdm = dev_get_drvdata(&platformdev->dev); 468 ret = RK3568I2sTdmSetSysClk(i2sTdm, param); 469 if (ret != HDF_SUCCESS) { 470 AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetSysClk error"); 471 return HDF_FAILURE; 472 } 473 ret = RK3568I2sTdmSetMclk(i2sTdm, &mclk, param); 474 if (ret != HDF_SUCCESS) { 475 AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetMclk error"); 476 return HDF_FAILURE; 477 } 478 AUDIO_DEVICE_LOG_DEBUG("success"); 479 return HDF_SUCCESS; 480 } 481 int32_t Rk3568NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device) 482 { 483 ...... 484 Rk3568TxAndRxSetReg(i2sTdm, streamType, triggerFlag); 485 ...... 486 } 487 4882> DaiData结构体的具体填充 489 490 /*封装linux内核的读寄存器接口*/ 491 int32_t Rk3568DeviceReadReg(const struct DaiDevice *dai, uint32_t reg, uint32_t *val) 492 { 493 ...... 494 if (regmap_read(i2sTdm->regmap, reg, val)) { 495 ...... 496 } 497 /*封装linux内核的写寄存器接口*/ 498 int32_t Rk3568DeviceWriteReg(const struct DaiDevice *dai, uint32_t reg, uint32_t value) 499 { 500 ...... 501 if (regmap_write(i2sTdm->regmap, reg, value)) { 502 ...... 503 } 504 /*dai 设备的初始化*/ 505 int32_t Rk3568DaiDeviceInit(struct AudioCard *card, const struct DaiDevice *dai) 506 5073. 完成 bind、init和release函数的实现 508 509HdfDriverEntry结构体中的bind、init、release具体填充: 510 511 static int32_t DaiDriverInit(struct HdfDeviceObject *device) 512 { 513 ...... 514 DaiGetConfigInfo(device, &g_daiData) 515 DaiGetServiceName(device) 516 AudioSocRegisterDai(device, (void *)&g_daiData); 517 ...... 518 } 519 static int32_t DaiDriverBind(struct HdfDeviceObject *device) 520 { 521 ...... 522 daiHost->device = device; 523 device->service = &daiHost->service; 524 g_daiData.daiInitFlag = false; 525 ...... 526 } 527 static void DaiDriverRelease(struct HdfDeviceObject *device) 528 { 529 ...... 530 OsalMutexDestroy(&g_daiData.mutex); 531 daiHost = (struct DaiHost *)device->service; 532 OsalMemFree(daiHost); 533 ...... 534 } 535 5364.配置dai hcs文件 537 5381> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs 539 540 device_dai0 :: device { 541 device0 :: deviceNode { 542 policy = 1; 543 priority = 50; 544 preload = 0; 545 permission = 0666; 546 moduleName = "DAI_RK3568"; 547 serviceName = "dai_service"; 548 deviceMatchAttr = "hdf_dai_driver"; 549 } 550 } 551 5522> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dai_config.hcs 553 554该文件涉及I2S时序、配置参数以及rk809使能等相关寄存器配置 555 556##### Platform驱动开发实例 557 5581. 将DMA驱动注册到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致 559 560 struct HdfDriverEntry g_platformDriverEntry = { 561 562 .moduleVersion = 1, 563 .moduleName = "DMA_RK3568", 564 .Bind = PlatformDriverBind, 565 .Init = PlatformDriverInit, 566 .Release = PlatformDriverRelease, 567 568 }; 569 HDF_INIT(g_platformDriverEntry); 570 5712. DMA模块需要填充下面两个结构体 572 573 struct AudioDmaOps g_dmaDeviceOps = { 574 575 .DmaBufAlloc = Rk3568DmaBufAlloc, //dma内存申请函数接口 576 .DmaBufFree = Rk3568DmaBufFree, // dma内存释放函数接口 577 .DmaRequestChannel = Rk3568DmaRequestChannel, // dma申请通道函数接口 578 .DmaConfigChannel = Rk3568DmaConfigChannel, // dma通道配置函数接口 579 .DmaPrep = Rk3568DmaPrep, // dma准备函数接口 580 .DmaSubmit = Rk3568DmaSubmit, // dma submit函数接口 581 .DmaPending = Rk3568DmaPending, // dma pending函数接口 582 .DmaPause = Rk3568DmaPause, // dma暂停、停止函数接口 583 .DmaResume = Rk3568DmaResume, // dma恢复函数接口 584 .DmaPointer = Rk3568PcmPointer, // dma获取当前播放或录音位置函数接口 585 586 }; 587 588 struct PlatformData g_platformData = { 589 590 .PlatformInit = AudioDmaDeviceInit, // dma设备初始化接口 591 .ops = &g_dmaDeviceOps, 592 593 }; 594 5953. 完成 bind、init和release函数的实现 596 597HdfDriverEntry结构体中的bind、init、release具体填充: 598 599 static int32_t PlatformDriverInit(struct HdfDeviceObject *device) 600 { 601 ...... 602 PlatformGetServiceName(device); 603 AudioSocRegisterPlatform(device, &g_platformData) 604 ...... 605 } 606 static int32_t PlatformDriverBind(struct HdfDeviceObject *device) 607 { 608 ...... 609 platformHost->device = device; 610 device->service = &platformHost->service; 611 ...... 612 } 613 static void PlatformDriverRelease(struct HdfDeviceObject *device) 614 { 615 ...... 616 platformHost = (struct PlatformHost *)device->service; 617 OsalMemFree(platformHost); 618 ...... 619 } 620 6214. 配置dma hcs文件 622 6231> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs 624 625相关配置如下: 626 627 device_dma :: device { 628 device0 :: deviceNode { 629 policy = 1; 630 priority = 50; 631 preload = 0; 632 permission = 0666; 633 moduleName = "DMA_RK3568"; 634 serviceName = "dma_service_0"; 635 deviceMatchAttr = "hdf_dma_driver"; 636 } 637 } 638 6392> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dma_config.hcs 640 641没有特殊参数需要配置,一般情况下不需改动。 642 643Makefile和Kconfig配置文件 644 645文件路径: 646 647 drivers/adapter/khdf/linux/model/audio 648 649Makefile文件相关内容: 650 651 obj-$(CONFIG_DRIVERS_HDF_AUDIO_RK3566) += \ 652 $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_adapter.o \ 653 $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_impl.o \ 654 $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_linux_driver.o \ 655 $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_adapter.o \ 656 $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_ops.o \ 657 $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_adapter.o \ 658 $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_ops.o \ 659 $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_linux_driver.o \ 660 $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_adapter.o \ 661 $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_ops.o 662 663Kconfig相关内容: 664 665 config DRIVERS_HDF_AUDIO_RK3566 666 bool "Enable HDF Audio Codec driver" 667 default n 668 depends on DRIVERS_HDF_AUDIO 669 help 670 Answer Y to choice HDF Audio Codec driver. 671 672## **LCD** 673 674khdvk_3566b平台默认支持一个mipi接口的lcd屏幕 675 676LCD的适配主要依赖于HDF显示模型,显示驱动模型基于 HDF 驱动框架、Platform 接口及 OSAL 接口开发,可以屏蔽不同内核形态(LiteOS、Linux)差异,适用于不同芯片平台,为显示屏器件提供统一的驱动平台。 677 678如图为 HDF Display驱动模型层次关系 679 680![640](../figures/khdvk_3566b_lcd_01.png) 681 682当前驱动模型主要部署在内核态中,向上对接到 Display 公共 hal 层,辅助 HDI 的实现。显示驱动通过 Display-HDI 层对图形服务暴露显示屏驱动能力;向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。 683 684所以LCD的适配主要在于LCD panel器件驱动的适配 685 686器件驱动的适配分为2部分:panel驱动和hcs配置 687 688涉及的文件有: 689 690 drivers/framework/model/display/driver/panel 691 vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info 692 vendor/kaihong/khdvk_3566b/hdf_config/khdf/input 693 694### panel驱动 695 696器件驱动主要围绕如下接口展开: 697 698 struct PanelData { 699 struct HdfDeviceObject *object; 700 int32_t (*init)(struct PanelData *panel); 701 int32_t (*on)(struct PanelData *panel); 702 int32_t (*off)(struct PanelData *panel); 703 int32_t (*prepare)(struct PanelData *panel); 704 int32_t (*unprepare)(struct PanelData *panel); 705 struct PanelInfo *info; 706 enum PowerStatus powerStatus; 707 struct PanelEsd *esd; 708 struct BacklightDev *blDev; 709 void *priv; 710 }; 711 712驱动中在初始化接口中实例化该结构体: 713 714 panelSimpleDev->panel.init = PanelSimpleInit; 715 panelSimpleDev->panel.on = PanelSimpleOn; 716 panelSimpleDev->panel.off = PanelSimpleOff; 717 panelSimpleDev->panel.prepare = PanelSimplePrepare; 718 panelSimpleDev->panel.unprepare = PanelSimpleUnprepare; 719 720 static void PanelResInit(struct panel_jdi_gt911_dev *panel_dev) 721 { 722 ...... 723 panel_dev->panel.info = &g_panelInfo; 724 panel_dev->panel.init = PanelInit; 725 panel_dev->panel.on = PanelOn; 726 panel_dev->panel.off = PanelOff; 727 panel_dev->panel.prepare = PanelPrepare; 728 panel_dev->panel.unprepare = PanelUnprepare; 729 ...... 730 } 731 732g_panelInfo配置panel基础参数 733 734PanelInit负责panel的软件初始化 735 736PanelOn负责亮屏 737 738PanelOff负责灭屏 739 740PanelPrepare负责亮屏的硬件时序初始化 741 742PanelUnprepare负责灭屏的硬件时序初始化 743 744实例化后使用RegisterPanel接口向display模型注册该panel驱动即可 745 746需要说明的是,khdvk_3566b上的这款lcd使用的时候DRM显示框架 747 748### hcs配置 749 750 device3 :: deviceNode { 751 policy = 0; 752 priority = 100; 753 preload = 0; 754 moduleName = "LCD_MIPI_JDI_GT911"; 755 } 756 757### 背光 758 759背光控制分为原生linux内核框架下背光驱动以及基于HDF框架开发的背光驱动模型。 760 761rk3566背光是通过pwm控制占空比实现的,具体使用的是pwm4 762 763linux背光驱动代码路径: 764 765 linux-5.10/drivers/video/backlight/pwm_bl.c 766 linux-5.10/drivers/video/backlight/backlight.c 767 linux-5.10/drivers/pwm/pwm-rockchip.c 768 769使用HDF框架下的背光驱动,需要关闭原生驱动 770 771 # CONFIG_BACKLIGHT_PWM is not set 772 773### HDF实现 774 775基于HDF框架开发的背光驱动模型,如下图: 776 777<img title="" src="../figures/khdvk_3566b_backlight-02.png" alt="" style="zoom:80%;"> 778 779代码路径: 780 781 drivers/framework/model/display/driver/backlight/hdf_bl.c 782 783HDF BL入口函数: 784 785 static int32_t BacklightInit(struct HdfDeviceObject *object) 786 { 787 if (object == NULL) { 788 HDF_LOGE("%s: object is null!", __func__); 789 return HDF_FAILURE; 790 } 791 792 HDF_LOGI("%s success", __func__); 793 return HDF_SUCCESS; 794 } 795 796 struct HdfDriverEntry g_blDevEntry = { 797 .moduleVersion = 1, 798 .moduleName = "HDF_BL", 799 .Init = BacklightInit, 800 .Bind = BacklightBind, 801 }; 802 803 HDF_INIT(g_blDevEntry); 804 805代码路径: 806 807 drivers/framework/model/display/driver/backlight/pwm_bl.c 808 809HDF PWM入口函数: 810 811 struct HdfDriverEntry g_pwmBlDevEntry = { 812 .moduleVersion = 1, 813 .moduleName = "PWM_BL", 814 .Init = BlPwmEntryInit, 815 }; 816 HDF_INIT(g_pwmBlDevEntry); 817 818具体控制背光的接口: 819 820 static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness) 821 { 822 int32_t ret; 823 uint32_t duty; 824 struct BlPwmDev *blPwmDev = NULL; 825 826 blPwmDev = ToBlDevPriv(blDev); 827 if (blPwmDev == NULL) { 828 HDF_LOGE("%s blPwmDev is null", __func__); 829 return HDF_FAILURE; 830 } 831 832 if (blPwmDev->props.maxBrightness == 0) { 833 HDF_LOGE("%s maxBrightness is 0", __func__); 834 return HDF_FAILURE; 835 } 836 837 if (brightness == 0) { 838 return PwmDisable(blPwmDev->pwmHandle); 839 } 840 841 duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness; 842 ret = PwmSetDuty(blPwmDev->pwmHandle, duty); 843 if (ret != HDF_SUCCESS) { 844 HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret); 845 return HDF_FAILURE; 846 } 847 return PwmEnable(blPwmDev->pwmHandle); 848 } 849 850 static struct BacklightOps g_blDevOps = { 851 .updateBrightness = BlPwmUpdateBrightness, 852 }; 853 854HDF PWM实现的调用的就是内核pwm的接口。 855 856<img title="" src="../figures/khdvk_3566b_backlight_03.png" alt="" style="zoom:80%;"> 857 858代码路径: 859 860 drivers/framework/model/display/driver/panel/mipi_jdi_gt911.c 861 862在LCD HDF器件驱动注册背光: 863 864 panel_dev->panel.blDev = GetBacklightDev("hdf_pwm"); 865 if (panel_dev->panel.blDev == NULL) { 866 HDF_LOGE("%s GetBacklightDev fail", __func__); 867 goto FAIL; 868 } 869 870### HCS配置 871 872驱动hcs配置: 873 874 device_pwm_bl :: device { 875 device0 :: deviceNode { 876 policy = 0; 877 priority = 95; 878 preload = 0; 879 moduleName = "PWM_BL"; 880 deviceMatchAttr = "pwm_bl_dev"; 881 } 882 } 883 device_backlight :: device { 884 device0 :: deviceNode { 885 policy = 2; 886 priority = 90; 887 preload = 0; 888 permission = 0660; 889 moduleName = "HDF_BL"; 890 serviceName = "hdf_bl"; 891 } 892 } 893 894pwm背光的hcs配置: 895 896 root { 897 backlightConfig { 898 pwmBacklightConfig { 899 match_attr = "pwm_bl_dev"; 900 pwmDevNum = 1; 901 pwmMaxPeroid = 25000; 902 backlightDevName = "hdf_pwm"; 903 minBrightness = 0; 904 defBrightness = 127; 905 maxBrightness = 255; 906 } 907 } 908 } 909 910### 测试 911 912cat /sys/kernel/debug/pwm 来查看hdf pwm是否申请到pwm4 913 914申请成功有如下结果: 915 916requested 代表申请成功 917 918enabled 代表pwm4使能成功 919 920 # cat /sys/kernel/debug/pwm 921 platform/fe6e0000.pwm, 1 PWM device 922 pwm-0 (backlight ): requested period: 25000 ns duty: 0 ns polarity: normal 923 924## 显示适配 925 926显示适配需要完成的工作:图形服务HDI接口适配、GPU适配、mipi dsi驱动适配 927 928### 显示HDI 929 930[显示HDI](https://gitee.com/openharmony/drivers_peripheral/blob/master/display/README_zh.md)对图形服务提供显示驱动能力,包括显示图层的管理、显示内存的管理及硬件加速等。 显示HDI需要适配两部分:gralloc 和 display_device。 931 932OpenHarmony提供了使用与Hi3516DV300参考实现,厂商可根据实际情况参考适配,khdvk_3566b display适配是在//device/soc/rockchip/hardware/display目录下,仓名为[device_soc_rockchip](https://gitee.com/openharmony-sig/device_soc_rockchip)。 933 934#### display gralloc适配 935 936gralloc模块提供显示内存管理功能,该实现基于drm开发。 937 938drm设备节点定义在//device/soc/rockchip/hardware/display/src/display_gralloc/display_gralloc_gbm.c文件中,根据khdvk_3566b实际情况修改了drm文件节点。 939 940``` 941const char *g_drmFileNode = "/dev/dri/renderD128"; 942``` 943 944#### display device适配 945 946display device模块提供显示设备管理、layer管理、硬件加速等功能。 947 9481. display drm设备节点初始化,根据khdvk_3566b实际情况修改了drm设备名称。 949 950``` 951//device/soc/rockchip/hardware/display/src/display_device/drm/drm_device.cpp 952std::shared_ptr<HdiDeviceInterface> DrmDevice::Create() 953{ 954 DISPLAY_DEBUGLOG(); 955 if (mDrmFd == nullptr) { 956 const std::string name("rockchip"); // 将drm驱动设备名称修改为“rockchip” 957 int drmFd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); // 将drm驱动设备文件句柄修改为"/dev/dri/card0" 958 if (drmFd < 0) { 959 DISPLAY_LOGE("drm file:%{public}s open failed %{public}s", name.c_str(), strerror(errno)); 960 return nullptr; 961 } 962 DISPLAY_DEBUGLOG("the drm fd is %{public}d", drmFd); 963 mDrmFd = std::make_shared<HdiFd>(drmFd); 964 } 965 if (mInstance == nullptr) { 966 mInstance = std::make_shared<DrmDevice>(); 967 } 968 return mInstance; 969} 970``` 971 9722. display硬件合成的修改 973 974``` 975//device/soc/rockchip/hardware/display/src/display_gfx/display_gfx.c 976``` 977 978硬件合成文件添加了颜色空间的支持模式 979 980``` 981RgaSURF_FORMAT colorSpaceModeChange(PixelFormat color, uint8_t *isYuv) 982{ 983 RgaSURF_FORMAT rkFormat; 984 switch (color) { 985 case PIXEL_FMT_RGB_565: /**< RGB565 format */ 986 rkFormat = RK_FORMAT_RGB_565; 987 *isYuv = 0; 988 break; 989 case PIXEL_FMT_RGBA_4444: /**< RGBA4444 format */ 990 rkFormat = RK_FORMAT_RGBA_4444; 991 *isYuv = 0; 992 break; 993 case PIXEL_FMT_RGBA_5551: /**< RGBA5551 format */ 994 rkFormat = RK_FORMAT_RGBA_5551; 995 *isYuv = 0; 996 break; 997 case PIXEL_FMT_RGBX_8888: /**< RGBX8888 format */ 998 rkFormat = RK_FORMAT_RGBX_8888; 999 *isYuv = 0; 1000 break; 1001 case PIXEL_FMT_RGBA_8888: /**< RGBA8888 format */ 1002 rkFormat = RK_FORMAT_RGBA_8888; 1003 *isYuv = 0; 1004 break; 1005 case PIXEL_FMT_RGB_888: /**< RGB888 format */ 1006 rkFormat = RK_FORMAT_RGB_888; 1007 *isYuv = 0; 1008 break; 1009 case PIXEL_FMT_BGR_565: /**< BGR565 format */ 1010 rkFormat = RK_FORMAT_BGR_565; 1011 *isYuv = 0; 1012 break; 1013 case PIXEL_FMT_BGRA_4444: /**< BGRA4444 format */ 1014 rkFormat = RK_FORMAT_BGRA_4444; 1015 *isYuv = 0; 1016 break; 1017 case PIXEL_FMT_BGRA_5551: /**< BGRA5551 format */ 1018 rkFormat = RK_FORMAT_BGRA_5551; 1019 *isYuv = 0; 1020 break; 1021 case PIXEL_FMT_BGRX_8888: /**< BGRX8888 format */ 1022 rkFormat = RK_FORMAT_BGRX_8888; 1023 *isYuv = 0; 1024 break; 1025 case PIXEL_FMT_BGRA_8888: /**< BGRA8888 format */ 1026 rkFormat = RK_FORMAT_BGRA_8888; 1027 *isYuv = 0; 1028 break; 1029 case PIXEL_FMT_YCBCR_422_SP: /**< YCBCR422 semi-planar format */ 1030 rkFormat = RK_FORMAT_YCbCr_420_SP; 1031 *isYuv = 1; 1032 break; 1033 case PIXEL_FMT_YCRCB_422_SP: /**< YCRCB422 semi-planar format */ 1034 rkFormat = RK_FORMAT_YCrCb_422_SP; 1035 *isYuv = 1; 1036 break; 1037 case PIXEL_FMT_YCBCR_420_SP: /**< YCBCR420 semi-planar format */ 1038 rkFormat = RK_FORMAT_YCbCr_420_SP; 1039 *isYuv = 1; 1040 break; 1041 case PIXEL_FMT_YCRCB_420_SP: /**< YCRCB420 semi-planar format */ 1042 rkFormat = RK_FORMAT_YCrCb_420_SP; 1043 *isYuv = 1; 1044 break; 1045 case PIXEL_FMT_YCBCR_422_P: /**< YCBCR422 planar format */ 1046 rkFormat = RK_FORMAT_YCbCr_422_P; 1047 *isYuv = 1; 1048 break; 1049 case PIXEL_FMT_YCRCB_422_P: /**< YCRCB422 planar format */ 1050 rkFormat = RK_FORMAT_YCrCb_422_P; 1051 *isYuv = 1; 1052 break; 1053 case PIXEL_FMT_YCBCR_420_P: /**< YCBCR420 planar format */ 1054 rkFormat = RK_FORMAT_YCbCr_420_P; 1055 *isYuv = 1; 1056 break; 1057 case PIXEL_FMT_YCRCB_420_P: /**< YCRCB420 planar format */ 1058 rkFormat = RK_FORMAT_YCrCb_420_P; 1059 *isYuv = 1; 1060 break; 1061 case PIXEL_FMT_YUYV_422_PKG: /**< YUYV422 packed format */ 1062 rkFormat = RK_FORMAT_YUYV_422; 1063 *isYuv = 1; 1064 break; 1065 case PIXEL_FMT_UYVY_422_PKG: /**< UYVY422 packed format */ 1066 rkFormat = RK_FORMAT_UYVY_422; 1067 *isYuv = 1; 1068 break; 1069 case PIXEL_FMT_YVYU_422_PKG: /**< YVYU422 packed format */ 1070 rkFormat = RK_FORMAT_YUYV_422; 1071 *isYuv = 1; 1072 break; 1073 case PIXEL_FMT_VYUY_422_PKG: /**< VYUY422 packed format */ 1074 rkFormat = RK_FORMAT_VYUY_422; 1075 *isYuv = 1; 1076 break; 1077 default: 1078 rkFormat = RK_FORMAT_UNKNOWN; 1079 break; 1080 } 1081 return rkFormat; 1082} 1083``` 1084 1085在合成时增加了旋转90、180、270度 1086 1087``` 1088int32_t TransformTypeChange(TransformType type) 1089{ 1090 int32_t rkRotateType; 1091 switch (type) { 1092 case ROTATE_90: /**< Rotation by 90 degrees */ 1093 rkRotateType = IM_HAL_TRANSFORM_ROT_90; 1094 break; 1095 case ROTATE_180: /**< Rotation by 180 degrees */ 1096 rkRotateType = IM_HAL_TRANSFORM_ROT_180; 1097 break; 1098 case ROTATE_270: /**< Rotation by 270 degrees */ 1099 rkRotateType = IM_HAL_TRANSFORM_ROT_270; 1100 break; 1101 default: 1102 rkRotateType = 0; /**< No rotation */ 1103 break; 1104 } 1105 return rkRotateType; 1106} 1107``` 1108 1109#### 测试验证 1110 1111[hello_composer](https://gitee.com/openharmony/graphic_graphic_2d/tree/master/rosen/samples/composer)测试模块:Rosen图形框架提供的测试程序,主要显示流程,HDI接口等功能是否正常,默认随系统编译。 1112 1113代码路径: 1114 1115``` 1116foundation/graphic/graphic_2d/rosen/samples/composer/ 1117├── BUILD.gn 1118├── hello_composer.cpp 1119├── hello_composer.h 1120├── layer_context.cpp 1121├── layer_context.h 1122└── main.cpp 1123``` 1124 1125具体验证如下: 1126 11271. 关闭render service 1128 1129 ``` 1130 service_control stop render_service 1131 ``` 1132 11332. 关闭 fondation进程 1134 1135 ``` 1136 service_control stop fondation 1137 ``` 1138 11393. 运行hello_composer测试相关接口 1140 切换到/system/bin目录下,运行hello_composer测试命令 1141 1142 ``` 1143 #cd /system/bin 1144 #./hello_composer 1145 rga_api version 1.3.0_[1] (df26244 build: 2021-09-01 11:23:31 base: ) 1146 ``` 1147 1148 查看mipi显示屏幕上的变化 1149 1150https://gitee.com/openharmony/drivers_peripheral/tree/master/display/test/unittest/standard单元测试:HDI显示模块提供的测试模块,主要测试HDI接口、显示buffer、驱动等能力,测试时也需要关闭render service和fondation进程。 1151 1152代码路径:/drivers/peripheral/display/test/unittest/standard 1153 1154``` 1155├── BUILD.gn 1156├── common 1157│ ├── display_test.h 1158│ ├── display_test_utils.cpp 1159│ └── display_test_utils.h 1160├── display_device 1161│ ├── hdi_composition_check.cpp 1162│ ├── hdi_composition_check.h 1163│ ├── hdi_device_test.cpp 1164│ ├── hdi_device_test.h 1165│ ├── hdi_test_device_common.h 1166│ ├── hdi_test_device.cpp 1167│ ├── hdi_test_device.h 1168│ ├── hdi_test_display.cpp 1169│ ├── hdi_test_display.h 1170│ ├── hdi_test_layer.cpp 1171│ ├── hdi_test_layer.h 1172│ ├── hdi_test_render_utils.cpp 1173│ └── hdi_test_render_utils.h 1174│── display_gfx 1175│ │── display_gfx_test.cpp 1176│ │── display_gfx_test.h 1177│ │── soft_blit.cpp 1178│ │── soft_blit.h 1179└── display_gralloc 1180 ├── display_gralloc_test.cpp 1181 └── display_gralloc_test.h 1182``` 1183 1184具体验证如下: 1185 11861. 添加编译模块 1187 修改drivers/peripheral/display/test/BUILD.gn 1188 1189``` 1190 group("hdf_test_display") { 1191 testonly = true 1192 deps = [ 1193 "fuzztest:hdf_display_fuzztest", 1194 "unittest/standard:hdf_unittest_display", //添加display单元测试 1195 ] 1196 } 1197``` 1198 11992. 添加缺失的文件包含 1200 修改drivers/peripheral/display/test/unittest/standard/BUILD.gn 1201 在第63行处,添加包含目录//device/soc/rockchip/hardware/display/src/display_gralloc,如果不修改此处有可能编译报错。 1202 1203``` 1204ohos_unittest("gralloctest") { 1205 module_out_path = module_output_path 1206 sources = [ "display_gralloc/display_gralloc_test.cpp" ] 1207 deps = [ 1208 "//drivers/peripheral/display/hal:hdi_display_gralloc", 1209 "//third_party/googletest:gtest_main", 1210 ] 1211 include_dirs = [ 1212 "common", 1213 "//drivers/peripheral/display/hal/default_standard/include", 1214 "//drivers/peripheral/display/hal/default_standard/src/display_gralloc", 1215 "//device/soc/rockchip/hardware/display/src/display_gralloc", //添加这行,将display_gralloc包含进编译 1216 "//drivers/peripheral/display/interfaces/include", 1217 "//drivers/peripheral/base", 1218 "//drivers/peripheral/display/interfaces/include", 1219 "//foundation/graphic/standard/utils/include", 1220 ] 1221 external_deps = [ 1222 "device_driver_framework:libhdf_utils", 1223 "utils_base:utils", 1224 ] 1225} 1226``` 1227 12283. 编译命令 1229 编译hdf_test_display的命令如下: 1230 1231``` 1232 ./build.sh --product-name khdvk_3566b --build-target hdf_test_display 1233``` 1234 12354. 编译结果 1236 编译结果路径在out/khdvk_3566b/tests/unittest/hdf/display目录下,该目录下有三个可执行文件devicetest、gfxtest、gralloctest,可将这三文件通过hdc发送到khdvk_3566b开发板上运行测试。 1237 12385. 运行测试 1239 通过hdc下载到开发板/system/bin/目录下,并修改测试程序的可执行属性,在终端下输入如下命令 1240 1241``` 1242hdc_std.exe file send D:\hdc\devicetest /system/bin/ 1243hdc_std.exe file send D:\hdc\gfxtest /system/bin/ 1244hdc_std.exe file send D:\hdc\gralloctest /system/bin/ 1245``` 1246 1247进入hdc命令hdc_std.exe shell后 1248 1249先关闭render service和foundation: 1250 1251``` 1252service_control stop render_service 1253service_control stop fondation 1254``` 1255 1256再分别执行命令,查看mipi屏显示结果: 1257 1258``` 1259cd /system/bin/ 1260``` 1261 1262执行devicetest 1263 1264``` 1265chmod -R 777 devicetest 1266devicetest 1267``` 1268 1269执行gfxtest 1270 1271``` 1272chmod -R 777 gfxtest 1273gfxtest 1274``` 1275 1276执行gralloctest 1277 1278``` 1279chmod -R 777 gralloctest 1280gralloctest 1281``` 1282 1283### GPU 1284 1285GPU图形处理器, khdvk_3566b GPU适配是在//device/soc/rockchip/hardware/gpu目录下,目前采用的是rockchip提供闭源的bifrost gpu方案。 1286 1287目录结构: 1288 1289``` 1290├── BUILD.gn 1291├── lib64 1292│ └── libmali-bifrost-g52-g2p0-ohos.so 1293├── lib 1294│ └── libmali-bifrost-g52-g2p0-ohos.so 1295└── include 1296 └── gbm.h 1297``` 1298 1299gpu编译的内容,我们来看下BUILD.gn的内容,其中我们预编译了libmali-bifrost-g52-g2p0-ohos.so动态库,khdvk_3566b是arm64位的,所以编译了lib64目录下的libmali-bifrost-g52-g2p0-ohos.so动态库。其中gup模块符号链接libEGL.so、libGLESv1.so、libGLESv2.so、libGLESv3.so、libmali.so.0、libmali.so.1动态库的符号。 1300 1301``` 1302import("//build/ohos.gni") 1303import("//build/ohos/ndk/ndk.gni") 1304 1305config("libmali-bifrost-g52-g2p0-ohos") { 1306 include_dirs = [ "include" ] 1307 cflags = [ 1308 "-Wno-incompatible-pointer-types", 1309 "-Werror", 1310 "-Wimplicit-function-declaration", 1311 "-Wno-error=unused-variable", 1312 ] 1313 cflags = [] 1314} 1315 1316ohos_prebuilt_shared_library("mali-bifrost-g52-g2p0-ohos") { 1317 if (target_cpu == "arm") { 1318 source = "lib/libmali-bifrost-g52-g2p0-ohos.so" 1319 } else if (target_cpu == "arm64") { 1320 source = "lib64/libmali-bifrost-g52-g2p0-ohos.so" 1321 } 1322 1323 # decoupling system.img and vendor.img 1324 install_images = [ chipset_base_dir ] 1325 relative_install_dir = "chipsetsdk" 1326 subsystem_name = "rockchip_products" 1327 part_name = "rockchip_products" 1328 install_enable = true 1329 symlink_target_name = [ 1330 "libEGL.so", 1331 "libGLESv1.so", 1332 "libGLESv2.so", 1333 "libGLESv3.so", 1334 "libmali.so.0", 1335 "libmali.so.1", 1336 ] 1337} 1338``` 1339 1340## TOUCH PANEL 1341 1342常见的INPUT设备有键盘、鼠标、游戏杆、Touch Screen等。Touch 设备与主机通讯采用标准 I2C 总线,触屏 IC 提供中断支持,提高了触屏数据的实时性。本项目的触摸屏器件IC 为 GT911。 1343 1344### **驱动框架模型** 1345 1346#### INPUT驱动模型 1347 1348![img](../figures/khdvk_3566b_tp_1.png) 1349 1350INPUT 驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。 1351 1352(1)设备管理层:为各类输入设备驱动提供input设备的注册、注销接口,同时统一管理 input 设备列表; 1353 1354(2)平台驱动层:指各类input设备的公共抽象驱动(例如触摸屏的公共驱动),负责对板级硬件进行初始化、硬件中断处理、向manager注册input设备等; 1355 1356(3)器件驱动层:指各器件厂家的差异化驱动,通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化。 1357 1358#### HDI接口层框架图 1359 1360INPUT驱动提供给系统服务Input Service可直接调用的驱动能力接口,按照属性分类三类:input设备管理模块、input数据上报模块、input业务控制模块,HDI接口主要包括如下三大类: 1361 1362- input设备管理模块:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等; 1363- input数据上报模块:负责输入事件的上报,包括注册、注销数据上报回调函数等; 1364- input业务控制模块:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。 1365 1366![image-20220704102754050](../figures/khdvk_3566b_tp_2.png) 1367 1368### **HDF驱动适配** 1369 1370#### **HCS配置** 1371 1372配置设备描述信息,在device_info.hcs中添加device_touch_chip: 1373 1374``` 1375 input :: host { 1376 hostName = "input_host"; 1377 priority = 100; 1378 device_input_manager :: device { // Input管理层设备描述信息 1379 device0 :: deviceNode { 1380 policy = 2; 1381 priority = 100; 1382 preload = 0; 1383 permission = 0660; 1384 moduleName = "HDF_INPUT_MANAGER"; 1385 serviceName = "hdf_input_host"; 1386 deviceMatchAttr = ""; 1387 } 1388 } 1389 device_hdf_touch :: device { // Input公共驱动层设备描述信息 1390 device0 :: deviceNode { 1391 policy = 2; 1392 priority = 120; 1393 preload = 0; 1394 permission = 0660; 1395 moduleName = "HDF_TOUCH"; 1396 serviceName = "hdf_input_event1"; 1397 deviceMatchAttr = "touch_device1"; 1398 } 1399 } 1400 device_touch_chip :: device { // Input器件驱动层信息 1401 device0 :: deviceNode { 1402 policy = 0; 1403 priority = 130; 1404 preload = 0; 1405 permission = 0660; 1406 moduleName = "HDF_TOUCH_GT911"; 1407 serviceName = "hdf_touch_gt911_service"; 1408 deviceMatchAttr = "zsj_gt911_5p5"; 1409 } 1410 } 1411 device_hdf_hid :: device { 1412 device0 :: deviceNode { 1413 policy = 2; 1414 priority = 200; 1415 preload = 0; 1416 permission = 0660; 1417 moduleName = "HDF_HID"; 1418 } 1419 } 1420 } 1421``` 1422 1423配置Touch器件信息,在input_config.hcs中添加器件的特性: 1424 1425``` 1426 chipConfig { 1427 template touchChip { 1428 match_attr = ""; 1429 chipName = "gt911"; 1430 vendorName = "zsj"; 1431 chipInfo = "AAAA11222"; 1432 busType = 0; 1433 deviceAddr = 0x5D; 1434 irqFlag = 2; 1435 maxSpeed = 400; 1436 chipVersion = 0; //parse Coord TypeA 1437 powerSequence { 1438 /* [type, status, dir , delay] 1439 <type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int 1440 <status> 0:off or low 1:on or high 2:no ops 1441 <dir> 0:input 1:output 2:no ops 1442 <delay> meanings delay xms, 20: delay 20ms 1443 */ 1444 powerOnSeq = [4, 0, 1, 5, 1445 3, 0, 1, 10, 1446 3, 1, 1, 60, 1447 4, 2, 0, 50]; 1448 suspendSeq = [3, 0, 2, 10]; 1449 resumeSeq = [3, 1, 2, 10]; 1450 powerOffSeq = [3, 0, 2, 10, 1451 1, 0, 2, 20]; 1452 } 1453 } 1454 1455 chip0 :: touchChip { 1456 match_attr = "zsj_gt911_5p5"; 1457 chipInfo = "ZIDN45100"; 1458 chipVersion = 0; 1459 } 1460 } 1461``` 1462 1463#### 适配文件 1464 1465Touch驱动适配涉及的文件及目录: 1466 14671、 编辑 Makefile 文件:./drivers/adapter/khdf/linux/model/input/Makefile 1468 14692、 公共配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs 1470 14713、 私有配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/input/input_config.hcs 1472 14734、 驱动:drivers\framework\model\input\driver\touchscreen 1474 1475HDF驱动模型高度抽象集成,TP驱动的适配主要是器件驱动层的适配,首先需要明确TP所需要的软硬件资源。 1476 1477TP模组需要主机上的如下硬件资源: 1478 14791.中断引脚 1480 14812.Reset引脚 1482 14833.使用的哪一组i2c,从设备的地址是什么 1484 14854.TP的初始化固件(通常由IC厂商提供) 1486 14875.触摸屏的分辨率 1488 1489TP模组需要依赖主机上的如下软件资源: 1490 14911.Hdf gpio子系统 用于设置gpio pin脚以及一些中断资源 1492 14932.Hdf i2c 子系统 用于进行i2c通信 1494 14953.Input模型 1496 1497器件差异化接口适配,示例代码路径: 1498 1499``` 1500./drivers/framework/model/input/driver/touchscreen/Touch_gdi_gt911.c 1501 1502static struct TouchChipOps g_gt911ChipOps = { // 器件IC接口 1503 .Init = ChipInit, // 初始化 1504 .Detect = ChipDetect, // 器件检测 1505 .Resume = ChipResume, // 唤醒 1506 .Suspend = ChipSuspend, // 休眠 1507 .DataHandle = ChipDataHandle, // 器件数据读取 1508 .UpdateFirmware = UpdateFirmware, // 固件升级 1509 .SetAbility = SetAbility, // 配置 1510}; 1511``` 1512 1513器件驱动初始化及HDF注册,示例代码路径: 1514 1515``` 1516./drivers/framework/model/input/driver/touchscreen/touch_jdi_gt911.c 1517 1518static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device) 1519{ 1520 ... 1521 /* 器件配置结构体内存申请、配置信息解析及挂载 */ 1522 chipCfg = ChipConfigInstance(device); 1523 ... 1524 /* 器件实例化 */ 1525 chipDev = ChipDeviceInstance(); 1526 ... 1527 /* 器件信息挂载及器件私有操作挂载 */ 1528 chipDev->chipCfg = chipCfg; 1529 chipDev->ops = &g_gt911ChipOps; 1530 ... 1531 /* 注册器件驱动至平台驱动 */ 1532 RegisterChipDevice(chipDev); 1533 ... 1534} 1535 1536struct HdfDriverEntry g_touchGoodixChipEntry = { 1537 .moduleVersion = 1, 1538 .moduleName = "HDF_TOUCH_GT911", 1539 .Init = HdfGoodixChipInit, // 器件驱动初始化函数 1540 .Release = HdfGoodixChipRelease, 1541}; 1542HDF_INIT(g_touchGoodixChipEntry); // 注册器件驱动至HDF框架 1543``` 1544 1545#### 代码分布 1546 1547```c 1548/drivers/peripheral/input 1549/drivers/framework/model/input 1550``` 1551 1552## OpenHarmony Camera HDF驱动框架概述 1553 1554OpenHarmony Camera驱动模型结构 1555 1556![图1](../figures/khdvk_3566b_camera_arch.png) 1557 1558- HDI Implementation:对上实现HDI接口,向下调用框架层的接口,完成HDI接口任务的转发。 1559- Buffer Manager:屏蔽不同内存管理的差异,为子系统提供统一的操作接口,同时提供buffer轮转的功能。 1560- Pipeline Core:解析HCS配置完成pipeline的搭建,调度pipeline中的各个node完成流的处理 1561- Device Manager:通过调用底层硬件适配层接口,实现查询控制底层设备、枚举监听底层设备的功能 1562- Platform Adaption:屏蔽硬件差异,为Device Manager提供统一的操作底层硬件的能力 1563 1564### CameraService 进程 1565 1566CameraService源码目录为:foundation/multimedia/camera_standard,camera app通过camera service与hal层进行交互 1567 1568 ├── bundle.json 1569 ├── figures 1570 ├── frameworks camera frameworks部分,支持js和native转换 1571 │ ├── js 1572 │ └── native 1573 ├── hisysevent.yaml 1574 ├── interfaces CameraService接口 1575 │ ├── inner_api 1576 │ └── kits 1577 ├── LICENSE 1578 ├── OAT.xml 1579 ├── README.md 1580 ├── README_zh.md 1581 ├── sa_profile CameraService进程加载配置文件 1582 │ ├── 3008.xml 1583 │ └── BUILD.gn 1584 └── services CameraService启动相关 1585 ├── camera_service 1586 └── etc 1587 1588CameraService启动入口在foundation/multimedia/camera_standard/services/etc/camera_service.cfg进行启动配置 1589 1590 "services" : [{ 1591 "name" : "camera_service", 1592 "path" : ["/system/bin/sa_main", "/system/profile/camera_service.xml"], 1593 "uid" : "cameraserver", 1594 "gid" : ["system", "shell"], 1595 "secon" : "u:r:camera_service:s0" 1596 } 1597 ] 1598 1599### Camera驱动框架介绍 1600 1601###Camera驱动整体架构 1602 1603![图2](../figures/khdvk_3566b_camera_driver.jpg) 1604 1605#### camera驱动源码分布 1606 1607Camera 驱动框架所在的仓为:drivers_peripheral,源码目录为:“drivers/peripheral/camera”。 1608 1609 ├── bundle.json 1610 ├── figures 1611 │ ├── Camera模块驱动模型.png 1612 │ └── logic-view-of-modules-related-to-this-repository_zh.png 1613 ├── hal 1614 │ ├── adapter #平台适配层,适配平台 1615 │ ├── buffer_manager 1616 │ ├── BUILD.gn #Camera驱动框架构建入口 1617 │ ├── camera.gni #定义组件所使用的全局变量 1618 │ ├── device_manager 1619 │ ├── hdi_impl 1620 │ ├── include 1621 │ ├── init #demo sample 1622 │ ├── pipeline_core 1623 │ ├── test #测试代码 1624 │ └── utils 1625 ├── hal_c #为海思平台提供专用C接口 1626 │ ├── BUILD.gn 1627 │ ├── camera.gni 1628 │ ├── hdi_cif 1629 │ └── include 1630 ├── interfaces #HDI接口 1631 │ ├── hdi_ipc 1632 │ ├── hdi_passthrough 1633 │ ├── include 1634 │ └── metadata 1635 └── README_zh.md 1636 1637#### Camera Host HDF驱动 1638 1639###配置文件 1640 1641Camera Host HDF配置相关在“vendor/kaihong/khdvk_3566b/hdf_config/uhdf/device_info.hcs” 1642 1643 hdi_server :: host { 1644 hostName = "camera_host"; 1645 priority = 50; 1646 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; 1647 camera_device :: device { 1648 device0 :: deviceNode { 1649 policy = 2; 1650 priority = 100; 1651 moduleName = "libcamera_hdi_impl.z.so"; 1652 serviceName = "camera_service"; 1653 } 1654 } 1655 ... 1656 } 1657 1658其中主要参数说明如下: 1659 1660- hostName = "camera_host":camera host节点,该节点为一个独立进程,如果需要独立进程,新增属于自己的host节点 1661- policy = 2:服务发布策略,Camera使用HDI服务,需设置为2 1662- moduleName:camera host驱动实现库名 1663- serviceName:服务名称,请保持全局唯一性,后面HDF Manager会根据这个名称拉起camera hdf 1664 1665###camera host服务启动 1666camera host 服务由hdf_devhost启动,配置文件存放于vendor/etc/init/hdf_devhost.cfg 1667 1668 { 1669 "name" : "camera_host", 1670 "path" : ["/vendor/bin/hdf_devhost", "8", "camera_host"], 1671 "uid" : "camera_host", 1672 "gid" : ["camera_host"], 1673 "caps" : ["DAC_OVERRIDE", "DAC_READ_SEARCH"], 1674 "secon" : "u:r:camera_host:s0" 1675 } 1676 1677###Camera host驱动实现 1678代码路径:drivers/peripheral/camera/interfaces/hdi_ipc/server/src/camera_host_driver.cpp 1679 1680驱动入口结构体,后面将该结构体注册进HDF框架中 1681 1682 struct HdfDriverEntry g_cameraHostDriverEntry = { 1683 .moduleVersion = 1, 1684 .moduleName = "camera_service", 1685 .Bind = HdfCameraHostDriverBind, 1686 .Init = HdfCameraHostDriverInit, 1687 .Release = HdfCameraHostDriverRelease, 1688 }; 1689 1690消息发布服务 1691 1692 static int32_t CameraServiceDispatch(struct HdfDeviceIoClient *client, int cmdId, 1693 struct HdfSBuf *data, struct HdfSBuf *reply) 1694 { 1695 HdfCameraService *hdfCameraService = CONTAINER_OF(client->device->service, HdfCameraService, ioservice); 1696 return CameraHostServiceOnRemoteRequest(hdfCameraService->instance, cmdId, data, reply); 1697 } 1698 1699参数说明: 1700 1701 client:HdfDeviceIoClient设备句柄 1702 cmdId:请求消息命令字 1703 data:其他服务或者IO请求数据 1704 reply:存储返回消息内容数据 1705 1706绑定服务:初始化设备服务对象和资源对象 1707 1708 int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) 1709 { 1710 ... 1711 hdfCameraService->ioservice.Dispatch = CameraServiceDispatch; 1712 hdfCameraService->ioservice.Open = nullptr; 1713 hdfCameraService->ioservice.Release = nullptr; 1714 hdfCameraService->instance = CameraHostStubInstance(); 1715 1716 deviceObject->service = &hdfCameraService->ioservice; 1717 return HDF_SUCCESS; 1718 } 1719 1720相关说明: 1721 1722 hdfCameraService->ioservice.Dispatch:注册消息分发服务接口 1723 hdfCameraService->instance:创建camerahost实例 1724 1725驱动初始化函数: 探测并初始化驱动程序 1726 1727 int HdfCameraHostDriverInit(struct HdfDeviceObject *deviceObject) 1728 { 1729 return HDF_SUCCESS; 1730 } 1731 1732驱动资源释放函数 : 如已经绑定的设备服务对象 1733 1734 void HdfCameraHostDriverRelease(HdfDeviceObject *deviceObject) 1735 { 1736 if (deviceObject == nullptr || deviceObject->service == nullptr) { 1737 HDF_LOGE("%{public}s deviceObject or deviceObject->service is NULL!", __FUNCTION__); 1738 return; 1739 } 1740 HdfCameraService *hdfCameraService = CONTAINER_OF(deviceObject->service, HdfCameraService, ioservice); 1741 if (hdfCameraService == nullptr) { 1742 HDF_LOGE("%{public}s hdfCameraService is NULL!", __FUNCTION__); 1743 return; 1744 } 1745 OsalMemFree(hdfCameraService); 1746 } 1747 1748设备创建不成功,关闭服务,释放相关资源 1749 1750#### DeviceManager 1751 1752创建SensorManager、FlashManager、ISPManager管理相应的设备。 1753![图3](../figures/khdvk_3566b_devicemanger.png) 1754 1755####SensorManager 1756sensor Manager结构如下 1757 1758 class SensorManager : public IManager { 1759 public: 1760 SensorManager(); 1761 explicit SensorManager(ManagerId managerId); 1762 virtual ~SensorManager(); 1763 RetCode CreateController(ControllerId controllerId, std::string hardwareName); 1764 RetCode DestroyController(ControllerId controllerId, std::string hardwareName); 1765 std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName); 1766 1767 void Configure(std::shared_ptr<CameraMetadata> meta); 1768 RetCode Start(std::string hardwareName, int buffCont, DeviceFormat& format); 1769 RetCode Stop(std::string hardwareName); 1770 RetCode PowerUp(std::string hardwareName); 1771 RetCode PowerDown(std::string hardwareName); 1772 std::shared_ptr<ISensor> GetSensor(std::string sensorName); 1773 1774 RetCode SendFrameBuffer(std::shared_ptr<FrameSpec> buffer, std::string hardwareName); 1775 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName); 1776 void SetNodeCallBack(const NodeBufferCb cb, std::string hardwareName); 1777 void SetMetaDataCallBack(const MetaDataCb cb, std::string hardwareName); 1778 1779 private: 1780 bool CheckCameraIdList(std::string hardwareName); 1781 std::vector<std::shared_ptr<SensorController>> sensorList_; 1782 }; 1783 } 1784 1785PowerUp为上电接口,OpenCamera时调用此接口进行设备上电操作 1786 1787PowerDown为下电接口,CloseCamera时调用此接口进行设备下电操作 1788 1789Configures为Metadata下发接口,如需设置metadata参数到硬件设备,可实现此接口进行解析及下发 1790 1791Start为硬件模块使能接口,pipeline中的各个node进行使能的时候,会去调用,可根据需要定义实现,比如sensor的起流操作就可放在此处进行实现,Stop和Start为相反操作,可实现停流操作 1792 1793SendFrameBuffer为每一帧buffer下发接口,所有和驱动进行buffer交互的操作,都是通过此接口进行的 1794 1795SetNodeCallBack为pipeline,通过此接口将buffer回调函数设置到devicemanager 1796 1797SetMetaDataCallBack为metadata回调接口,通过此接口将从底层获取的metadata数据上报给上层 1798 1799BufferCallback上传每一帧已填充数据buffer的接口,通过此接口将buffer上报给pipeline 1800 1801SetAbilityMetaDataTag设置需要从底层获取哪些类型的metadata数据,因为框架支持单独获取某一类型或多类型的硬件设备信息,所以可以通过此接口,获取想要的metadata数据 1802 1803Camera Sensor Controller结构如下: 1804 1805 class SensorController : public IController { 1806 public: 1807 SensorController(); 1808 explicit SensorController(std::string hardwareName); 1809 virtual ~SensorController(); 1810 RetCode Init(); 1811 RetCode PowerUp(); 1812 RetCode PowerDown(); 1813 RetCode Configure(std::shared_ptr<CameraMetadata> meta); 1814 RetCode Start(int buffCont, DeviceFormat& format); 1815 RetCode Stop(); 1816 ... 1817 void SetMetaDataCallBack(MetaDataCb cb) override; 1818 void BufferCallback(std::shared_ptr<FrameSpec> buffer); 1819 1820 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag); 1821 RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta); 1822 RetCode Flush(int32_t streamId); 1823 ... 1824 1825 }; 1826 1827PowerUp下发命令给v4l2 dev去操作实际设备进行上电操作 1828PowerDown下发命令给v4l2 dev去操作实际设备进行下电操作 1829同理其他操作参考SensorManager. 1830####FlashManager 1831Flash Manger结构如下: 1832 1833 class FlashManager : public IManager { 1834 public: 1835 FlashManager(); 1836 explicit FlashManager(ManagerId managerId); 1837 virtual ~FlashManager(); 1838 RetCode CreateController(ControllerId controllerId, std::string hardwareName); 1839 std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName); 1840 RetCode PowerUp(std::string hardwareName); 1841 RetCode PowerDown(std::string hardwareName); 1842 void Configure(std::shared_ptr<CameraMetadata> meta); 1843 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName) 1844 { 1845 (void)abilityMetaDataTag; 1846 (void)hardwareName; 1847 return; 1848 } 1849 RetCode SetFlashlight(FlashMode flashMode, bool enable, std::string hardwareName); 1850 private: 1851 bool CheckCameraIdList(std::string hardwareName); 1852 std::vector<std::shared_ptr<FlashController>> flashList_; 1853 } 1854 1855Flash controller结构如下: 1856 1857 class FlashController : public IController { 1858 public: 1859 FlashController(); 1860 explicit FlashController(std::string hardwareName); 1861 virtual ~FlashController(); 1862 RetCode Init(); 1863 RetCode PowerUp(); 1864 RetCode PowerDown(); 1865 RetCode Configure(std::shared_ptr<CameraMetadata> meta) 1866 { 1867 (void)meta; 1868 return RC_OK; 1869 } 1870 RetCode SetFlashlight(FlashMode flashMode, bool enable); 1871 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag); 1872 RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta); 1873 private: 1874 std::mutex startVolock_; 1875 bool startVoState_ = false; 1876 } 1877 1878####ISPManager 1879ISP Manager结构如下 1880 1881 class IspManager : public IManager { 1882 public: 1883 IspManager(); 1884 explicit IspManager(ManagerId managerId); 1885 virtual ~IspManager(); 1886 RetCode CreateController(ControllerId controllerId, std::string hardwareName); 1887 std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName); 1888 void Configure(std::shared_ptr<CameraMetadata> meta); 1889 RetCode Start(std::string hardwareName); 1890 RetCode Stop(std::string hardwareName); 1891 RetCode PowerUp(std::string hardwareName); 1892 RetCode PowerDown(std::string hardwareName); 1893 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName) 1894 { 1895 (void)abilityMetaDataTag; 1896 (void)hardwareName; 1897 return; 1898 } 1899 private: 1900 bool CheckCameraIdList(std::string hardwareName); 1901 std::vector<std::shared_ptr<IspController>> ispList_; 1902 }; 1903 1904ISP controller结构如下 1905 1906 class IspController : public IController { 1907 public: 1908 IspController(); 1909 explicit IspController(std::string hardwareName); 1910 virtual ~IspController(); 1911 RetCode Init(); 1912 RetCode Configure(std::shared_ptr<CameraMetadata> meta); 1913 RetCode PowerUp(); 1914 RetCode PowerDown(); 1915 RetCode Stop(); 1916 RetCode Start(); 1917 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag) 1918 { 1919 (void)abilityMetaDataTag; 1920 return; 1921 } 1922 RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta) 1923 { 1924 (void)meta; 1925 return RC_OK; 1926 } 1927 private: 1928 std::mutex startIsplock_; 1929 bool startIspState_ = false; 1930 } 1931 1932#### PlatForm Adapter 1933 1934这部分通过V4l2框架对video设备进行管理,包括对相应设备的打开、启动/关闭数据流、设置/获取图像格式等等 1935![图4](../figures/khdvk_3566b_adapter_platform.png) 1936 1937####源代码 1938V4l2 Adapter 源码位于driver/peripheral/camera/hal/adapter/platform/v4l2/src/driver_adapter 1939部分关键函数如下: 1940 1941 class HosV4L2Dev { 1942 public: 1943 ... 1944 RetCode start(const std::string& cameraID); 1945 1946 RetCode stop(const std::string& cameraID); 1947 RetCode CreatBuffer(const std::string& cameraID, const std::shared_ptr<FrameSpec>& frameSpec); 1948 1949 RetCode StartStream(const std::string& cameraID); 1950 1951 RetCode QueueBuffer(const std::string& cameraID, const std::shared_ptr<FrameSpec>& frameSpec); 1952 1953 RetCode ReleaseBuffers(const std::string& cameraID); 1954 1955 RetCode StopStream(const std::string& cameraID); 1956 1957 RetCode SetCallback(BufCallback cb); 1958 1959 static RetCode Init(std::vector<std::string>& cameraIDs); 1960 1961 static std::map<std::string, std::string> deviceMatch; 1962 1963 private: 1964 std::shared_ptr<HosV4L2Buffers> myBuffers_ = nullptr; 1965 std::shared_ptr<HosV4L2Streams> myStreams_ = nullptr; 1966 std::shared_ptr<HosFileFormat> myFileFormat_ = nullptr; 1967 std::shared_ptr<HosV4L2Control> myControl_ = nullptr; 1968 ... 1969 enum v4l2_memory memoryType_ = V4L2_MEMORY_USERPTR; 1970 enum v4l2_buf_type bufferType_ = V4L2_BUF_TYPE_PRIVATE; 1971 }; 1972 1973#### PipeLineCore 1974 1975这个模块解析HCS配置完成pipeline的搭建,调度pipeline中的各个node完成流的处理 1976![图5](../figures/khdvk_3566b_pipelinecore.png) 1977 1978###IPP算法加载 1979IPP是pipeline 中的一个算法插件模块,由ippnode加载,对流数据进行算法处理,ippnode支持同时多路数据输入,只支持一路数据输出。 1980 1981vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/ipp_algo_config.hcs为算法插件配置文件,后面有新的算法库需要在这里添加相关内容,添加模板如下: 1982 1983 root { 1984 module="sample"; 1985 ipp_algo_config { 1986 algo1 { 1987 name = "example"; 1988 description = "example algorithm"; 1989 path = "libcamera_ipp_algo_example.z.so"; 1990 mode = "IPP_ALGO_MODE_NORMAL"; 1991 } 1992 } 1993 } 1994 1995name:算法插件名称 description:描述算法插件的功能 path:算法插件所在路径 mode:算法插件所运行的模式 1996 1997算法插件可运行的模式由 drivers/peripheral/camera/hal/pipeline_core/ipp/include/ipp_algo.h中的IppAlgoMode提供,可以根据需要进行扩展。 1998 1999 enum IppAlgoMode { 2000 IPP_ALGO_MODE_BEGIN, 2001 IPP_ALGO_MODE_NORMAL = IPP_ALGO_MODE_BEGIN, 2002 IPP_ALGO_MODE_BEAUTY, 2003 IPP_ALGO_MODE_HDR, 2004 IPP_ALGO_MODE_END 2005 }; 2006 2007算法插件由device/board/kaihong/khdvk_3566b/camera/BUILD.gn文件进行编译,算法插件需实现如下接口(接口由ipp_algo.h指定)供ippnode调用: 2008 2009 typedef struct IppAlgoFunc { 2010 int (*Init)(IppAlgoMeta* meta); 2011 int (*Start)(); 2012 int (*Flush)(); 2013 int (*Process)(IppAlgoBuffer* inBuffer[], int inBufferCount, IppAlgoBuffer* outBuffer, IppAlgoMeta* meta); 2014 int (*Stop)(); 2015 } IppAlgoFunc; 2016 2017Init : 算法插件初始化接口,在起流前被ippnode调用,其中IppAlgoMeta定义在ipp_algo.h 中,为ippnode和算法插件提供非图像数据的传递通道,如当前运行的场景,算法处理后输出的人脸坐标等等,可根据实际需求进行扩展 2018 2019Start:开始接口,起流时被ippnode调用 2020 2021Flush:刷新数据的接口,停流之前被ippnode调用。此接口被调用时,算法插件需尽可能快地停止处理 2022 2023Process: 数据处理接口,每帧数据都通过此接口输入至算法插件进行处理。inBuffer是一组输入buffer,inBufferCount是输入buffer的个数,outBuffer是输出buffer,meta是算法处理时产生的非图像数据,IppAlgoBuffer在ipp_algo.h中定义 2024 2025Stop:停止处理接口,停流时被ippnode调用 2026 2027下边代码中的id指的是和ippnode对应的port口id,比如inBuffer[0]的id为0,则对应的是ippnode 的第0个输入port口。需要注意的是outBuffer可以为空,此时其中一个输入buffer 被ippnode作为输出buffer传递到下个node,inBuffer至少有一个buffer不为空。输入输出buffer 由pipeline配置决定。 比如在普通预览场景无算法处理且只有一路拍照数据传递到ippnode的情况下,输入buffer只有一个,输出buffer为空,即对于算法插件输入buffer 进行了透传; 比如算法插件进行两路预览图像数据进行合并的场景,第一路buffer需要预览送显示。把第二路图像拷贝到第一路的buffer即可,此时输入buffer有两个,输出buffer为空; 比如在算法插件中进行预览数据格式转换的场景,yuv转换为RGBA,那么只有一个yuv格式的输入buffer的情况下无法完成RGBA格式buffer的输出,此时需要一个新的buffer,那么ippnode的输出port口buffer作为outBuffer传递到算法插件。也即输入buffer只有一个,输出buffer也有一个。 2028 2029 typedef struct IppAlgoBuffer { 2030 void* addr; 2031 unsigned int width; 2032 unsigned int height; 2033 unsigned int stride; 2034 unsigned int size; 2035 int id; 2036 } IppAlgoBuffer; 2037 2038##camera HDF驱动适配 2039###rk3566rp camera HDF驱动编译选项添加 2040camera HDF驱动的配置位于drivers/peripheral/camera/hal/camera.gni中,内容如下: 2041 2042 if (defined(ohos_lite)) { 2043 import("//build/lite/config/component/lite_component.gni") 2044 import("//device/board/hisilicon/hispark_taurus/device.gni") 2045 } else { 2046 import("//build/ohos.gni") 2047 import("//vendor/$product_company/$product_name/product.gni") 2048 } 2049 2050 camera_path = "//drivers/peripheral/camera/hal" 2051 current_path = "." 2052 enable_camera_device_utest = false 2053 2054 use_hitrace = false 2055 if (use_hitrace) { 2056 defines += [ "HITRACE_LOG_ENABLED" ] 2057 } 2058 2059 if (defined(ohos_lite)) { 2060 defines += [ "CAMERA_BUILT_ON_OHOS_LITE" ] 2061 } 2062 2063根据编译配置可以找到对应的vendor/kaihong/khdvk_3566b/product.gni,从中获取到实际的文件是device/board/kaihong/khdvk_3566b/device.gni,后面修改入口基于这里 2064 2065 soc_company = "rockchip" 2066 soc_name = "rk3566" 2067 2068 import("//device/soc/${soc_company}/${soc_name}/soc.gni") 2069 2070 import("//build/ohos.gni") 2071 if (!defined(defines)) { 2072 defines = [] 2073 } 2074 product_config_path = "//vendor/${product_company}/${device_name}" 2075 board_camera_path = "//device/board/${product_company}/khdvk_3566b/camera" 2076 2077 camera_product_name_path = "//vendor/${product_company}/${device_name}" 2078 camera_device_name_path = "//device/board/${product_company}/khdvk_3566b" 2079 is_support_v4l2 = true 2080 if (is_support_v4l2) { 2081 is_support_mpi = false 2082 defines += [ "SUPPORT_V4L2" ] 2083 chipset_build_deps = "$camera_device_name_path/camera:chipset_build" 2084 camera_device_manager_deps = "$camera_device_name_path/camera/device_manager:camera_device_manager" 2085 camera_pipeline_core_deps = "$camera_device_name_path/camera/pipeline_core:camera_pipeline_core" 2086 } 2087 2088最终这里的配置文件里的参数将被drivers/peripheral/camera/hal/BUILD.gn使用。 2089###HCS配置文件介绍 2090camera的配置文件位于vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/ 2091 2092目录结构如下: 2093 2094 ├── hdi_impl 2095 │ ├── camera_host_config.hcs 2096 └── pipeline_core 2097 ├── config.hcs 2098 ├── ipp_algo_config.hcs 2099 └── params.hcs 2100 2101Camera所有配置文件使用系统支持的HCS类型的配置文件,HCS类型的配置文件,在编译时,会转成HCB文件,最终烧录到开发板里的配置文件即为HCB格式,代码中通过HCS解析接口解析HCB文件,获取配置文件中的信息。 2102 2103 ohos_prebuilt_etc("camera_host_config.hcb") { 2104 deps = [ ":build_camera_host_config" ] 2105 hcs_outputs = get_target_outputs(":build_camera_host_config") 2106 source = hcs_outputs[0] 2107 relative_install_dir = "hdfconfig" 2108 install_images = [ chipset_base_dir ] 2109 subsystem_name = "hdf" 2110 part_name = "camera_device_driver" 2111 } 2112 2113camera_host_config.hcs:配置当前camera支持的能力集,物理/逻辑Camera配置、能力配置,此处的物理/逻辑Camera配置,需要在hal内部使用,逻辑Camera及能力配置需要上报给上层,这里需要根据设备实际支持的属性进行相应的修改。 2114这里的键值对参考文件drivers/peripheral/camera/hal/hdi_impl/include/camera_host/metadata_enum_map.h 2115 2116 ability_01 :: ability { 2117 logicCameraId = "lcam001"; 2118 physicsCameraIds = [ 2119 "CAMERA_FIRST", 2120 "CAMERA_SECOND" 2121 ]; 2122 metadata { 2123 aeAvailableAntiBandingModes = [ 2124 "OHOS_CAMERA_AE_ANTIBANDING_MODE_OFF" 2125 ]; 2126 aeAvailableModes = ["OHOS_CAMERA_AE_MODE_OFF"]; 2127 availableFpsRange = [30, 30]; 2128 cameraPosition = "OHOS_CAMERA_POSITION_FRONT"; 2129 cameraType = "OHOS_CAMERA_TYPE_WIDE_ANGLE"; 2130 cameraConnectionType ="OHOS_CAMERA_CONNECTION_TYPE_BUILTIN"; 2131 faceDetectMaxNum = "10"; 2132 aeCompensationRange = [0, 0]; 2133 aeCompensationSteps = [0, 0]; 2134 availableAwbModes = [ 2135 "OHOS_CAMERA_AWB_MODE_OFF" 2136 ]; 2137 ... 2138 } 2139 2140vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/config.hcs为pipeline的连接方式,按场景划分每一路流由哪些Node组成,其连接方式是怎样的。 2141 2142 normal_preview :: pipeline_spec { 2143 name = "normal_preview"; 2144 v4l2_source :: node_spec { 2145 name = "v4l2_source#0"; 2146 status = "new"; 2147 out_port_0 :: port_spec { 2148 name = "out0"; 2149 peer_port_name = "in0"; 2150 peer_port_node_name = "sink#0"; 2151 direction = 1; 2152 width = 0; 2153 height = 0; 2154 format = 0; 2155 } 2156 } 2157 sink :: node_spec { 2158 name = "sink#0"; 2159 status = "new"; 2160 stream_type = "preview"; 2161 in_port_0 :: port_spec { 2162 name = "in0"; 2163 peer_port_name = "out0"; 2164 peer_port_node_name = "v4l2_source#0"; 2165 direction = 0; 2166 } 2167 } 2168 } 2169 2170上面为preview场景的示例,normal_preview为该场景的名称,source和sink为Node,source为数据数据源端,sink为末端,source为第一个node,node的名称是source#0,status、in/out_port分别为Node状态及输入/输出口的配置。 2171 2172以in_port_0为例,name = “in0”代表它的输入为“port0”,它的对端为source node的port口out0口,direction为它的源Node和对端Node是否为直连方式。如新添加芯片产品,必须按实际连接方式配置此文件。 2173 2174新增功能node时需继承NodeBase类,且在cpp文件中注册该node。具体可参考//drivers/peripheral/camera/hal/pipeline_core/nodes/src下已经实现的node。 2175 2176vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/param.hcs为场景、流类型名及其id定义,pipeline内部是以流id区分流类型的,所以此处需要添加定义。 2177 2178 root { 2179 module = ""; 2180 template stream_info { 2181 id = 0; 2182 name = ""; 2183 } 2184 template scene_info { 2185 id = 0; 2186 name = ""; 2187 } 2188 priview :: stream_info { 2189 id = 0; 2190 name = "preview"; 2191 } 2192 video :: stream_info { 2193 id = 1; 2194 name = "video"; 2195 } 2196 snapshot :: stream_info { 2197 id = 2; 2198 name = "snapshot"; 2199 } 2200 normal :: scene_info { 2201 id = 0; 2202 name = "normal"; 2203 } 2204 dual :: scene_info { 2205 id = 1; 2206 name = "dual"; 2207 } 2208 } 2209 2210##适配过程中遇到的问题 2211###camera启动时无法出图排查方向 2212 2213首先排查camera sensor有没有正常的上下电,初始化序列是否正确。 2214如果上述都正常,需要到HDF层面,看看设备配置是否正确,具体操作如下: 2215在ohos系统的上电启动过程中,camera host 服务进程调用InitSensors() -->SensorController::Init()-->HosV4L2Dev::Init()->HosFileFormat::V4L2MatchDevice()既ohos在初始化过程中就会去匹配camera实例与linux 驱动系统中的camera硬件,如果匹配则记录存下cameraId与/dev/videox的关系;所以在camera drive中一般需要修改的地方就是camera hardware的name与linux驱动的/dev/videox关系; 2216代码如下: 2217cameraIDs向量组内是hdf支持的所以camera 的名称(string); 2218./drivers/peripheral/camera/hal/adapter/platform/v4l2/src/device_manager/include/v4l2_device_manager.h定义的cameraId 2219 2220 std::vector<HardwareConfiguration> hardware = { 2221 {CAMERA_FIRST, DM_M_SENSOR, DM_C_SENSOR, (std::string) "bm2835 mmal"}, 2222 {CAMERA_FIRST, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, 2223 {CAMERA_FIRST, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"}, 2224 {CAMERA_SECOND, DM_M_SENSOR, DM_C_SENSOR, (std::string) "Imx600"}, 2225 {CAMERA_SECOND, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, 2226 {CAMERA_SECOND, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"} 2227 }; 2228 2229每个名称应与/dev/videox其中任意一个的capabilities中的driver name是一样的,只有一样的名称才能将hdf的camera name与/dev/videox绑定; 2230 2231 void HosFileFormat::V4L2MatchDevice(std::vector<std::string>& cameraIDs) 2232 { 2233 struct stat st = {}; 2234 char devName[16] = {0}; 2235 std::string name = DEVICENAMEX; 2236 int fd = 0; 2237 int rc = 0; 2238 2239 for (auto &it : cameraIDs) { 2240 for (int i = 0; i < MAXVIDEODEVICE; ++i) { 2241 if ((sprintf_s(devName, sizeof(devName), "%s%d", name.c_str(), i)) < 0) { 2242 CAMERA_LOGE("%s: sprintf devName failed", __func__); 2243 } 2244 ... 2245 rc = V4L2GetCapability(fd, devName, it); 2246 if (rc == RC_ERROR) { 2247 close(fd); 2248 continue; 2249 } 2250 ... 2251 } 2252 } 2253 } 2254 2255注意“(cameraId != std::string((char*)cap.driver)”比较cap中的名称是否相同。 2256 2257 RetCode HosFileFormat::V4L2GetCapability(int fd, const std::string& devName, std::string& cameraId) 2258 { 2259 struct v4l2_capability cap = {}; 2260 2261 int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap); 2262 if (rc < 0) { 2263 return RC_ERROR; 2264 } 2265 2266 if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 2267 return RC_ERROR; 2268 } 2269 2270 if (!((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) || (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))) { 2271 return RC_ERROR; 2272 } 2273 2274 if (cameraId != std::string((char*)cap.driver)) { 2275 return RC_ERROR; 2276 } 2277 2278 std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_); 2279 HosV4L2Dev::deviceMatch.insert(std::make_pair(std::string((char*)cap.driver), devName)); 2280 ... 2281 return RC_OK; 2282 } 2283 2284## **BT** 2285 2286### **HCI接口** 2287 2288蓝牙整体硬件架构上分为主机(计算机或MCU)和主机控制器(BT蓝牙模组)两部分;通信遵循主机控制器接口(HCI),通常使用串口进行通信,如下所示: 2289 2290![img](../figures/khdvk_3566b_bt_1.png) 2291 2292HCI定义了如何交换命令,事件,异步和同步数据包。异步数据包(ACL)用于数据传输,而同步数据包(SCO)用于带有耳机和免提配置文件的语音。 2293 2294#### **硬件连接** 2295 2296从RK3566芯片描述中看,该芯片并不没有集成WIFI/蓝牙功能,都需要外接蓝牙芯片才能支持蓝牙功能,这也符合上述逻辑架构。串口使用普通带流控串口即可,一般在原理图中可以看到对应的串口引脚: 2297 2298![img](../figures/khdvk_3566b_bt_2.png) 2299 2300可以看到使用的是UART1 M0,在设备树里就要使能对应的串口和pinctrl,同时还可以看到有几个管脚分别做电源和休眠控制。 2301 2302``` 2303 wireless_bluetooth: wireless-bluetooth { 2304 compatible = "bluetooth-platdata"; 2305 clocks = <&rk817 1>; 2306 clock-names = "ext_clock"; 2307 //wifi-bt-power-toggle; 2308 uart_rts_gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>; 2309 pinctrl-names = "default", "rts_gpio"; 2310 pinctrl-0 = <&uart1m0_rtsn &bt_host_wake_gpio &bt_poweren &bt_host_wake_irq>; 2311 pinctrl-1 = <&uart1_gpios>; 2312 BT,reset_gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; 2313 BT,wake_gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>; 2314 BT,wake_host_irq = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>; 2315 status = "okay"; 2316 }; 2317 2318 wireless-bluetooth { 2319 uart1_gpios: uart1-gpios { 2320 rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; 2321 }; 2322 2323 bt_host_wake_irq: bt-host-wake-irq { 2324 rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_down>; 2325 }; 2326 bt_host_wake_gpio: bt-host-wake-gpio { 2327 rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>; 2328 }; 2329 bt_poweren: bt-poweren { 2330 rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>; 2331 }; 2332 }; 2333 2334 2335 &uart1 { 2336 status = "okay"; 2337 pinctrl-names = "default"; 2338 pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>; 2339 }; 2340``` 2341 2342### **蓝牙VENDORLIB适配** 2343 2344#### **vendorlib是什么** 2345 2346vendorlib部署在主机侧,可以认为是主机侧对蓝牙芯片驱动层,屏蔽不同蓝牙芯片的技术细节。从代码层面解读,其主要功能有两个: 2347 23481、为协议栈提供蓝牙芯片之间的通道(串口的文件描述符) 2349 23502、提供特定芯片的具体控制方法 2351 2352#### **代码层面解读vendorlib** 2353 2354bt_vendor_lib.h 路径: 2355 2356``` 2357foundation/communication/bluetooth/services/bluetooth_standard/hardware/include 2358``` 2359 2360该文件定义了协议栈和vendor_lib交互接口,分为两组: 2361 23621、 vendorlib实现,协议栈调用 2363 2364``` 2365 typedef struct { 2366 /** 2367 \* Set to sizeof(bt_vndor_interface_t) 2368 */ 2369 size_t size; 2370 /** 2371 \* Caller will open the interface and pass in the callback routines 2372 \* to the implemenation of this interface. 2373 */ 2374 int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr); 2375 2376 /** 2377 \* Vendor specific operations 2378 */ 2379 int (*op)(bt_opcode_t opcode, void* param); 2380 2381 /** 2382 \* Closes the interface 2383 */ 2384 void (*close)(void); 2385 } bt_vendor_interface_t; 2386``` 2387 2388协议栈启动时的基本流程如下: 2389 23901.1、协议栈动态打开libbt_vendor.z.so,并调用init函数,初始化vendorlib 2391 23921.2、协议栈调用op函数,分别调用BT_OP_POWER_ON、BT_OP_HCI_CHANNEL_OPEN、BT_OP_INIT三个opcode;原则上BT_OP_INIT成功后说明芯片初始化完成。 2393 23942、协议栈实现,vendorlib调用(回调函数) 2395 2396``` 2397 typedef struct { 2398 /** 2399 \* set to sizeof(bt_vendor_callbacks_t) 2400 */ 2401 size_t size; 2402 2403 /* notifies caller result of init request */ 2404 init_callback init_cb; 2405 2406 /* buffer allocation request */ 2407 malloc_callback alloc; 2408 2409 /* buffer free request */ 2410 free_callback dealloc; 2411 2412 /* hci command packet transmit request */ 2413 cmd_xmit_callback xmit_cb; 2414 } bt_vendor_callbacks_t; 2415``` 2416 2417init_cb在BT_OP_INIT完成后调用 2418 2419alloc/dealloc用于发送HCI消息时申请/释放消息控件 2420 2421xmit_cb发送HCI Commands 2422 2423##### vendor_lib实现的几个重要函数 2424 24251、 init函数 2426 2427``` 2428 static int init(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr) 2429 { 2430 /* * ... */ 2431 userial_vendor_init(); 2432 upio_init(); 2433 2434 vnd_load_conf(VENDOR_LIB_CONF_FILE); 2435 2436 /* store reference to user callbacks */ 2437 bt_vendor_cbacks = (bt_vendor_callbacks_t *)p_cb; 2438 /* This is handed over from the stack */ 2439 return memcpy_s(vnd_local_bd_addr, BD_ADDR_LEN, local_bdaddr, BD_ADDR_LEN); 2440 } 2441``` 2442 2443vendorlib被调用的第一个函数,vendorlib保存好协议栈的callback和mac地址即可。 2444 24452、 BT_OP_POWER_ON对应处理 2446 2447观名知意,这个操作理论上需要拉高电源管脚电平;该函数中使用rfill设备来处理,并没有直接调用驱动拉高电平 2448 2449``` 2450 int upio_set_bluetooth_power(int on) 2451 { 2452 int sz; 2453 int fd = -1; 2454 int ret = -1; 2455 char buffer = '0'; 2456 2457 switch (on) { 2458 case UPIO_BT_POWER_OFF: 2459 buffer = '0'; 2460 break; 2461 2462 case UPIO_BT_POWER_ON: 2463 buffer = '1'; 2464 break; 2465 default: 2466 return 0; 2467 } 2468 2469 /* check if we have rfkill interface */ 2470 if (is_rfkill_disabled()) { 2471 return 0; 2472 } 2473 2474 if (rfkill_id == -1) { 2475 if (init_rfkill()) { 2476 return ret; 2477 } 2478 } 2479 2480 fd = open(rfkill_state_path, O_WRONLY); 2481 if (fd < 0) { 2482 return ret; 2483 } 2484 2485 sz = write(fd, &buffer, 1); 2486 /* ... */ 2487 return ret; 2488 } 2489``` 2490 24913、BT_OP_HCI_CHANNEL_OPEN对应处理 2492 2493``` 2494 case BT_OP_HCI_CHANNEL_OPEN: { // BT_VND_OP_USERIAL_OPEN 2495 int(*fd_array)[] = (int(*)[])param; 2496 int fd, idx; 2497 fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg); 2498 if (fd != -1) { 2499 for (idx = 0; idx < HCI_MAX_CHANNEL; idx++) 2500 (*fd_array)[idx] = fd; 2501 retval = 1; 2502 } 2503 /* retval contains numbers of open fd of HCI channels */ 2504 break; 2505``` 2506 2507userial_vendor_open函数打开串口设备(UART)得到文件描述符(fd),通过op的参数param返回该fd 2508 2509该串口设备在系统中的名字在vendor下的bluetooth相关目录中的bt_vendor_brcm.h文件定义了,本次开发板上设备为/dev/ttyS1 2510 25114、BT_OP_INIT对应处理 2512 2513该操作码要求对蓝牙芯片进行初始化,具体要进行的处理和蓝牙芯片强相关。以本次调测的AP6xxx芯片为例,初始化过程中主要是下发蓝牙固件。 2514 2515初始化结束后,必须调用init_cb回调函数(参见bt_vendor_callbacks_t)通知协议栈初始化结果,否则会阻塞协议栈线程导致蓝牙相关功能无法正常使用。协议栈的具体处理如下: 2516 2517协议栈调用BT_OP_INIT后会等待信号量,该信号量由init_cb函数置位 2518 2519``` 2520 static int HciInitHal() 2521 { 2522 int result = BT_NO_ERROR; 2523 2524 g_waitHdiInit = SemaphoreCreate(0); 2525 int ret = g_hdiLib->hdiInit(&g_hdiCallbacks); 2526 if (ret == SUCCESS) { 2527 SemaphoreWait(g_waitHdiInit); 2528 } 2529 } 2530``` 2531 2532#### **vendorlib移植问题** 2533 25341、 vendorlib的so命名 2535 2536vendorlib必须是libbt_vendor.z.so;因为协议栈打开动态链接库就是这个名字 2537 25382、 固件问题 2539 2540开发时一定要关注芯片固件,有些蓝牙芯片可能无需升级固件,有些则必须升级固件, 不同型号的蓝牙对应固件也不一样;本次AP6xxx适配过程中最开始没有下发固件,导致蓝牙接收信号很差。固件下发时需要注意如下两点: 2541 25422.1、对于AP6xxx芯片,因为蓝牙芯片内并没有类似flash存储,要求芯片上下电后必须重新下发,固件要通过BUILD.gn把固件标记为prebuilt_etc 2543 2544``` 2545 ohos_prebuilt_etc("BCM43430A1.hcd") { 2546 source = "//vendor/kaihong/RK3566-xx/bluetooth/BCM43430A1.hcd" 2547 install_images = [ vendor_base_dir ] 2548 relative_install_dir = "firmware" 2549 part_name = "kaihong_products" 2550 install_enable = true 2551 } 2552``` 2553 2554然后在device/kaihong/build中把固件打包在镜像中 2555 2556``` 2557 "//vendor/kaihong/RK3566-xx/bluetooth:libbt_vendor", 2558 "//vendor/kaihong/RK3566-xx/bluetooth:BCM43430A1.hcd", 2559``` 2560 25612.2、按照芯片本身的要求处理,最好能找到厂商的参考代码;以Broadcom系列芯片为例,其固件下发过程比较复杂,通过一个状态机驱动;共如下9个状态 2562 2563``` 2564 / Hardware Configuration State */ 2565 enum { 2566 HW_CFG_START = 1, 2567 HW_CFG_SET_UART_CLOCK, 2568 HW_CFG_SET_UART_BAUD_1, 2569 HW_CFG_READ_LOCAL_NAME, 2570 HW_CFG_DL_MINIDRIVER, 2571 HW_CFG_DL_FW_PATCH, 2572 HW_CFG_SET_UART_BAUD_2, 2573 HW_CFG_SET_BD_ADDR, 2574 HW_CFG_READ_BD_ADDR 2575 }; 2576``` 2577 2578在收到BT_OP_INIT后初始化状态机,然后发送HCI_REST命令,切换状态为HW_CFG_START; 2579 2580``` 2581 void hw_config_start(void) 2582 { 2583 HC_BT_HDR *p_buf = NULL; 2584 uint8_t *p; 2585 hw_cfg_cb.state = 0; 2586 hw_cfg_cb.fw_fd = -1; 2587 hw_cfg_cb.f_set_baud_2 = FALSE; 2588 2589 if (bt_vendor_cbacks) { 2590 p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + 2591 HCI_CMD_PREAMBLE_SIZE); 2592 } 2593 2594 if (p_buf) { 2595 p_buf->event = MSG_STACK_TO_HC_HCI_CMD; 2596 p_buf->offset = 0; 2597 p_buf->layer_specific = 0; 2598 p_buf->len = HCI_CMD_PREAMBLE_SIZE; 2599 2600 p = (uint8_t *)(p_buf + 1); 2601 UINT16_TO_STREAM(p, HCI_RESET); 2602 *p = 0; 2603 2604 hw_cfg_cb.state = HW_CFG_START; 2605 bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf); 2606 } else { 2607 if (bt_vendor_cbacks) { 2608 HILOGE("vendor lib fw conf aborted [no buffer]"); 2609 bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL); 2610 } 2611 } 2612 } 2613``` 2614 2615收到芯片返回的HCI_RESET完成事件后,继续切换到下一个状态机并发送下一个COMMAND,一直到状态机完成固件下发。 2616 2617详细实现请参见hw_config_cback函数。 2618 26193、 关注系统间接口差异 2620 2621不同系统的接口可能有一些细微差异,需要重点关注;对比安卓和OHOS的接口,vendorlib调用xmit_cb发送HCI命令的函数定义略有差异 2622 2623安卓: 2624 2625``` 2626 /* define callback of the cmd_xmit_cb 2627 * 2628 2629 The callback function which HCI lib will call with the return of command 2630 2631 complete packet. Vendor lib is responsible for releasing the buffer passed 2632 2633 in at the p_mem parameter by calling dealloc callout function. 2634 */ 2635 typedef void (*tINT_CMD_CBACK)(void* p_mem); 2636 typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void* p_buf, tINT_CMD_CBACK p_cback); 2637 OHOS: 2638 /** 2639 2640 hci command packet transmit callback 2641 2642 Vendor lib calls cmd_xmit_cb function in order to send a HCI Command 2643 2644 packet to BT Controller. 2645 * 2646 2647 The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of 2648 2649 HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command 2650 2651 packet. */ 2652 2653 typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf); 2654``` 2655 2656也就是说vendorlib中发送命令后,安卓会直接调用callback通知芯片返回的消息,OHOS则是通过BT_OP_EVENT_CALLBACK操作码(参见bt_opcode_t定义)通知芯片返回的消息;vendorlib需要解析报文中的消息码确认芯片是处理的哪个消息,然后调用对应的处理函数。 2657 2658``` 2659 void hw_process_event(HC_BT_HDR *p_buf) 2660 { 2661 uint16_t opcode; 2662 uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; 2663 STREAM_TO_UINT16(opcode, p); 2664 switch (opcode) { 2665 case HCI_VSC_WRITE_BD_ADDR: 2666 \#if (USE_CONTROLLER_BDADDR == TRUE) 2667 case HCI_READ_LOCAL_BDADDR: 2668 \#endif 2669 case HCI_READ_LOCAL_NAME: 2670 case HCI_VSC_DOWNLOAD_MINIDRV: 2671 case HCI_VSC_WRITE_FIRMWARE: 2672 case HCI_VSC_LAUNCH_RAM: 2673 case HCI_RESET: 2674 case HCI_VSC_WRITE_UART_CLOCK_SETTING: 2675 case HCI_VSC_UPDATE_BAUDRATE: 2676 hw_config_cback(p_buf); 2677 break; 2678``` 2679 2680另外,OHOS返回的是发送消息的字节数,<=0为发送失败,和安卓接口的返回值也不同 2681 26824、 btvendor日志 2683 2684在vendor下的bluetooth/include相关目录里的Log.h中定义log文件的保存路径,我们代码里生成文件为/data/btvendor.log。也可以通过wireshark或其它报文分析工具可以看到Host和Controller之间的交互流程,有助于问题分析。 2685 2686## WIFI 2687 2688#### **整改思路及实现流程** 2689 2690##### 整改思路 2691 2692主要参考https://mp.weixin.qq.com/s/iiE97pqPtzWIZadcjrQtsw 《OpenHarmony HDF WLAN驱动分析与使用》这篇文章,熟悉HDF WLAN的框架以及需要实现的主要接口,包括HDF驱动初始化接口、WLAN控制侧接口集、AP模式接口集、STA模式接口集、网络侧接口集、事件上报接口的实现。接下来熟悉HCS文件的格式以及"HDF WIFI”核心驱动框架的代码启动初始化过程,参考hi3881的代码进行改造。 2693 2694##### **HDF WiFi框架总体框架图** 2695 2696WLAN驱动架构组成: 2697 2698![img](../figures/khdvk_3566b_wifi_1.png) 2699 2700#### ap6256驱动代码流程分析 2701 2702##### **驱动模块初始化流程分析** 2703 2704![img](../figures/khdvk_3566b_wifi_2.png) 2705 2706Ap6256 是一款SDIO设备WiFi模组驱动,使用标准Linux的SDIO设备驱动。内核模块初始化入口module_init()调用dhd_wifi_platform_load_sdio()函数进行初始化工作,这里调用wifi_platform_set_power()进行GPIO上电,调用dhd_wlan_set_carddetect()进行探测SDIO设备卡,最后调用sdio_register_driver(&bcmsdh_sdmmc_driver);进行SDIO设备驱动的注册,SDIO总线已经检测到WiFi模块设备,根据设备号和厂商号与该设备驱动匹配, 所以立即回调该驱动的bcmsdh_sdmmc_probe()函数,这里进行WiFi模组芯片的初始化工作,最后创建net_device网络接口wlan0,然后注册到Linux内核协议栈中。 2707 2708下面对其中比较重要的函数进行举例分析: 2709 2710(1) dhd_bus_register函数,主要实现sdio设备的注册,通过回调dhd_sdio中的相关函数,对wifi模块进行驱动注册等相关操作。 2711 2712![img](../figures/khdvk_3566b_wifi_3.png) 2713 2714其中函数bcmsdh_register将静态结构体变量dhd_sdio赋值给静态结构体drvinfo,然后通过函数bcmsdh_register_client_driver调用函数sdio_register_driver向系统注册sdio接口驱动。 2715 2716![img](../figures/khdvk_3566b_wifi_4.png) 2717 2718![img](../figures/khdvk_3566b_wifi_5.png) 2719 2720当sdio设备与sdio总线进行匹配后,会回调函数bcmsdh_sdmmc_probe,函数bcmsdh_sdmmc_probe会进一步回调dhd_sdio结构体中的成员函数dhdsdio_probe。 2721 2722(2) dhdsdio_probe函数,主要实现net_device对象(wlan0)的创建,以及wireless_dev对象创建,并与net_device对象的成员ieee80211_ptr进行关联,给net_device对象的操作方法成员netdev_ops赋值,最后将net_device对象注册到协议栈中。 2723 2724- 创建net_device网络接口wlan0对象 2725 2726dhd_allocate_if()会调用alloc_etherdev()创建net_device对象,即wlan0网络接口。wl_cfg80211_attach()会创建wireless_dev对象,并将wireless_dev对象赋值给net_device对象的成员ieee80211_ptr。 2727 2728- 将wlan0注册到内核协议栈 2729 2730调用dhd_register_if()函数,这里将net_device_ops操作方法的实例dhd_ops_pri赋值给net_device对象的成员netdev_ops,然后调用register_netdev(net);将net_device对象wlan0网络接口注册到协议栈。 2731 2732#### 整改代码适配HDF WiFi框架 2733 2734对于系统WiFi功能的使用,需要实现AP模式、STA模式、P2P三种主流模式,这里使用wpa_supplicant应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现STA模式和P2P模式的功能,使用hostapd应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现AP模式和P2P模式的功能。 2735 2736Ap6256 WiFi6内核驱动依赖platform能力,主要包括SDIO总线的通讯能力;与用户态通信依赖HDF WiFi框架的能力,在确保上述能力功能正常后,即可开始本次WiFi驱动的HDF适配移植工作。本文档基于已经开源的rk3568开源版代码为基础版本,来进行此次移植。 2737 2738适配移植ap6256 WiFi驱动涉及到的文件和目录如下: 2739 27403.1 WIFI相关的HDF框架编译控制宏 2741 2742ap6256采用的是sdio总线,涉及到的通用编译控制宏如下: 2743 2744CONFIG_DRIVERS_HDF_PLATFORM_SDIO=y 2745 2746CONFIG_DRIVERS_HDF_PLATFORM_MMC=y 2747 2748CONFIG_DRIVERS_HDF_WIFI=y 2749 2750CONFIG_DRIVERS_HDF_STORAGE=y 2751 27523.2 具体WiFi设备驱动编译控制宏 2753 2754涉及到wifi设备驱动的编译控制宏位于drivers/adapter/khdf/linux/model/network/wifi/Kconfig中,其中主要涉及到编译控制宏如下: 2755 2756CONFIG_DRIVERS_HDF_NETDEV_EXT=y 2757 2758CONFIG_AP6XXX_WIFI6_HDF=y 2759 2760编译控制选项CONFIG_AP6XXX_WIFI6_HDF,内容如下: 2761 2762config AP6XXX_WIFI6_HDF 2763 2764 tristate "support ap6xxx wifi6(80211ax) HDF" 2765 2766 depends on DRIVERS_HDF_WIFI 2767 2768 select CFG80211 2769 2770 select MAC80211 2771 2772 select DRIVERS_HDF_NETDEV_EXT 2773 2774 help 2775 2776 This driver supports wifi6 for ap6xxx HDF chipset. 2777 2778 This driver uses the kernel's wireless extensions subsystem. 2779 2780 If you choose to build a module, it'll be called dhd. Say M if unsure. 2781 2782NOTE:此处为了保证框架侧与社区代码一致,不建议修改,设置CONFIG_AP6XXX_WIFI6_HDF的配置即可。 2783 27843.3 修改编译规则Makefile文件,添加ap6256驱动的源码位置 2785 2786在drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile文件,添加如下内容: 2787 2788ifneq ($(CONFIG_AP6XXX_WIFI6_HDF),) 2789 2790\#RKWIFI_PATH := $(HDF_VENDOR_PREFIX)/device/$(product_company)/$(product_device)/wifi 2791 2792RKWIFI_PATH := $(HDF_VENDOR_PREFIX)/device/kaihong/rk3568-khdvk/wifi //修改添加部分 2793 2794obj-$(CONFIG_AP6XXX_WIFI6_HDF) += $(RKWIFI_PATH)/ 2795 2796endif 2797 2798ap6256驱动源码就位于源码/device/kaihong/rk3568-khdvk/wifi中,另外再根据ap6256的编译规则,修改wifi中的Makefile。 2799 2800NOTE:此处也不建议修改,源码就位于device/$(product_company)/$(product_device)/wifi中,但此处不能获取$(product_company)与$(product_device)的值,还需要社区进行完善 2801 2802###### WiFi驱动源码目录 2803 2804驱动代码编译规则修改 2805 2806参考device/kaihong/rk3568-khdvk/wifi/Makefile文件,内容如下: 2807 2808obj-$(CONFIG_AP6XXX_WIFI6_HDF) += bcmdhd_hdf/ 2809 2810NOTE:可以修改目标规则指向不同的wifi驱动代码。 2811 2812原生驱动代码存放于: 2813 2814device/kaihong/rk3568-khdvk/patches/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/ 2815 2816在原生驱动上修改编译规则Makefile文件 2817 2818由于驱动中添加了HDF框架代码,其中涉及到头文件位于drivers目录中,需要将相关路径加入编译规则中,主要是修改两点: 2819 2820(1) 引用drivers/hdf/khdf/model/network/wifi/hdfwifi.mk中规则,在Makefile中添加语句如下: 2821 2822include drivers/hdf/khdf/model/network/wifi/hdfwifi.mk 2823 2824(2) 将hdfwifi.mk中涉及到的头文件定义添加到编译规则中,方便编译时引用,添加语句如下: 2825 2826EXTRA_CFLAGS += $(HDF_FRAMEWORKS_INC) \ 2827 2828 $(HDF_WIFI_FRAMEWORKS_INC) \ 2829 2830 $(HDF_WIFI_ADAPTER_INC) \ 2831 2832 $(HDF_WIFI_VENDOR_INC) \ 2833 2834 $(SECURE_LIB_INC) 2835 2836NOTE:如果有其他编译要求,可以修改Makefile中的相关规则 2837 28383.4.4 在原生驱动上增加以及修改的HDF驱动代码文件位于: 2839 2840device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/ 2841 2842目录结构: 2843 2844./device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf 2845 2846├── hdf_bdh_mac80211.c 2847 2848├── hdf_driver_bdh_register.c 2849 2850├── hdfinit_bdh.c 2851 2852├── hdf_mac80211_ap.c 2853 2854├── hdf_mac80211_sta.c 2855 2856├── hdf_mac80211_sta.h 2857 2858├── hdf_mac80211_sta_event.c 2859 2860├── hdf_mac80211_sta_event.h 2861 2862├── hdf_mac80211_p2p.c 2863 2864├── hdf_public_ap6256.h 2865 2866├── net_bdh_adpater.c 2867 2868├── net_bdh_adpater.h 2869 2870其中hdf_bdh_mac80211.c主要对g_bdh6_baseOps所需函数的填充,hdf_mac80211_ap.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_sta.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_p2p.c主要对g_bdh6_p2pOps所需函数进行填充,在drivers/framework/include/wifi/wifi_mac80211_ops.h里有对wifi基本功能所需api的说明。 2871 2872###### 驱动文件编写 2873 2874HDF WLAN驱动框架由Module、NetDevice、NetBuf、BUS、HAL、Client 和 Message 这七个部分组成。开发者在WiFi驱动HDF适配过程中主要实现以下几部分功能: 2875 2876适配HDF WLAN框架的驱动模块初始化 2877 2878代码流程框图如下: 2879 2880![img](../figures/khdvk_3566b_wifi_6.png) 2881 2882HDF代码入口 2883 2884HDF代码入口位于device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf_driver_bdh_register.c 2885 2886struct HdfDriverEntry g_hdfBdh6ChipEntry = { 2887 2888 .moduleVersion = 1, 2889 2890 .Bind = HdfWlanBDH6DriverBind, 2891 2892 .Init = HdfWlanBDH6ChipDriverInit, 2893 2894 .Release = HdfWlanBDH6ChipRelease, 2895 2896 .moduleName = "HDF_WLAN_CHIPS" 2897 2898}; 2899 2900HDF_INIT(g_hdfBdh6ChipEntry); 2901 29023.5.2 HDF驱动的注册 2903 2904在函数HDFWlanRegBDH6DriverFactory中完成HDF驱动的注册,相关代码如下: 2905 2906static int32_t HDFWlanRegBDH6DriverFactory(void) 2907 2908{ 2909 2910 static struct HdfChipDriverFactory BDH6Factory = { 0 }; // WiFi device chip driver 2911 2912 struct HdfChipDriverManager *driverMgr = NULL; 2913 2914 driverMgr = HdfWlanGetChipDriverMgr(); 2915 2916 if (driverMgr == NULL) { 2917 2918 HDF_LOGE("%s fail: driverMgr is NULL!", __func__); 2919 2920 return HDF_FAILURE; 2921 2922 } 2923 2924 BDH6Factory.driverName = BDH6_DRIVER_NAME; 2925 2926 BDH6Factory.GetMaxIFCount = GetBDH6GetMaxIFCount; 2927 2928 BDH6Factory.InitChip = InitBDH6Chip; 2929 2930 BDH6Factory.DeinitChip = DeinitBDH6Chip; 2931 2932 BDH6Factory.Build = BuildBDH6Driver; 2933 2934 BDH6Factory.Release = ReleaseBDH6Driver; 2935 2936 BDH6Factory.ReleaseFactory = NULL; 2937 2938 if (driverMgr->RegChipDriver(&BDH6Factory) != HDF_SUCCESS) { 2939 2940 HDF_LOGE("%s fail: driverMgr is NULL!", __func__); 2941 2942 return HDF_FAILURE; 2943 2944 } 2945 2946 return HDF_SUCCESS; 2947 2948} 2949 2950在注册HDF驱动时,需要实现HDF的基本操作,对struct HdfChipDriverFactory结构体进行初始化,struct HdfChipDriverFactory结构体的内容如下: 2951 2952struct HdfChipDriverFactory { 2953 2954 const char *driverName; /**< Driver name */ 2955 2956 int32_t (*InitChip)(struct HdfWlanDevice *device); 2957 2958 int32_t (*DeinitChip)(struct HdfWlanDevice *device); 2959 2960 void (*ReleaseFactory)(struct HdfChipDriverFactory *factory); 2961 2962 struct HdfChipDriver *(*Build)(struct HdfWlanDevice *device, uint8_t ifIndex); 2963 2964 void (*Release)(struct HdfChipDriver *chipDriver); 2965 2966 uint8_t (*GetMaxIFCount)(struct HdfChipDriverFactory *factory); 2967 2968}; 2969 2970相关函数接口说明: 2971 2972| 函数 | 功能 | 2973| -------------------- | ---------- | 2974| GetBDH6GetMaxIFCount | 无需实现具体操作 | 2975| InitBDH6Chip | 芯片初始化 | 2976| DeinitBDH6Chip | 芯片去初始化 | 2977| BuildBDH6Driver | 实现芯片驱动侧绑定 | 2978| ReleaseBDH6Driver | 释放WLAN芯片驱动 | 2979| ReleaseFactory | 无需实现 | 2980 29813.5.3 芯片驱动初始化 2982 2983芯片驱动初始化函数以及wifi相关的ap、sta、p2p操作函数的注册都在BuildBDH6Driver函数中实现,主要是实现struct HdfChipDriver结构体的初始化,struct HdfChipDriver结构体如下: 2984 2985struct HdfChipDriver { 2986 2987 uint16_t type; /**< Chip type */ 2988 2989 char name[MAX_WIFI_COMPONENT_NAME_LEN]; /**< Chip name */ 2990 2991 struct HdfMac80211BaseOps *ops; /**< MAC address for the basic feature */ 2992 2993 struct HdfMac80211STAOps *staOps; /**< MAC address for the STA feature */ 2994 2995 struct HdfMac80211APOps *apOps; /**< MAC address for the AP feature */ 2996 2997 struct HdfMac80211P2POps *p2pOps; /**< MAC address for the P2Pfeature */ 2998 2999 void *priv; /**< Private data of the chip driver */ 3000 3001 int32_t (*init)(struct HdfChipDriver *chipDriver, NetDevice *netDev); 3002 3003 int32_t (*deinit)(struct HdfChipDriver *chipDriver, NetDevice *netDev); 3004 3005}; 3006 30071)函数BuildBDH6Driver具体实现如下: 3008 3009static struct HdfChipDriver *BuildBDH6Driver(struct HdfWlanDevice *device, uint8_t ifIndex) 3010 3011{ 3012 3013 struct HdfChipDriver *specificDriver = NULL; 3014 3015 if (device == NULL) { 3016 3017 HDF_LOGE("%s fail : channel is NULL", __func__); 3018 3019 return NULL; 3020 3021 } 3022 3023 (void)device; 3024 3025 (void)ifIndex; 3026 3027 specificDriver = (struct HdfChipDriver *)OsalMemCalloc(sizeof(struct HdfChipDriver)); //分配结构体地址空间 3028 3029 if (specificDriver == NULL) { 3030 3031 HDF_LOGE("%s fail: OsalMemCalloc fail!", __func__); 3032 3033 return NULL; 3034 3035 } 3036 3037 if (memset_s(specificDriver, sizeof(struct HdfChipDriver), 0, sizeof(struct HdfChipDriver)) != EOK) { 3038 3039 HDF_LOGE("%s fail: memset_s fail!", __func__); 3040 3041 OsalMemFree(specificDriver); 3042 3043 return NULL; 3044 3045 } 3046 3047 if (strcpy_s(specificDriver->name, MAX_WIFI_COMPONENT_NAME_LEN, BDH6_DRIVER_NAME) != EOK) { 3048 3049 HDF_LOGE("%s fail : strcpy_s fail", __func__); 3050 3051 OsalMemFree(specificDriver); 3052 3053 return NULL; 3054 3055 } 3056 3057 specificDriver->init = BDH6Init; 3058 3059 specificDriver->deinit = BDH6Deinit; 3060 3061 HDF_LOGW("bdh6: call BuildBDH6Driver %p", specificDriver); 3062 3063 BDH6Mac80211Init(specificDriver); //wifi相关的ap、sta、p2p操作接口初始化赋值 3064 3065 return specificDriver; 3066 3067} 3068 30692)函数BDH6Mac80211Init实现wifi相关的ap、sta、p2p操作接口赋值到struct HdfChipDriver结构体中,具体实现如下 3070 3071void BDH6Mac80211Init(struct HdfChipDriver *chipDriver) 3072 3073{ 3074 3075 HDF_LOGE("%s: start...", __func__); 3076 3077 if (chipDriver == NULL) { 3078 3079 HDF_LOGE("%s: input is NULL", __func__); 3080 3081 return; 3082 3083 } 3084 3085 chipDriver->ops = &g_bdh6_baseOps; 3086 3087 chipDriver->staOps = &g_bdh6_staOps; 3088 3089 chipDriver->apOps = &g_bdh6_apOps; 3090 3091 chipDriver->p2pOps = &g_bdh6_p2pOps; 3092 3093} 3094 30953.5.4 Wifi芯片驱动初始化 3096 3097Wifi芯片驱动初始化过程,由函数BDH6Init实现,主要涉及到wlan0网络节点的注册与p2p0网络节点的注册,以及芯片驱动的初始化过程。 3098 3099整体流程如下: 3100 3101![img](../figures/khdvk_3566b_wifi_7.png) 3102 3103下面对涉及的重要函数代码进行列举: 3104 3105(1) 设置NetDevice对象的操作接口,函数主要通过全局结构体赋值给NetDevice对象的成员netDeviceIf指针来实现,具体代码如下: 3106 3107![img](../figures/khdvk_3566b_wifi_8.png) 3108 3109(2) 给NetDevice对象分配私有数据空间,具体实现如下: 3110 3111![img](../figures/khdvk_3566b_wifi_9.png) 3112 3113(3) 启动芯片初始化流程,请参考原生驱动的初始化流程,其中需要注意的是,需要进行wlan0的节点注册,代码在原生驱动函数dhd_register_if中进行实现,具体代码如下: 3114 3115![img](../figures/khdvk_3566b_wifi_10.png) 3116 3117(4) 创建p2p0的NetDevice对象,具体代码实现如下: 3118 3119![img](../figures/khdvk_3566b_wifi_11.png) 3120 3121(5) 重新设置p2p0的操作方法,并进行p2p0节点注册,具体代码实现如下: 3122 3123![img](../figures/khdvk_3566b_wifi_12.png) 3124 31253.5.5 HDF WlAN相关的控制接口 3126 3127HDF WlAN相关的控制接口主要涉及到HdfMac80211BaseOps、HdfMac80211STAOps、HdfMac80211APOps、HdfMac80211P2POps结构体,通过将以上结构体的全局变量赋值给struct HdfChipDriver结构体的ops、staOps、apOps、p2pOps成员来实现。 3128 31291)HDF WLAN Base控制侧接口的实现 3130 3131代码位于hdf_bdh_mac80211.c 3132 3133static struct HdfMac80211BaseOps g_bdh6_baseOps = { 3134 3135 .SetMode = BDH6WalSetMode, 3136 3137 .AddKey = BDH6WalAddKey, 3138 3139 .DelKey = BDH6WalDelKey, 3140 3141 .SetDefaultKey = BDH6WalSetDefaultKey, 3142 3143 .GetDeviceMacAddr = BDH6WalGetDeviceMacAddr, 3144 3145 .SetMacAddr = BDH6WalSetMacAddr, 3146 3147 .SetTxPower = BDH6WalSetTxPower, 3148 3149 .GetValidFreqsWithBand = BDH6WalGetValidFreqsWithBand, 3150 3151 .GetHwCapability = BDH6WalGetHwCapability, 3152 3153.SendAction = BDH6WalSendAction, 3154 3155 .GetIftype = BDH6WalGetIftype, 3156 3157}; 3158 3159上述实现的接口供STA、AP、P2P三种模式中所调用。 3160 31612)HDF WLAN STA模式接口的实现 3162 3163STA模式调用流程图如下: 3164 3165![img](../figures/khdvk_3566b_wifi_13.png) 3166 3167代码位于hdf_mac80211_sta.c 3168 3169struct HdfMac80211STAOps g_bdh6_staOps = { 3170 3171 .Connect = HdfConnect, 3172 3173 .Disconnect = HdfDisconnect, 3174 3175 .StartScan = HdfStartScan, 3176 3177 .AbortScan = HdfAbortScan, 3178 3179 .SetScanningMacAddress = HdfSetScanningMacAddress, 3180 3181}; 3182 31833) HDF WLAN AP模式接口的实现 3184 3185AP模式调用流程图如下: 3186 3187![img](../figures/khdvk_3566b_wifi_14.png) 3188 3189代码位于hdf_mac80211_ap.c 3190 3191struct HdfMac80211APOps g_bdh6_apOps = { 3192 3193 .ConfigAp = WalConfigAp, 3194 3195 .StartAp = WalStartAp, 3196 3197 .StopAp = WalStopAp, 3198 3199 .ConfigBeacon = WalChangeBeacon, 3200 3201 .DelStation = WalDelStation, 3202 3203 .SetCountryCode = WalSetCountryCode, 3204 3205 .GetAssociatedStasCount = WalGetAssociatedStasCount, 3206 3207 .GetAssociatedStasInfo = WalGetAssociatedStasInfo 3208 3209}; 3210 32114)HDF WLAN P2P模式接口的实现 3212 3213P2P模式调用流程图如下: 3214 3215![img](../figures/khdvk_3566b_wifi_15.png) 3216 3217struct HdfMac80211P2POps g_bdh6_p2pOps = { 3218 3219 .RemainOnChannel = WalRemainOnChannel, 3220 3221 .CancelRemainOnChannel = WalCancelRemainOnChannel, 3222 3223 .ProbeReqReport = WalProbeReqReport, 3224 3225 .AddIf = WalAddIf, 3226 3227 .RemoveIf = WalRemoveIf, 3228 3229 .SetApWpsP2pIe = WalSetApWpsP2pIe, 3230 3231 .GetDriverFlag = WalGetDriverFlag, 3232 3233}; 3234 32355) HDF WLAN框架事件上报接口的实现 3236 3237WiFi驱动需要通过上报事件给wpa_supplicant和hostapd应用程序,比如扫描热点结果上报,新STA终端关联完成事件上报等等,HDF WLAN事件上报的所有接口请参考drivers/framework/include/wifi/hdf_wifi_event.h: 3238 3239事件上报HDF WLAN接口主要有: 3240 3241| 头文件 | 接口名称 | 功能描述 | 3242| --------------------------------- | -------------------- | ----------- | 3243| hdf_wifi_event.h | HdfWifiEventNewSta() | 上报一个新的sta事件 | 3244| HdfWifiEventDelSta() | 上报一个删除sta事件 | | 3245| HdfWifiEventInformBssFrame() | 上报扫描Bss事件 | | 3246| HdfWifiEventScanDone() | 上报扫描完成事件 | | 3247| HdfWifiEventConnectResult() | 上报连接结果事件 | | 3248| HdfWifiEventDisconnected() | 上报断开连接事件 | | 3249| HdfWifiEventMgmtTxStatus() | 上报发送状态事件 | | 3250| HdfWifiEventRxMgmt() | 上报接受状态事件 | | 3251| HdfWifiEventCsaChannelSwitch() | 上报Csa频段切换事件 | | 3252| HdfWifiEventTimeoutDisconnected() | 上报连接超时事件 | | 3253| HdfWifiEventEapolRecv() | 上报Eapol接收事件 | | 3254| HdfWifiEventResetResult() | 上报wlan驱动复位结果事件 | | 3255| HdfWifiEventRemainOnChannel() | 上报保持信道事件 | | 3256| HdfWifiEventCancelRemainOnChannel | 上报取消保持信道事件 | | 3257 3258#### 所有关键问题总结 3259 3260##### 调试AP模块时,启动AP模式的方法 3261 3262调试AP模块时,无法正常开启AP功能的解决方法 3263 3264需要使用到busybox和hostapd配置ap功能,操作步骤如下: 3265 32661) ifconfig wlan0 up 3267 32682) ifconfig wlan0 192.168.12.1 netmask 255.255.255.0 3269 32703) ./busybox udhcpd /data/l2tool/udhcpd.conf 3271 32724) hostapd -d /data/l2tool/hostapd.conf 3273 3274调试STA模块时,启动STA模式的方法 3275 3276## 3277 3278NOTE:需要对busybox与dhcpc.sh设置成可执行权限 3279 3280##### 调试P2P模块时,启动P2P模式的方法 3281 3282调试P2P模块时,模块可以作为GO模式或者GC模式,区别在于配置文件不同,操作步骤如下: 3283 3284wpa_supplicant -i wlan0 -c /data/l2tool/p2p_supplicant.conf & 设置p2p模式 3285 3286wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_find 启动p2p查找 3287 3288wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_connect 06:86:29:e8:47:84 pbc 连接p2p设备 3289 3290./busybox udhcpc -ip2p-wlan0-0 -s /data/l2tool/dhcpc.sh 启动p2p-wlan0-0的dhcp获取地址 3291 3292NOTE:在GO模式下,连接上设备后,应该立即获取IP地址,否则,连接会自动断开。 3293 3294##### 扫描热点事件无法上报到wap_supplicant的解决办法 3295 3296wpa_supplicant 这个应用程序启动时不能加 -B参数后台启动,-B后台启动的话,调用poll()等待接收事件的线程会退出,所以无法接收上报事件, 3297 3298wpa_supplicant -iwlan0 -c /data/wpa_supplicant.conf & 这样后台启动就可以了。 3299 3300##### wpa2psk方式无法认证超时问题解决方法 3301 3302分析流程发现 hostapd没有接收到WIFI_WPA_EVENT_EAPOL_RECV = 13这个事件,原来是驱动没有将接收到的EAPOL报文通过HDF WiFi框架发送给hostapd进程,在驱动接收报文后,调用netif_rx()触发软中断前将EAPOL报文发送给HDF WiFi框架,认证通过了。 3303 3304##### P2P模式连接不成功问题定位分析 3305 3306在调试P2P连接接口时,发现手机P2P直连界面总是处于已邀请提示,无法连接成功,通过抓取手机和WiFi模组正常连接成功报文和HDF适配后连接失败的报文进行比对,在失败的报文组中,发现手机侧多回复了一帧ACTION报文,提示无效参数,然后终止了P2P连接。 3307 3308![img](../figures/khdvk_3566b_wifi_16.png) 3309 3310最后比对WiFi模组向手机发送的ACTION报文内容,发现填充的P2P Device Info的MAC地址值不对,如下: 3311 3312正确帧内容: 3313 3314![img](../figures/khdvk_3566b_wifi_17.png) 3315 3316错误帧内容: 3317 3318![img](../figures/khdvk_3566b_wifi_18.png) 3319 3320最后经过分析MAC地址的填充部分代码,这个MAC地址是wpa_supplicant 根据p2p0的MAC地址填充的,所以将wdev对象(即p2p-dev-wlan0)的MAC地址更新给p2p0接口,二者保持一致即可,见代码wl_get_vif_macaddr(cfg, 7, p2p_hnetdev->macAddr);的调用。 3321 3322#### 连接成功日志 3323 3324##### STA模式连接成功日志 3325 3326WPA: Key negotiation ccompleted with 50:eb:f6:02:8e6:d4 [PTK=CCMP GTK=CCMP] 3327 3328 06 wlan0: State: GROUP_HANDSHAKEc -> COMPLETED 3329 3330wlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=] 3331 3332WifiWpaReceid eEapol done 3333 3334##### AP模式连接成功日志 3335 3336wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port 3337 3338wlan0: STA 96:27:b3:95:b7:6e WPA: pairwiseb key handshake completed (RSN) 3339 3340WifiWpaReceiveEapol done 3341 3342##### P2P模式连接成功日志 3343 3344P2P: cli_channels: 3345 3346EAPOL: External notification - portValid=1 3347 3348EAPOL: External notifica:tion - EAP success=1 3349 3350EAPOL: SUPP_PAE entering state AUTHENTIwCATING 3351 3352EAPOL: SUPP_BE enterilng state SUCCESS 3353 3354EAP: EAP ent_ering state DISABLED 3355 3356EAPOL: SUPP_PAE entering state AUTHENTICATED 3357 3358EAPOL:n Supplicant port status: Authoorized 3359 3360EAPOL: SUPP_BE enteringtstate IDLE 3361 3362WifiWpaReceiveEapol donepleted - result=SUCCESS 3363 3364\# ifconfig 3365 3366lo Link encap:Local Loopback 3367 3368 inet addr:127.0.0.1 Mask:255.0.0.0 3369 3370 inet6 addr: ::1/128 Scope: Host 3371 3372 UP LOOPBACK RUNNING MTU:65536 Metric:1 3373 3374 RX packets:12 errors:0 dropped:0 overruns:0 frame:0 3375 3376 TX packets:12 errors:0 dropped:0 overruns:0 carrier:0 3377 3378 collisions:0 txqueuelen:1000 3379 3380 RX bytes:565 TX bytes:565 3381 3382wlan0 Link encap:Ethernet HWaddr 10:2c:6b:11:61:e0 Driver bcmsdh_sdmmc 3383 3384 inet6 addr: fe80::122c:6bff:fe11:61e0/64 Scope: Link 3385 3386 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 3387 3388 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 3389 3390 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 3391 3392 collisions:0 txqueuelen:1000 3393 3394 RX bytes:0 TX bytes:0 3395 3396p2p0 Link encap:Ethernet HWaddr 12:2c:6b:11:61:e0 3397 3398 inet6 addr: fe80::102c:6bff:fe11:61e0/64 Scope: Link 3399 3400 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 3401 3402 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 3403 3404 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 3405 3406 collisions:0 txqueuelen:1000 3407 3408 RX bytes:0 TX bytes:0 3409 3410p2p-p2p0-0 Link encap:Ethernet HWaddr 12:2c:6b:11:21:e0 Driver bcmsdh_sdmmc 3411 3412 inet6 addr: fe80::102c:6bff:fe11:21e0/64 Scope: Link 3413 3414 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 3415 3416 RX packets:0 errors:0 dropped:9 overruns:0 frame:0 3417 3418 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 3419 3420 collisions:0 txqueuelen:1000 3421 3422 RX bytes:0 TX bytes:0 3423 3424# 3425 3426## **4G** 3427 3428### **EC20模块** 3429 3430EC20模块是移远的一款比较经典的4G通信模组,MCU可以通过USB或者串口来和4G模块进行通信,我们rk3566使用的则是USB接口。 3431 34324G模块作为usb device,在加载对应的驱动后会生成ttyUSBx节点,框架层可以通过这些节点使用AT指令或者模块的状态和信息,通过ppp拨号注册一个网卡设备,拨号成功后在命令行可以通过ifconfig -a,可以看到有pppx网卡生成。 3433 3434### **硬件连接** 3435 3436从原理图中我们看到我们的4G模块使用的PCIE接口,细心的同学会发现36和38引脚是USBDN和USBDP,也就是说我们使用的是PCIE转USB接口,最终的表现和直接使用USB接口是一样的。 3437 3438![img](../figures/khdvk_3566b_4G_1.png) 3439 3440因为4G模块使用的是USB接口,对应USB的host功能一定要工作正常,比如USB VBUS的使能,USB设备树的正确配置,kernel config的一些配置都要相应的打开,有的4G模块还有电源使能引脚,也需要在设备树中配置。 3441 3442### **Kennel修改** 3443 3444#### **配置VID PID** 3445 3446在drivers/usb/serial/option.c,添加对应的vid pid,当插入一个新的usb设备,option里相关的USB虚拟串口驱动会匹配vid pid,如果匹配成功,就会生成ttysUSBx节点,具体模块的修改方法在供应商提供的模块的资料里一般都会有,如Linux_USB_Driver_User_Guide 3447 34481、option.c增加EC20的pid vid如下,在option_ids结构体中增加: 3449 3450``` 3451static const struct usb_device_id option_ids[] = { 3452 3453{ USB_DEVICE(0x2c7c, 0x6002) }, /* Quectel EC20 */ 3454``` 3455 3456### **测试** 3457 34581、 在/dev/查看有无ttyUSBx节点,有类似如下节点表明模块配置没有问题。 3459 3460``` 3461#ls /dev/ttyUSB* 3462 3463/dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3 3464``` 3465 34662、 AT指令测试,使用microcom串口指令 3467 3468``` 3469#microcom /dev/ttyUSB2 3470 3471AT 3472 3473OK 3474``` 3475 3476## Vibrator 3477 3478Vibrator是振动器的意思,也可以被叫做马达,马达旋转或者做直线运动会产生振动。 3479 3480### **驱动框架模型** 3481 3482#### Vibrator驱动模型 3483 3484![img](../figures/khdvk_3566b_vibrator_1.png) 3485 3486Vibrator驱动按HDF标准框架开发,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动。Vibrator驱动提供HDI能力接口,支持静态HCS配置的时间序列和动态配置持续时间两种振动效果。调用StartOnce接口动态配置持续振动时间,调用StartEffect接口启动静态配置的振动效果。 3487 3488### HDF驱动适配 3489 3490#### HCS配置 3491 3492配置设备描述信息,在device_info.hcs中添加device_linear_vibrator: 3493 3494 vibrator :: host { 3495 hostName = "vibrator_host"; 3496 device_vibrator :: device { 3497 device0 :: deviceNode { 3498 policy = 2; 3499 priority = 100; 3500 preload = 0; 3501 permission = 0664; 3502 moduleName = "HDF_VIBRATOR"; 3503 serviceName = "hdf_misc_vibrator"; 3504 deviceMatchAttr = "hdf_vibrator_driver"; 3505 } 3506 } 3507 device_linear_vibrator :: device { 3508 device0 :: deviceNode { 3509 policy = 1; 3510 priority = 105; 3511 preload = 0; 3512 permission = 0664; 3513 moduleName = "HDF_LINEAR_VIBRATOR"; 3514 serviceName = "hdf_misc_linear_vibrator"; 3515 deviceMatchAttr = "hdf_linear_vibrator_driver"; 3516 } 3517 } 3518 } 3519 3520配置线性马达器件信息,在linear_vibrator_config.hcs和vibrator_config.hcs中添加器件的特性: 3521 3522 root{ 3523 linearVibratorConfig { 3524 boardConfig { 3525 match_attr = "hdf_linear_vibrator_driver"; 3526 vibratorChipConfig { 3527 busType = 1; // 0:i2c 1:gpio 3528 gpioNum = 154; 3529 startReg = 0; 3530 stopReg = 0; 3531 startMask = 0; 3532 } 3533 } 3534 } 3535 } 3536 3537 3538 root { 3539 vibratorConfig { 3540 boardConfig { 3541 match_attr = "hdf_vibrator_driver"; 3542 vibratorAttr { 3543 /* 0:rotor 1:linear */ 3544 deviceType = 1; 3545 supportPreset = 1; 3546 } 3547 vibratorHapticConfig { 3548 haptic_clock_timer { 3549 effectName = "haptic.clock.timer"; 3550 type = 1; // 0 means built-in, 1 time series 3551 seq = [600, 600, 200, 600]; // time seq 3552 } 3553 haptic_default_effect { 3554 effectName = "haptic.default.effect"; 3555 type = 0; 3556 seq = [0, 3, 800, 1]; 3557 } 3558 } 3559 } 3560 } 3561 } 3562 3563### HDF适配 3564 3565驱动入口函数实现: 3566 3567 struct VibratorOps { 3568 int32_t (*Start)(void); 3569 int32_t (*StartEffect)(uint32_t effectType); 3570 int32_t (*Stop)(void); 3571 }; 3572 3573 int32_t InitLinearVibratorDriver(struct HdfDeviceObject *device) 3574 { 3575 static struct VibratorOps ops; 3576 ------ 3577 ops.Start = StartLinearVibrator; 3578 ops.StartEffect = StartEffectLinearVibrator; 3579 ops.Stop = StopLinearVibrator; 3580 3581 RegisterVibrator(&ops); 3582 3583 ParserLinearConfig(device->property, drvData); 3584 3585 GpioSetDir(drvData->gpioNum, GPIO_DIR_OUT); 3586 } 3587 3588 struct HdfDriverEntry g_linearVibratorDriverEntry = { 3589 .moduleVersion = 1, 3590 .moduleName = "HDF_LINEAR_VIBRATOR", 3591 .Bind = BindLinearVibratorDriver, 3592 .Init = InitLinearVibratorDriver, 3593 .Release = ReleaseLinearVibratorDriver, 3594 }; 3595 3596 HDF_INIT(g_linearVibratorDriverEntry); 3597 3598### 代码分布 3599 3600./drivers/peripheral/misc/vibrator/chipset/vibrator\_linear\_driver.c 3601./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/device\_info/device\_info.hcs 3602./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/vibrator/linear\_vibrator\_config.hcs 3603./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/vibrator/vibrator\_config.hcs 3604 3605### UT测试 3606 3607代码路径 3608 3609./drivers/peripheral/misc/vibrator/test/unittest/common/hdf\_vibrator\_test.cpp 3610./drivers/peripheral/misc/vibrator/test/unittest/hdi/hdf\_vibrator_hdi\_test.cpp 3611 3612编译UT代码命令 3613 3614./build.sh --product-name khdvk\_3566b --build-target hdf\_test\_vibrator 3615 3616生成目标文件路径 3617 3618./out/khdvk\_3566b/tests/unittest/hdf/vibrator/hdf\_unittest\_vibrator 3619./out/khdvk\_3566b/tests/unittest/hdf/vibrator/hdf\_unittest\_hdi\_vibrator 3620 3621将编译生成的bin文件 push到开发板上system/bin目录,修改执行权限,执行结果如下 3622 3623# ./hdfunittest\_hdi\_vibrator 3624 3625Load parameter\_contexts succes: /system/etc/selinux/targeted/contexts/parameter\_contexts 3626Running main() from ../../third_party/googletest/googletest/src/gtest_main.cc 3627[==========] Running 14 tests from 1 test case. 3628[----------] Global test environment set-up. 3629[----------] 14 tests from HdfVibratorHdiTest 3630[ RUN ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty 3631[ OK ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty (0 ms) 3632[ RUN ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_001 3633[ OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_001 (2002 ms) 3634[ RUN ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_002 3635[ OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_002 (2 ms) 3636[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_001 3637[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_001 (5002 ms) 3638[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_002 3639[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_002 (2002 ms) 3640[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_004 3641[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_004 (5005 ms) 3642[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_005 3643[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_005 (5002 ms) 3644[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_006 3645[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_006 (5002 ms) 3646[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect\_007 3647[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_007 (3 ms) 3648# 3649# 3650# 3651 3652# ./hdf\_unittest\_vibrator 3653 3654Load parameter\_contexts succes: /system/etc/selinux/targeted/contexts/parameter\_contexts 3655Running main() from ../../third_party/googletest/googletest/src/gtest\_main.cc 3656[==========] Running 16 tests from 1 test case. 3657[----------] Global test environment set-up. 3658[----------] 16 tests from HdfVibratorTest 3659[ RUN ] HdfVibratorTest.CheckVibratorInstanceIsEmpty 3660[ OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms) 3661[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration\_001 3662[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration\_001 (2001 ms) 3663[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration\_002 3664[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration\_002 (0 ms) 3665[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_001 3666[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_001 (5000 ms) 3667[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_002 3668[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_002 (2001 ms) 3669[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_003 3670[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_003 (0 ms) 3671[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_004 3672[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_004 (5001 ms) 3673[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_005 3674[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_005 (5000 ms) 3675[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_006 3676[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_006 (5000 ms) 3677[ RUN ] HdfVibratorTest.ExecuteVibratorEffect\_007 3678[ OK ] HdfVibratorTest.ExecuteVibratorEffect\_007 (1 ms) 3679# 3680# 3681# 3682