1# Standard System Solution – Rockchip RK3568 Porting Case 2 3This document describes how to port standard system functions based on the DAYU200 development board of the RK3568 chip from Rockchip. The porting processing mainly includes product configuration adding, kernel startup and upgrade, ADM-based conversion of audio, case summary of the camera, TP, LCD, Wi-Fi, Bluetooth, vibrator, sensor, and graphics display modules, as well as related function adaptation. 4 5## Product Configuration and Directory Planning 6 7### Product Configuration 8 9Create a JSON file named after RK3568 in the `//productdefine/common/device` directory and specify the CPU architecture. The `//productdefine/common/device/rk3568.json` file is configured is as follows: 10 11``` 12{ 13 "device_name": "rk3568", 14 "device_company": "rockchip", 15 "target_os": "ohos", 16 "target_cpu": "arm", 17 "kernel_version": "", 18 "device_build_path": "device/board/hihope/rk3568", 19 "enable_ramdisk": true, // Specifies whether to support ramdisk secondary boot. 20 "build_selinux": true // Indicates whether SELinux permission management is supported. 21} 22``` 23 24Create a **rk3568.json** file in the **//productdefine/common/products** directory. This file is used to describe the SoC used by the product and the required subsystems. Configure the file as follows: 25 26``` 27{ 28 "product_name": "rk3568", 29 "product_company" : "hihope", 30 "product_device": "rk3568", 31 "version": "2.0", 32 "type": "standard", 33 "parts":{ 34 "ace:ace_engine_standard":{}, 35 "ace:napi":{}, 36 ... 37 "xts:phone_tests":{} 38 } 39} 40``` 41 42The main configurations are as follows: 43 441. **product_device**: SoC used by the product. 452. **type**: system type. In this example, set it to **standard**. 463. **parts**: subsystem to enable. A subsystem can be treated as an independently built functional block. 47 48You can find predefined subsystems in **//build/subsystem_config.json**. You can also customize subsystems. 49 50You are advised to copy the configuration file of Hi3516D V300 and delete the **hisilicon_products** subsystem, which is used to compile the kernel for Hi3516D V300. 51 52### Directory Planning 53 54This solution designs the directory structure using the [board and SoC decoupling idea](https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md), and plans the SoC adaptation directory as follows: 55 56``` 57device 58├── board --- Board vendor directory 59│ └── hihope --- Board vendor 60│ └── rk3568 --- Board name, RK3568, which contains driver service code 61└── soc --- SoC vendor directory 62 └── rockchip --- SoC vendor: Rockchip 63 └── rk3568 --- SoC series: RK3568, mainly solutions provided by the chip vendor and closed-source libraries 64 65 66``` 67 68``` 69vendor 70└── hihope 71 └── rk3568 --- Product name: product, HCS, and demo 72``` 73 74## **Kernel Startup** 75 76### Secondary Boot 77 78Unlike traditional boot that directly mounts **system** and boots using **init** of **system**, secondary boot is to mount **ramdsik**, boot using **init** of **ramdsik**, perform necessary initialization operations (such as mounting the **system** and **vendor** partitions), and then switch to **init** of **system**. 79 80RK3568 adaptation is to pack **ramdisk** compiled in the mainline version into **boot_linux.img**. The procedure is as follows: 81 821. Enable secondary boot. 83 84 Set **enable_ramdisk** in **productdefine/common/device/rk3568.json**. 85 86 ``` 87 { 88 "device_name": "rk3568", 89 "device_company": "hihope", 90 "target_os": "ohos", 91 "target_cpu": "arm", 92 "kernel_version": "", 93 "device_build_path": "device/hihope/build", 94 "enable_ramdisk": true, 95 "build_selinux": true 96 } 97 ``` 98 992. Pack the **ramdsik.img** file compiled in the mainline version to **boot_linux.img**. 100 101 View the configuration as follows: 102 103 RK supports **uboot** from **ramdisk**. You only need to add **ramdisk.img** to the configuration file of the packed **boot_linux.img**. Therefore, the **its** format of the mainline version is not used. Specifically, add the following content to the kernel compilation script **make-ohos.sh**: 104 105 ``` 106 function make_extlinux_conf() 107 { 108 dtb_path=$1 109 uart=$2 110 image=$3 111 112 echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF} 113 echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF} 114 echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF} 115 if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then 116 echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF} 117 fi 118 cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4" 119 echo " ${cmdline}" >> ${EXTLINUX_CONF} 120 } 121 ``` 122 123### Packing 124 125Add the **make-boot.sh** script for packing the boot image. This script can be called when the boot image is packed after **ramdisk** is compiled. The main content is as follows: 126 127``` 128genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img 129``` 130 131For details about modification for calling **make-boot.sh**, see the following: 132 133https://gitee.com/openharmony/build/pulls/569/files 134 135### INIT Configuration 136 137For details about the init configuration, see the [specifications of the startup subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/startup.md). 138 139## **Audio** 140 141### Overall structure of the RK3568 audio module 142 143 144 145### Introduction to ADM Adaptation Solution 146 147#### ADM framework adaptation of the RK3568 platform 148 149 150 1511. ADM Drivers adapter 152 153 Register the Codec/DMA/I2S driver so that the ADM can load the driver node. Register the API functions for the interaction between the ADM and drivers. 154 1552. ADM Drivers impl 156 157 Implement the ADM Drivers adapter API function, obtain configuration information such as **Codec_config.hcs/dai_config.hcs**, and register the information with the corresponding device. 158 1593. Linux Drivers 160 161 You can use **ADM Drivers impl** to complete end-to-end driver configuration based on the hardware manual. It can also use the native Linux driver implementation and APIs to reduce your workload. 162 163#### Directory Structure 164 165``` 166./device/board/hihope/rk3568/audio_drivers 167├── codec 168│ └── rk809_codec 169│ ├── include 170│ │ ├── rk809_codec_impl.h 171│ │ └── rk817_codec.h 172│ └── src 173│ ├── rk809_codec_adapter.c 174│ ├── rk809_codec_linux_driver.c 175│ └── rk809_codec_ops.c 176├── dai 177│ ├── include 178│ │ ├── rk3568_dai_linux.h 179│ │ └── rk3568_dai_ops.h 180│ └── src 181│ ├── rk3568_dai_adapter.c 182│ ├── rk3568_dai_linux_driver.c 183│ └── rk3568_dai_ops.c 184├── dsp 185│ ├── include 186│ │ └── rk3568_dsp_ops.h 187│ └── src 188│ ├── rk3568_dsp_adapter.c 189│ └── rk3568_dsp_ops.c 190├── include 191│ ├── audio_device_log.h 192│ └── rk3568_audio_common.h 193└── soc 194 ├── include 195 │ └── rk3568_dma_ops.h 196 └── src 197 ├── rk3568_dma_adapter.c 198 └── rk3568_dma_ops.c 199``` 200 201### Detailed Process of Adapting RK3568 to ADM 202 203#### Audio Framework Sorting 204 205Sort out the audio structure of the target platform and specify the data stream and control stream path. 206 2071. For the RK3568 platform, the audio structure is relatively simple. For details, see the overall audio structure of the RK3568 platform. The Codec functions as an independent device. The I2C controls the device, and the I2S implements the interaction between the codec device and the CPU. 2082. Sort out the hardware information such as the I2S channel ID, corresponding pin ID, I2C channel ID, and address based on the schematic diagram. 2093. Obtain the datasheet corresponding to the codec and the datasheet of the RK3568 platform (including the introduction to the registers such as the I2S and DMA channels). 210 211#### ADM Structure 212 213The following figure shows the ADM structure. Audio Peripheral Drivers and Platform Drivers are required for platform adaptation. 214 215 216 217Based on the audio structure analysis in step 1, Audio Peripheral Drivers contain the RK809 driver, and Platform Drivers contain the DMA driver and I2S driver. 218 219| Driver to Adapt| ADM Module| API File Path | 220| -------------- | ----------- | ---------------------------------------------------- | 221| RK809 driver | Accessory | drivers/framework/include/audio/audio_accessory_if.h | 222| DMA driver | platform | drivers/framework/include/audio/audio_platform_if.h | 223| I2S driver | DAI | drivers/framework/include/audio/audio_dai_if.h.h | 224 225#### Driver Code Framework Setup 226 227##### Configuring the HCS File 228 229Register the driver node under **audio** in the **device_info.hcs** file. 230 231```c 232 audio :: host { 233 hostName = "audio_host"; 234 priority = 60; 235 device_dai0 :: device { 236 device0 :: deviceNode { 237 policy = 1; 238 priority = 50; 239 preload = 0; 240 permission = 0666; 241 moduleName = "DAI_RK3568"; 242 serviceName = "dai_service"; 243 deviceMatchAttr = "hdf_dai_driver"; 244 } 245 } 246 device_codec :: device { 247 device0 :: deviceNode { 248 policy = 1; 249 priority = 50; 250 preload = 0; 251 permission = 0666; 252 moduleName = "CODEC_RK809"; 253 serviceName = "codec_service_0"; 254 deviceMatchAttr = "hdf_codec_driver"; 255 } 256 } 257 device_codec_ex :: device { 258 device0 :: deviceNode { 259 policy = 1; 260 priority = 50; 261 preload = 0; 262 permission = 0666; 263 moduleName = "CODEC_RK817"; 264 serviceName = "codec_service_1"; 265 deviceMatchAttr = "hdf_codec_driver_ex"; 266 } 267 } 268 device_dsp :: device { 269 device0 :: deviceNode { 270 policy = 1; 271 priority = 50; 272 preload = 0; 273 permission = 0666; 274 moduleName = "DSP_RK3568"; 275 serviceName = "dsp_service_0"; 276 deviceMatchAttr = "hdf_dsp_driver"; 277 } 278 } 279 device_dma :: device { 280 device0 :: deviceNode { 281 policy = 1; 282 priority = 50; 283 preload = 0; 284 permission = 0666; 285 moduleName = "DMA_RK3568"; 286 serviceName = "dma_service_0"; 287 deviceMatchAttr = "hdf_dma_driver"; 288 } 289 } 290 ...... 291 } 292 293``` 294 295Select the Codec node or Accessory node based on the connected device, and configure the private attributes (including the start address of the register and the address of the related control register) corresponding to the device. **Codec_config.hcs** and **DAI_config.hcs** are involved. 296 297For details about the configuration, see the HCS configuration section and **audio_parse** module code of the ADM framework in [Audio](https://gitee.com/openharmony/docs/blob/master/en/device-dev/driver/driver-peripherals-audio-des.md). 298 299##### Codec/Accessory Module 300 3011. Register the driver with the HDF framework. The code snippet is as follows. The **moduleName** is the same as that in the HCS file. 302 303 ``` 304 struct HdfDriverEntry g_codecDriverEntry = { 305 .moduleVersion = 1, 306 .moduleName = "CODEC_HI3516", 307 .Bind = CodecDriverBind, 308 .Init = CodecDriverInit, 309 .Release = CodecDriverRelease, 310 }; 311 HDF_INIT(g_codecDriverEntry); 312 ``` 313 3142. Fill the Codec module with: 315 316 **g_codecData**: operation function set and private data set of the codec device. 317 318 **g_codecDaiDeviceOps**: codec DAI device operation function set, including APIs for starting transmission and setting parameters. 319 320 **g_codecDaiData**: operation function set and private data set of the digital audio API of the codec. 321 3223. Implement the bind, init, and release functions. 323 3244. Verification 325 326 Add debug logs to the bind and init functions, compile the version, and obtain system logs. 327 328 ``` 329 [ 1.548624] [E/"rk809_codec_adapter"] [Rk809DriverBind][line:258]: enter 330 [ 1.548635] [E/"rk809_codec_adapter"] [Rk809DriverBind][line:260]: success 331 [ 1.548655] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:270]: enter 332 [ 1.549050] [E/"rk809_codec_adapter"] [GetServiceName][line:226]: enter 333 [ 1.549061] [E/"rk809_codec_adapter"] [GetServiceName][line:250]: success 334 [ 1.549072] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:316]: g_chip->accessory.drvAccessoryName = codec_service_1 335 [ 1.549085] [E/audio_core] [AudioSocRegisterDai][line:86]: Register [accessory_dai] success. 336 [ 1.549096] [E/audio_core] [AudioRegisterAccessory][line:120]: Register [codec_service_1] success. 337 [ 1.549107] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:323]: success! 338 ``` 339 340 341 342##### DAI Module 343 3441. Register the I2S driver with the HDF framework. The code snippet is as follows. The **moduleName** is the same as that in the HCS file. 345 346 ```c 347 struct HdfDriverEntry g_daiDriverEntry = { 348 .moduleVersion = 1, 349 .moduleName = "DAI_RK3568", 350 .Bind = DaiDriverBind, 351 .Init = DaiDriverInit, 352 .Release = DaiDriverRelease, 353 }; 354 HDF_INIT(g_daiDriverEntry); 355 ``` 356 3572. Fill the DAI module with: 358 359 ```c 360 struct AudioDaiOps g_daiDeviceOps = { 361 .Startup = Rk3568DaiStartup, 362 .HwParams = Rk3568DaiHwParams, 363 .Trigger = Rk3568NormalTrigger, 364 }; 365 366 struct DaiData g_daiData = { 367 .Read = Rk3568DeviceReadReg, 368 .Write = Rk3568DeviceWriteReg, 369 .DaiInit = Rk3568DaiDeviceInit, 370 .ops = &g_daiDeviceOps, 371 }; 372 ``` 373 3743. Implement the bind, init, and release functions. 375 3764. Verification 377 378 Add debug logs to the bind and init functions, compile the version, and obtain system logs. 379 380 ``` 381 [ 1.549193] [I/device_node] launch devnode dai_service 382 [ 1.549204] [E/HDF_LOG_TAG] [DaiDriverBind][line:38]: entry! 383 [ 1.549216] [E/HDF_LOG_TAG] [DaiDriverBind][line:55]: success! 384 [ 1.549504] [E/audio_core] [AudioSocRegisterDai][line:86]: Register [dai_service] success. 385 [ 1.549515] [E/HDF_LOG_TAG] [DaiDriverInit][line:116]: success. 386 ``` 387 388##### Platform Module 389 3901. Register the DMA driver with the HDF framework. The code snippet is as follows. The **moduleName** is the same as that in the HCS file. 391 392 ``` 393 struct HdfDriverEntry g_platformDriverEntry = { 394 .moduleVersion = 1, 395 .moduleName = "DMA_RK3568", 396 .Bind = PlatformDriverBind, 397 .Init = PlatformDriverInit, 398 .Release = PlatformDriverRelease, 399 }; 400 HDF_INIT(g_platformDriverEntry); 401 ``` 402 4032. Fill the DMA module with: 404 405 ```c 406 struct AudioDmaOps g_dmaDeviceOps = { 407 .DmaBufAlloc = Rk3568DmaBufAlloc, 408 .DmaBufFree = Rk3568DmaBufFree, 409 .DmaRequestChannel = Rk3568DmaRequestChannel, 410 .DmaConfigChannel = Rk3568DmaConfigChannel, 411 .DmaPrep = Rk3568DmaPrep, 412 .DmaSubmit = Rk3568DmaSubmit, 413 .DmaPending = Rk3568DmaPending, 414 .DmaPause = Rk3568DmaPause, 415 .DmaResume = Rk3568DmaResume, 416 .DmaPointer = Rk3568PcmPointer, 417 }; 418 419 struct PlatformData g_platformData = { 420 .PlatformInit = AudioDmaDeviceInit, 421 .ops = &g_dmaDeviceOps, 422 }; 423 ``` 424 4253. Implement the bind, init, and release functions. 426 4274. Verification 428 429 Add debug logs to the bind and init functions, compile the version, and obtain system logs. 430 431 ``` 432 [ 1.548469] [E/rk3568_platform_adapter] [PlatformDriverBind][line:42]: entry! 433 [ 1.548481] [E/rk3568_platform_adapter] [PlatformDriverBind][line:58]: success! 434 [ 1.548492] [E/rk3568_platform_adapter] [PlatformDriverInit][line:100]: entry. 435 [ 1.548504] [E/rk3568_platform_adapter] [PlatformGetServiceName][line:67]: entry! 436 [ 1.548515] [E/rk3568_platform_adapter] [PlatformGetServiceName][line:91]: success! 437 [ 1.548528] [E/audio_core] [AudioSocRegisterPlatform][line:63]: Register [dma_service_0] success. 438 [ 1.548536] [E/rk3568_platform_adapter] [PlatformDriverInit][line:119]: success. 439 ``` 440 441#### Driver Adaptation 442 443##### Codec/Accessory Module 444 4451. Read the DTS file to obtain the corresponding device node, and use the native driver registration function of Linux to obtain the corresponding device. 446 447 ``` 448 static int rk817_platform_probe(struct platform_device *pdev) { 449 rk817_pdev = pdev; 450 dev_info(&pdev->dev, "got rk817-codec platform_device"); 451 return 0; 452 } 453 454 static struct platform_driver rk817_codec_driver = { 455 .driver = { 456 .name = "rk817-codec", // codec node in dts file 457 .of_match_table = rk817_codec_dt_ids, 458 }, 459 .probe = rk817_platform_probe, 460 .remove = rk817_platform_remove, 461 }; 462 ``` 463 4642. Encapsulate the functions for reading and writing registers. 465 Use the **regmap** function of Linux based on the obtained device. You do not need to obtain the base address of the module. 466 Obtain the regmap code snippet of RK817. 467 468 ``` 469 g_chip = devm_kzalloc(&rk817_pdev->dev, sizeof(struct Rk809ChipData), GFP_KERNEL); 470 if (!g_chip) { 471 AUDIO_DEVICE_LOG_ERR("no memory"); 472 return HDF_ERR_MALLOC_FAIL; 473 } 474 g_chip->pdev = rk817_pdev; 475 476 struct rk808 *rk808 = dev_get_drvdata(g_chip->pdev->dev.parent); 477 if (!rk808) { 478 AUDIO_DEVICE_LOG_ERR("%s: rk808 is NULL\n", __func__); 479 ret = HDF_FAILURE; 480 RK809ChipRelease(); 481 return ret; 482 } 483 g_chip->regmap = devm_regmap_init_i2c(rk808->i2c, 484 &rk817_codec_regmap_config); 485 if (IS_ERR(g_chip->regmap)) { 486 AUDIO_DEVICE_LOG_ERR("failed to allocate regmap: %ld\n", PTR_ERR(g_chip->regmap)); 487 RK809ChipRelease(); 488 return ret; 489 } 490 ``` 491 492 Code snippet of read and write functions of the register 493 494 ``` 495 int32_t Rk809DeviceRegRead(uint32_t reg, uint32_t *val) 496 { 497 if (regmap_read(g_chip->regmap, reg, val)) { 498 AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg); 499 return HDF_FAILURE; 500 } 501 502 return HDF_SUCCESS; 503 } 504 505 int32_t Rk809DeviceRegWrite(uint32_t reg, uint32_t value) { 506 if (regmap_write(g_chip->regmap, reg, value)) { 507 AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value); 508 return HDF_FAILURE; 509 } 510 511 return HDF_SUCCESS; 512 } 513 514 int32_t Rk809DeviceRegUpdatebits(uint32_t reg, uint32_t mask, uint32_t value) { 515 if (regmap_update_bits(g_chip->regmap, reg, mask, value)) { 516 AUDIO_DRIVER_LOG_ERR("update register bits fail: [%04x] = %04x", reg, value); 517 return HDF_FAILURE; 518 } 519 520 return HDF_SUCCESS; 521 } 522 ``` 523 5243. Define the register Initialization function. 525 526 The **regmap** function of Linux is used. Therefore, you need to define the **RegDefaultInit** function and read the initSeqConfig register and value in the HCS for configurations. 527 528 RK809RegDefaultInit code snippet 529 530 ```c 531 int32_t RK809RegDefaultInit(struct AudioRegCfgGroupNode **regCfgGroup) 532 { 533 int32_t i; 534 struct AudioAddrConfig *regAttr = NULL; 535 536 if (regCfgGroup == NULL || regCfgGroup[AUDIO_INIT_GROUP] == NULL || 537 regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem == NULL || regCfgGroup[AUDIO_INIT_GROUP]->itemNum <= 0) { 538 AUDIO_DEVICE_LOG_ERR("input invalid parameter."); 539 540 return HDF_ERR_INVALID_PARAM; 541 } 542 543 regAttr = regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem; 544 545 for (i = 0; i < regCfgGroup[AUDIO_INIT_GROUP]->itemNum; i++) { 546 Rk809DeviceRegWrite(regAttr[i].addr, regAttr[i].value); 547 } 548 549 return HDF_SUCCESS; 550 } 551 ``` 552 5534. Encapsulate the read and write functions of the control API. 554 555 Set the control read/write functions to **RK809CodecReadReg** and **RK809CodecWriteReg**. 556 557 ```c 558 struct CodecData g_rk809Data = { 559 .Init = Rk809DeviceInit, 560 .Read = RK809CodecReadReg, 561 .Write = RK809CodecWriteReg, 562 }; 563 564 struct AudioDaiOps g_rk809DaiDeviceOps = { 565 .Startup = Rk809DaiStartup, 566 .HwParams = Rk809DaiHwParams, 567 .Trigger = RK809NormalTrigger, 568 }; 569 570 struct DaiData g_rk809DaiData = { 571 .DaiInit = Rk809DaiDeviceInit, 572 .ops = &g_rk809DaiDeviceOps, 573 }; 574 ``` 575 576 Encapsulate the read and write functions of the control API. 577 578 The original read/write prototype involves three parameters (**unsigned long virtualAddress**, **uint32_t reg**, and **uint32_t *val**). The virtual address is not required. Therefore, you only need to encapsulate one API as follows: 579 580 ```c 581 int32_t RK809CodecReadReg(unsigned long virtualAddress,uint32_t reg, uint32_t *val) 582 { 583 if (val == NULL) { 584 AUDIO_DRIVER_LOG_ERR("param val is null."); 585 return HDF_FAILURE; 586 } 587 if (Rk809DeviceRegRead(reg, val)) { 588 AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg); 589 return HDF_FAILURE; 590 } 591 ADM_LOG_ERR("read reg 0x[%02x] = 0x[%02x]",reg,*val); 592 return HDF_SUCCESS; 593 } 594 595 int32_t RK809CodecWriteReg(unsigned long virtualAddress,uint32_t reg, uint32_t value) 596 { 597 if (Rk809DeviceRegWrite(reg, value)) { 598 AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value); 599 return HDF_FAILURE; 600 } 601 ADM_LOG_ERR("write reg 0x[%02x] = 0x[%02x]",reg,value); 602 return HDF_SUCCESS; 603 } 604 ``` 605 6065. For other OPS functions: 607 608 - **Rk809DeviceInit**: Read the HCS file, initialize the codec register, and add the corresponding control configuration (/* reg, rreg, shift, rshift, min, max, mask, invert, value */) to kcontrol to facilitate dispatch control. 609 - **Rk809DaiStartup**: Read the HCS file, and configure the control register of the codec/accessory. 610 - **Rk809DaiHwParams**: Configure the corresponding register based on the audio attributes (such as the sampling rate, format, and channel) delivered by the HAL. 611 - **RK809NormalTrigger**: Operate the corresponding register based on the operation command code delivered by the HAL to start or stop the codec and switch between recording and playing. 612 613##### DAI (i2s) Module 614 6151. Read and write registers. 616 617 The idea is the same as that of the Codec module. Read the Linux DTS file and use the **regmap** function of Linux to read and write registers. 618 619 ```c 620 int32_t Rk3568DeviceReadReg(unsigned long regBase, uint32_t reg, uint32_t *val) 621 { 622 AUDIO_DEVICE_LOG_ERR("entry"); 623 (void)regBase; 624 struct device_node *dmaOfNode = of_find_node_by_path("/i2s@fe410000"); 625 if(dmaOfNode == NULL) { 626 AUDIO_DEVICE_LOG_ERR("of_node is NULL."); 627 } 628 struct platform_device *platformdev = of_find_device_by_node(dmaOfNode); 629 struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&platformdev->dev); 630 631 (void)regBase; 632 if (regmap_read(i2s_tdm->regmap, reg, val)) { 633 AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", reg); 634 return HDF_FAILURE; 635 } 636 return HDF_SUCCESS; 637 } 638 639 int32_t Rk3568DeviceWriteReg(unsigned long regBase, uint32_t reg, uint32_t value) 640 { 641 AUDIO_DEVICE_LOG_ERR("entry"); 642 (void)regBase; 643 struct device_node *dmaOfNode = of_find_node_by_path("/i2s@fe410000"); 644 if(dmaOfNode == NULL) { 645 AUDIO_DEVICE_LOG_ERR("of_node is NULL."); 646 } 647 struct platform_device *platformdev = of_find_device_by_node(dmaOfNode); 648 struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&platformdev->dev); 649 if (regmap_write(i2s_tdm->regmap, reg, value)) { 650 AUDIO_DEVICE_LOG_ERR("write register fail: [%04x] = %04x", reg, value); 651 return HDF_FAILURE; 652 } 653 return HDF_SUCCESS; 654 } 655 ``` 656 6572. For other OPS functions: 658 659 - Rk3568DaiDeviceInit 660 661 Original framework, which reads the **DAI_config.hcs** parameter list and works with HwParams to set parameters. 662 663 664 - Rk3568DaiHwParams 665 666 Configure the I2S MCLK/BCLK/LRCLK clocks. 667 668 669 1. Calculate the MCLK based on different sampling rates. 670 671 ```c 672 int32_t RK3568I2sTdmSetSysClk(struct rk3568_i2s_tdm_dev *i2s_tdm, const struct AudioPcmHwParams *param) 673 { 674 /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ 675 uint32_t sampleRate = param->rate; 676 uint32_t mclk_parent_freq = 0; 677 switch (sampleRate) { 678 case AUDIO_DEVICE_SAMPLE_RATE_8000: 679 case AUDIO_DEVICE_SAMPLE_RATE_16000: 680 case AUDIO_DEVICE_SAMPLE_RATE_24000: 681 case AUDIO_DEVICE_SAMPLE_RATE_32000: 682 case AUDIO_DEVICE_SAMPLE_RATE_48000: 683 case AUDIO_DEVICE_SAMPLE_RATE_64000: 684 case AUDIO_DEVICE_SAMPLE_RATE_96000: 685 mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_192000; 686 break; 687 case AUDIO_DEVICE_SAMPLE_RATE_11025: 688 case AUDIO_DEVICE_SAMPLE_RATE_22050: 689 case AUDIO_DEVICE_SAMPLE_RATE_44100: 690 691 mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_176400; 692 break; 693 default: 694 AUDIO_DEVICE_LOG_ERR("Invalid LRCK freq: %u Hz\n", sampleRate); 695 return HDF_FAILURE; 696 } 697 i2s_tdm->mclk_tx_freq = mclk_parent_freq; 698 i2s_tdm->mclk_rx_freq = mclk_parent_freq; 699 700 return HDF_SUCCESS; 701 } 702 ``` 703 704 2. Calculate the BCLK/LRclk frequency division coefficient based on the obtained MCLK. 705 706 - Rk3568NormalTrigger 707 708 Complete a series of configurations based on the input and output types and commands (start/stop/pause/resume). 709 710 711 1. Start and stop the MCLK. 712 2. Start and stop DMA transfer. 713 3. Start and stop transmission. 714 See the code for detailed implementation, and refer to the API functions of the native Linux I2S driver. 715 716 ```c 717 // Start or restore the process. 718 if (streamType == AUDIO_RENDER_STREAM) { 719 clk_prepare_enable(i2s_tdm->mclk_tx); 720 regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, 721 I2S_DMACR_TDE_ENABLE, 722 I2S_DMACR_TDE_ENABLE); 723 } else { 724 clk_prepare_enable(i2s_tdm->mclk_rx); 725 regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, 726 I2S_DMACR_RDE_ENABLE, 727 I2S_DMACR_RDE_ENABLE); 728 if (regmap_read(i2s_tdm->regmap, I2S_DMACR, &val)) { 729 AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", I2S_DMACR); 730 return ; 731 } 732 AUDIO_DEVICE_LOG_ERR("i2s reg: 0x%x = 0x%x ", I2S_DMACR, val); 733 } 734 735 if (atomic_inc_return(&i2s_tdm->refcount) == 1) { 736 regmap_update_bits(i2s_tdm->regmap, I2S_XFER, 737 I2S_XFER_TXS_START | 738 I2S_XFER_RXS_START, 739 I2S_XFER_TXS_START | 740 I2S_XFER_RXS_START); 741 if (regmap_read(i2s_tdm->regmap, I2S_XFER, &val)) { 742 AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", I2S_XFER); 743 return ; 744 } 745 AUDIO_DEVICE_LOG_ERR("i2s reg: 0x%x = 0x%x ", I2S_XFER, val); 746 } 747 ``` 748##### Platform (DMA) Module 749 750For other OPS functions: 751 7521. Rk3568DmaBufAlloc/Rk3568DmaBufFree 753 754 Obtain the DMA device node. Use the system function **dma_alloc_wc** or **dma_free_wc** to apply for or release the DMA virtual memory and physical memory by referring to the method of obtaining the I2S device. 755 7562. Rk3568DmaRequestChannel 757 758 Use the native Linux DMA API function to obtain the DMA transfer channel **dma_request_slave_channel**. 759 760 ``` 761 dmaRtd->dmaChn[streamType] = dma_request_slave_channel(dmaDevice, dmaChannelNames[streamType]); 762 ``` 763 7643. Rk3568DmaConfigChannel 765 766 ``` 767 // Set channel parameters. 768 // Set voice playing channel parameters. 769 slave_config.direction = DMA_MEM_TO_DEV; 770 slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 771 slave_config.dst_addr = I2S1_ADDR + I2S_TXDR; 772 slave_config.dst_maxburst = 8; 773 // Set recording channel parameters. 774 slave_config.direction = DMA_DEV_TO_MEM; 775 slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 776 slave_config.src_addr = I2S1_ADDR + I2S_RXDR; 777 slave_config.src_maxburst = 8; 778 779 // Use the native Linux DMA API function to configure the DMA channel. 780 ret = dmaengine_slave_config(dmaChan, &slave_config); 781 if (ret != 0) { 782 AUDIO_DEVICE_LOG_ERR("dmaengine_slave_config failed"); 783 return HDF_FAILURE; 784 } 785 ``` 786 7874. Rk3568DmaSubmit/Rk3568DmaPending 788 789 Initialize a periodic DMA transfer descriptor by using the native Linux DMA API function **dmaengine_prep_dma_cyclic**. The **dmaengine_submit** API places the descriptor in the transfer queue, and then calls **dma_async_issue_pending** to start the transfer. 790 7915. Rk3568PcmPointer 792 793 After step 4 is complete, the ADM framework calls Rk3568PcmPointer to cyclically write CirBuf and calculate the pointer. 794 795 ``` 796 797 dma_chn = dmaRtd->dmaChn[DMA_TX_CHANNEL]; 798 buf_size = data->renderBufInfo.cirBufSize; 799 dmaengine_tx_status(dma_chn, dmaRtd->cookie[DMA_TX_CHANNEL], &dma_state); 800 if (dma_state.residue) { 801 currentPointer = buf_size - dma_state.residue; 802 *pointer = BytesToFrames(data->pcmInfo.frameSize, currentPointer); 803 } else { 804 *pointer = 0; 805 } 806 ``` 807 8086. Rk3568DmaPause 809 810 Use the native Linux DMA API function **dmaengine_terminate_async** to stop DMA transfer. 811 812 ``` 813 dmaengine_terminate_async(dmaChan); 814 ``` 815 8167. Rk3568DmaResume 817 818 Restart DMA transfer. You can perform operations related to **Rk3568DmaSubmit/Rk3568DmaPending**. 819 820##### FAQs for Adaptation 821 8221. After the audio plays for a period of time, the audio stops playing and there is a sharp and small sound. 823 Cause: After the playback stops, the components related to the codec are not powered off. 824 Solution: Register the **trigger** function of the codec. When the received command is **Stop**, power off the codec. 825 8262. After the audio plays for a period of time and stops, no sound can be heard when the playback is resumed. 827 Cause: The **PAUSE** API function of the DMA driver does not stop DMA transfer. 828 Solution: When the playback stops, the **PAUSE** function of the DMA is not used. Instead, the DAM transfer stop API is used. Accordingly, the service logic of the resume function is equivalent to restarting the DMA transfer. You can perform operations related to **Rk3568DmaSubmit/Rk3568DmaPending**. 829 8303. Noise occurs during playback. 831 Cause: The pointer position during DMA data transfer is incorrect. 832 Solution: The return value of the **Rk3568PcmPointer** function is the memory location for DMA transfer, which is calculated based on the difference between **buf** and **dma_state.residue**. 833 8344. The audio can be played, but the MCLK pin does not have clock signals. 835 Cause: The pin-ctrl in the DTS file is not configured with the MCLK pin. 836 Solution: Modify the DTS file. 837 838### Camera 839 840**Basic Concepts** 841 842The OpenHarmony camera driver model implements the HDI and the camera pipeline model to manage camera devices. The basic concepts of each layer are as follows: 843 8441. HDI implementation layer: implements standard device APIs of OHOS cameras. 845 8462. Framework layer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. 847 8483. Adaptation layer: shields the differences between bottom-layer chips and OSs for multi-platform adaptation. 849 850### Camera Driver Framework 851 852#### Source Code Framework 853 854The camera driver framework is stored in **drivers_peripheral**, and the source code directory is **drivers/peripheral/camera**. 855 856``` 857|-- README_zh.md 858|-- figures 859| -- logic-view-of-modules-related-to-this-repository_zh.png 860|-- hal 861| |-- BUILD.gn # Entry for building the camera driver framework 862| |-- adapter # Platform adaptation layer 863| |-- buffer_manager 864| |-- camera.gni # Global variables used by the component 865| |-- device_manager 866| |-- hdi_impl 867| |-- include 868| |-- init #demo sample 869| |-- pipeline_core 870| |-- test # Test code 871| |-- utils 872|-- hal_c # Dedicated C API for HiSilicon 873| |-- BUILD.gn 874| |-- camera.gni 875| |-- hdi_cif 876| |-- include 877|-- interfaces # HDI APIs 878 |-- hdi_ipc 879|-- hdi_passthrough 880 |-- include 881``` 882 883The camera .hcs file is configurable for each chipset. Therefore, it is placed in the chipset-related repository. The following takes RK3568 as an example. The repository name is **vendor_hihope**, and the source code directory is **vendor/hihope/rk3568/hdf_config/uhdf/camera**. 884 885 ├── hdi_impl 886 │ └── camera_host_config.hcs 887 └── pipeline_core 888 ├── config.hcs 889 ├── ipp_algo_config.hcs 890 └── params.hcs 891The code repository related to the camera chipset of RK3568 is **device_hihope**. Path: device/board/hihope/rk3568/camera/ 892``` 893├── BUILD.gn 894├── demo 895│ └── include 896│ └── project_camera_demo.h 897├── device_manager 898│ ├── BUILD.gn 899│ ├── include 900│ │ ├── imx600.h 901│ │ ├── project_hardware.h 902│ │ └── rkispv5.h 903│ └── src 904│ ├── imx600.cpp 905│ └── rkispv5.cpp 906├── driver_adapter 907│ └── test 908│ ├── BUILD.gn 909│ ├── unittest 910│ │ ├── include 911│ │ │ └── utest_v4l2_dev.h 912│ │ └── src 913│ │ └── utest_v4l2_dev.cpp 914│ └── v4l2_test 915│ └── include 916│ └── project_v4l2_main.h 917└── pipeline_core 918 ├── BUILD.gn 919 └── src 920 ├── ipp_algo_example 921 │ └── ipp_algo_example.c 922 └── node 923 ├── rk_codec_node.cpp 924 └── rk_codec_node.h 925``` 926 #### Camera Driver Framework Configuration 927 928Path of the RK3568 configuration file: "vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs". 929 930For other platforms, refer to the RK3568 adaptation. 931 932``` 933 hdi_server :: host { 934 hostName = "camera_host"; 935 priority = 50; 936 caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; 937 camera_device :: device { 938 device0 :: deviceNode { 939 policy = 2; 940 priority = 100; 941 moduleName = "libcamera_hdi_impl.z.so"; 942 serviceName = "camera_service"; 943 } 944 } 945 ... 946 } 947``` 948 949Parameter description: 950 951**Host**: A host node is an independent process. If an independent process is required, add a host node. 952 953**Policy**: service publish policy. Set this parameter to **2** for the HDI service. 954 955**moduleName**: name of the driver implementation library. 956 957**serviceName**: service name, which must be globally unique. 958 959Entry for implementing the Camera_host driver 960 961File path: drivers/peripheral/camera/interfaces/hdi_ipc/server/src/camera_host_driver.cpp 962 963Dispatch device service messages. 964 965**cmd Id:** command ID of the request. 966 967**Data:** pointer to other services or I/O requests. 968 969**Reply:** pointer to the content of the returned message. 970 971``` 972static int32_t CameraServiceDispatch(struct HdfDeviceIoClient *client, int cmdId, 973 struct HdfSBuf *data, struct HdfSBuf *reply) 974{ 975 HdfCameraService *hdfCameraService = CONTAINER_OF(client->device->service, HdfCameraService, ioservice); 976 return CameraHostServiceOnRemoteRequest(hdfCameraService->instance, cmdId, data, reply); 977 } 978``` 979 980Bind a device service: initializes the device service object and resource object. 981 982``` 983int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) 984{ 985 HDF_LOGI("HdfCameraHostDriverBind enter!"); 986 if (deviceObject == nullptr) { 987 HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !"); 988 return HDF_FAILURE; 989} 990``` 991 992Driver initialization function: detects and initializes the driver. 993 994``` 995int HdfCameraHostDriverInit(struct HdfDeviceObject *deviceObject) 996{ 997 return HDF_SUCCESS; 998} 999``` 1000 1001Driver resource release function: releases the bound device service object. 1002 1003``` 1004 void HdfCameraHostDriverRelease(HdfDeviceObject *deviceObject) 1005 { 1006 if (deviceObject == nullptr || deviceObject->service == nullptr) { 1007 HDF_LOGE("%{public}s deviceObject or deviceObject->service is NULL!", __FUNCTION__); 1008 return; 1009 } 1010 HdfCameraService *hdfCameraService = CONTAINER_OF(deviceObject->service, HdfCameraService, ioservice); 1011 if (hdfCameraService == nullptr) { 1012 HDF_LOGE("%{public}s hdfCameraService is NULL!", __FUNCTION__); 1013 return; 1014 } 1015``` 1016 1017Define the driver descriptor: registers the driver code with the driver framework. 1018 1019 1020 struct HdfDriverEntry g_cameraHostDriverEntry = { 1021 .moduleVersion = 1, 1022 .moduleName = "camera_service", 1023 .Bind = HdfCameraHostDriverBind, 1024 .Init = HdfCameraHostDriverInit, 1025 .Release = HdfCameraHostDriverRelease, 1026 }; 1027 1028 1029#### Camera Configuration 1030 1031In the camera module, all configuration files use the HCS configuration files supported by the system. The HCS configuration files are converted into HCB files during compilation. The configuration files burnt to the development board are in HCB format. In the code, the HCB files are parsed by using the HCS parsing API, to obtain the information in the configuration file. 1032 1033 1034 hc_gen("build_camera_host_config") { 1035 sources = [ rebase_path( 1036 "$camera_product_name_path/hdf_config/uhdf/camera/hdi_impl/camera_host_config.hcs") ] 1037 } 1038 1039 ohos_prebuilt_etc("camera_host_config.hcb") { 1040 deps = [ ":build_camera_host_config" ] 1041 hcs_outputs = get_target_outputs(":build_camera_host_config") 1042 source = hcs_outputs[0] 1043 relative_install_dir = "hdfconfig" 1044 install_images = [ chipset_base_dir ] 1045 subsystem_name = "hdf" 1046 part_name = "camera_device_driver" 1047 } 1048 1049### Camera Adaptation 1050 1051#### New Product Platform Adaptation 1052 1053 1054In the **drivers/peripheral/camera/hal/camera.gni** file, call **product.gni** of different chipsets based on the input **product_company**, **product_name**, and **device_name** during compilation. 1055 1056 if (defined(ohos_lite)) { 1057 import("//build/lite/config/component/lite_component.gni") 1058 import( 1059 "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") 1060 } else { 1061 import("//build/ohos.gni") 1062 if ("${product_name}" == "ohos-arm64") { 1063 import( 1064 "//drivers/peripheral/camera/hal/adapter/chipset/rpi/rpi3/device/camera/product.gni") 1065 } else if ("${product_name}" == "Hi3516DV300") { 1066 import( 1067 "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") 1068 } else if ("${product_name}" == "watchos") { 1069 import( 1070 "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") 1071 } else { 1072 import( 1073 "//device/board/${product_company}/${device_name}/camera/product.gni") 1074 } 1075 } 1076 1077The **product.gni** file in the following path specifies the path for compiling the code related to different chipsets: 1078 1079``` 1080 device/${product_company}/${device_name}/camera/ 1081``` 1082 1083The following is the **product.gni** file content of RK3568: 1084 1085 camera_device_name_path = "//device/board/${product_company}/${device_name}" 1086 is_support_v4l2 = true 1087 if (is_support_v4l2) { 1088 is_support_mpi = false 1089 defines += [ "SUPPORT_V4L2" ] 1090 chipset_build_deps = "$camera_device_name_path/camera/:chipset_build" 1091 camera_device_manager_deps = 1092 "$camera_device_name_path/camera/src/device_manager:camera_device_manager" 1093 camera_pipeline_core_deps = 1094 "$camera_device_name_path/camera/src/pipeline_core:camera_pipeline_core" 1095 } 1096 1097Three code compilation paths **chipset_build_deps**, **camera_device_manager_deps**, and **camera_pipeline_core_deps** are specified in **product.gni**. The paths are used in **drivers/peripheral/camera/hal/BUILD.gn**. 1098 1099#### Framework Adaptation 1100 1101 1102 1103 1104Take V4l2 as an example. The pipeline connection mode is to configure the connection in the HCS configuration file. The data source is called SourceNode, including hardware device control and data stream transfer. 1105 You can determine whether to add the ISPNode as required because the ISPNode and SensorNode can be unified as the SourceNode in many operations. SinkNode is the key point of data transmission in the pipeline. Data is transmitted back to the buffer queue. 1106 1107A node in the pipeline is the abstraction of the hardware/software module. Therefore, the hardware module node needs to control the hardware module. Before controlling the hardware module, you need to obtain the **deviceManager** of the corresponding hardware module and transmit the control command/data buffer through the **deviceManager**, therefore, the **deviceManager** has a v4l2 device manager abstract module, which is used to create the manager and controller of each hardware device, such as sensorManager, IspManager, and sensorController. Therefore, the v4l2 device manager is the general manager of each hardware device. 1108 1109The controller in deviceManager directly interacts with the driver adaptation layer. 1110 1111Based on the preceding description, to adapt a chip platform based on the Linux v4l2 framework, you only need to modify certain modules and HCS configuration file. If the standard v4l2 framework is used, the adapted code can be used. The following describes how to modify the modules. 1112 1113The following directories are added: 1114 1115- **vendor/hihope/rk3568/hdf_config/uhdf/camera/**: HCS configuration file directory of the current chip product. 1116 1117- **device/hihope/rk3568/camera/**: code adaptation directory of the current chip product. 1118 1119- **drivers/peripheral/camera/hal/adapter/platform/v4l2**: common platform code. 1120 1121#### HCS Configuration File Adaptation 1122 1123``` 1124 ├── hdi_impl 1125 │ └── camera_host_config.hcs 1126 └── pipeline_core 1127 ├── config.hcs 1128 ├── ipp_algo_config.hcs 1129 └── params.hcs 1130``` 1131 1132Take the RK3568 development board as an example. The HCS file must be stored in the corresponding path. 1133 1134``` 1135 vendor/${product_company}/${product_name}/ hdf_config/uhdf/camera/ 1136``` 1137 1138 ``` 1139 template ability { 1140 logicCameraId = "lcam001"; 1141 physicsCameraIds = [ 1142 "CAMERA_FIRST", 1143 "CAMERA_SECOND" 1144 ]; 1145 metadata { 1146 aeAvailableAntiBandingModes = [ 1147 "OHOS_CONTROL_AE_ANTIBANDING_MODE_OFF", 1148 "OHOS_CONTROL_AE_ANTIBANDING_MODE_50HZ", 1149 "OHOS_CONTROL_AE_ANTIBANDING_MODE_60HZ", 1150 "OHOS_CONTROL_AE_ANTIBANDING_MODE_AUTO" 1151 ]; 1152 1153 ``` 1154 1155The **camera_host_config.hcs** file under **hdi_impl** contains the physical/logical camera configuration and capability configuration. The physical/logical camera configuration needs to be used in the HAL, and the logical camera and capability configuration need to be reported to the upper layer. Add the capability configuration based on the adapted chip product. The used capability values are key-value pairs, which are defined in **//drivers/peripheral/camera/hal/hdi_impl/include/camera_host/metadata_enum_map.h**. 1156 1157``` 1158 normal_preview :: pipeline_spec { 1159 name = "normal_preview"; 1160 v4l2_source :: node_spec { 1161 name = "v4l2_source#0"; 1162 status = "new"; 1163 out_port_0 :: port_spec { 1164 name = "out0"; 1165 peer_port_name = "in0"; 1166 peer_port_node_name = "sink#0"; 1167 direction = 1; 1168 width = 0; 1169 height = 0; 1170 format = 0; 1171 } 1172 } 1173 sink :: node_spec { 1174 name = "sink#0"; 1175 status = "new"; 1176 stream_type = "preview"; 1177 in_port_0 :: port_spec { 1178 name = "in0"; 1179 peer_port_name = "out0"; 1180 peer_port_node_name = "v4l2_source#0"; 1181 direction = 0; 1182 } 1183 } 1184 } 1185``` 1186 1187 The **config.hcs** file under **pipeline_core** uses the pipeline connection mode. Nodes of each flow and connection modes are classified by scenario. 1188 1189The preceding is an example of the preview scenario. **normal_preview** is the scenario name, **source** and **sink** are nodes, **source** is the data source, and **sink** is the end. The source is the first node, and the node name is **source#0**. **status** and **in/out_port** indicate the node status and input/output port configurations, respectively. 1190 1191 Take **in_port_0** as an example. **name = "in0"** indicates that the input port is port0, the peer end is the out0 port of the source node, and **direction** indicates whether the source node and the peer node are directly connected. If a new chip is added, you must configure this file based on the actual connection mode. 1192 1193When adding a function node, you need to inherit the **NodeBase** class and register the node in the .cpp file. For details, see the implemented nodes in **//drivers/peripheral/camera/hal/pipeline_core/nodes/src**. 1194 1195 1196 root { 1197 module = ""; 1198 template stream_info { 1199 id = 0; 1200 name = ""; 1201 } 1202 template scene_info { 1203 id = 0; 1204 name = ""; 1205 } 1206 preview :: stream_info { 1207 id = 0; 1208 name = "preview"; 1209 } 1210 video :: stream_info { 1211 id = 1; 1212 name = "video"; 1213 } 1214 1215 1216The **param.hcs** file defines the scenario, flow type name, and flow ID. In the pipeline, the flow ID is used to identify the flow type. Therefore, you need to add the definition here. 1217 1218#### Chipset and Platform Adaptation 1219 1220The platform contains the platform common code, such as the Linux standard v4l2 adaptation API definition, common nodes adapted the v4l2 framework, and common **device_manager** adapted the v4l2 framework. The directory structure is as follows: 1221 1222 drivers/peripheral/camera/hal/adapter/platform 1223 ├── mpp 1224 │ └── src 1225 │ ├── device_manager 1226 │ └── pipeline_core 1227 └── v4l2 1228 └── src 1229 ├── device_manager 1230 ├── driver_adapter 1231 └── pipeline_core 1232 1233The **v4l2** in the **platform** directory contains **src**. **driver_adapter** in **src** is the standard adaptation API of Linux v4l2. If custom functions are required, you can inherit **driver_adapter**, and implement the custom function APIs in the chipset. If there is no chip custom function, you can directly use the existing **driver_adapter**. 1234 1235Nodes in the **platform** directory are hardware modules **v4l2_source_node** and **uvc_node** implemented based on the Linux v4l2 standard. The **uvc_node** is used for USB hot swap devices and is also a standard Linux API and can be directly used. The following shows the API declaration header file of **v4l2_source_node**. 1236 1237 1238 namespace OHOS::Camera { 1239 class V4L2SourceNode : public SourceNode { 1240 public: 1241 V4L2SourceNode(const std::string& name, const std::string& type); 1242 ~V4L2SourceNode() override; 1243 RetCode Init(const int32_t streamId) override; 1244 RetCode Start(const int32_t streamId) override; 1245 RetCode Flush(const int32_t streamId) override; 1246 RetCode Stop(const int32_t streamId) override; 1247 RetCode GetDeviceController(); 1248 void SetBufferCallback() override; 1249 RetCode ProvideBuffers(std::shared_ptr\<FrameSpec> frameSpec) override; 1250 1251 private: 1252 std::mutex requestLock_; 1253 std::map<int32_t, std::list<int32_t>> captureRequests_ = {}; 1254 std::shared_ptr\<SensorController> sensorController_ = nullptr; 1255 std::shared_ptr\<IDeviceManager> deviceManager_ = nullptr; 1256 }; 1257 } // namespace OHOS::Camera 1258 1259 **Init**: initializes modules. 1260 1261**Start**: enables functions, such as start stream. 1262 1263 **Stop**: disables functions. 1264 1265 **GetDeviceController** is the controller API for obtaining the deviceManager. 1266 1267Chipset is the code related to a specific chip platform, for example, the RK3568 development board. The **device_manager** directory stores the configuration files of the sensors adapted the development board. The **pipeline_core** directory can store pipeline nodes added to meet specific requirements. 1268 1269``` 1270 device/board/hihope/rk3568/camera 1271 ├── BUILD.gn 1272 ├── camera_demo 1273 │ └── project_camera_demo.h 1274 ├── include 1275 │ └── device_manager 1276 ├── product.gni 1277 └── src 1278 ├── device_manager 1279 ├── driver_adapter 1280 └── pipeline_core 1281``` 1282 1283The **device/board/hihope/rk3568/camera/** directory contains **include** and **src**. In **camera_demo** and **src**, **device manager** contains the sensor files adapted the chipset, works with the device management directory of **device manager** on the platform, and connects to the pipeline to implement platform-specific hardware processing APIs, data buffer delivery and reporting, and metadata interaction. 1284 1285The following figure shows the implementation of **device_manager**. The pipeline controls and manages each hardware module. First, you need to obtain the manager of the corresponding device, and obtain the corresponding controller through the manager. The controller interacts with the corresponding driver. 1286 1287  1288 1289Key APIs that need to be implemented in DeviceManager: 1290 1291``` 1292 class SensorController : public IController { 1293 public: 1294 SensorController(); 1295 explicit SensorController(std::string hardwareName); 1296 virtual ~SensorController(); 1297 RetCode Init(); 1298 RetCode PowerUp(); 1299 RetCode PowerDown(); 1300 RetCode Configure(std::shared_ptr<CameraStandard::CameraMetadata> meta); 1301 RetCode Start(int buffCont, DeviceFormat& format); 1302 RetCode Stop(); 1303 RetCode SendFrameBuffer(std::shared_ptr<FrameSpec> buffer); 1304 void SetNodeCallBack(const NodeBufferCb cb); 1305 void SetMetaDataCallBack(const MetaDataCb cb); 1306 void BufferCallback(std::shared_ptr<FrameSpec> buffer); 1307 void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag); 1308 } 1309``` 1310 1311 **PowerUp** is used for power-on. You can call this API to power on a device for OpenCamera. 1312 **PowerDown** is used for power-off. You can call this API to power off a device for CloseCamera. 1313 The **Configures** API is used to deliver metadata. If metadata parameters need to be set on hardware devices, this API can be used to parse and deliver metadata parameters. 1314 The **Start** API is used to enable the hardware module, and is called when each node in the pipeline is enabled. You can define the implementation as required. For example, the start operation of the sensor can be implemented here. 1315 **Stop** and **Start** are reverse operations. You can call **Stop** for a stop operation. 1316 **SendFrameBuffer** is an API for delivering the buffer of each frame. All buffer interaction operations with the driver are performed through this API. 1317 SetNodeCallBack is a pipeline. This API is used to set the buffer callback function to DeviceManager. 1318 SetMetaDataCallBack is a metadata callback. This API is used to report the metadata obtained from the bottom layer to the upper layer. 1319 BufferCallback is used to upload the filled data buffer of each frame. This API is used to report the buffer to the pipeline. 1320 **SetAbilityMetaDataTag** specifies the types of metadata to be obtained from the bottom layer. The framework can obtain information about one or more types of hardware devices separately. Therefore, you can use this API to obtain the required metadata. 1321 1322 For details about other APIs, see **drivers/peripheral/camera/hal/adapter/platform/v4l2/src/device_manager/**. 1323 1324#### IPP Adaptation 1325 1326The IPP is an algorithm plugin module in the pipeline. It is loaded by the ippnode to perform algorithm processing on stream data. The ippnode supports simultaneous input of multiple channels of data and output of only one channel of data. The algorithm plugin loaded by the ippnode is specified in the following .hcs file: 1327vendor/${product_company}/${product_name}/hdf_config/uhdf/camera/pipeline_core/ipp_algo_config.hcs 1328 1329``` 1330 root { 1331 module="sample"; 1332 ipp_algo_config { 1333 algo1 { 1334 name = "example"; 1335 description = "example algorithm"; 1336 path = "libcamera_ipp_algo_example.z.so"; 1337 mode = "IPP_ALGO_MODE_NORMAL"; 1338 } 1339 } 1340 } 1341 1342``` 1343 1344 **name**: algorithm plugin name. 1345 **description**: functions of the algorithm plugin. 1346 **path**: path of the algorithm plugin. 1347 **mode**: running mode of the algorithm plugin. 1348 1349The running modes of the algorithm plugin are provided by IppAlgoMode in **drivers/peripheral/camera/hal/pipeline_core/ipp/include/ipp_algo.h** and can be extended as required. 1350 1351``` 1352 enum IppAlgoMode { 1353 IPP_ALGO_MODE_BEGIN, 1354 IPP_ALGO_MODE_NORMAL = IPP_ALGO_MODE_BEGIN, 1355 IPP_ALGO_MODE_BEAUTY, 1356 IPP_ALGO_MODE_HDR, 1357 IPP_ALGO_MODE_END 1358 }; 1359``` 1360 1361The algorithm plugin is compiled by the GN file **device/${product_company}/${device_name}/camera/BUILD.gn**. The algorithm plugin needs to implement the following APIs (specified by **ipp_algo.h**) for the ippnode to call: 1362 1363 typedef struct IppAlgoFunc { 1364 int (*Init)(IppAlgoMeta* meta); 1365 int (*Start)(); 1366 int (*Flush)(); 1367 int (*Process)(IppAlgoBuffer* inBuffer[], int inBufferCount, IppAlgoBuffer* outBuffer, IppAlgoMeta* meta); 1368 int (*Stop)(); 1369 } IppAlgoFunc; 1370 1371 1) **Init**: initializes the algorithm plugin. It is called by the ippnode before the start operation. **IppAlgoMeta** is defined in **ipp_algo.h** to provide a channel for transferring non-image data between the ippnode and algorithm plugin, such as the current running scenario and face coordinates output after algorithm processing, which can be extended as required. 1372 2) **Start**: called by the ippnode for the start operation. 1373 3) **Flush**: refreshes data. This API is called by the ippnode before the stop operation. When this API is called, the algorithm plugin needs to stop processing as quickly as possible. 1374 4) **Process**: data processing API. Each frame of data is input to the algorithm plugin for processing through this API. **inBuffer** is a group of input buffers, **inBufferCount** is the number of input buffers, **outBuffer** is the output buffer, **meta** is the non-image data generated during algorithm processing, and **IppAlgoBuffer** is defined in **ipp_algo.h**. 1375 5) **Stop**: called by the ippnode for the stop operation. 1376 1377 1378``` 1379typedef struct IppAlgoBuffer { 1380 void* addr; 1381 unsigned int width; 1382 unsigned int height; 1383 unsigned int stride; 1384 unsigned int size; 1385 int id; 1386 } IppAlgoBuffer; 1387``` 1388 1389In the preceding code, **id** indicates the ID of the port corresponding to the ippnode. For example, if the ID of **inBuffer[0]** is 0, **inBuffer[0]** corresponds to input port 0 of the ippnode. Note that **outBuffer** can be empty. In this case, one input buffer is transferred to the next node by the ippnode as the output buffer. At least one buffer in **inBuffer** is not empty. The input and output buffers are determined by the pipeline configuration. 1390For example, in the common preview scenario where there is no algorithm processing and only one channel of photographing data is transmitted to the ippnode, there is only one input buffer and the output buffer is empty. That is, the input buffer of the algorithm plugin is transparently transmitted. 1391For example, in the scenario where the algorithm plugin combines two channels of preview image data, the first channel of buffer needs to be previewed and sent for display. Copy the image of the second channel to the buffer of the first channel. In this case, there are two input buffers and the output buffer is empty. 1392For example, in the preview data format conversion scenario in the algorithm plugin, YUV data is converted into RGBA data. If there is only one YUV input buffer, the RGBA buffer cannot be output. In this case, a new buffer is required, and the output port buffer of the ippnode is transferred to the algorithm plugin as **outBuffer**. That is, there is only one input buffer and one output buffer. 1393 1394For details about the port configuration of the ippnode, see the description of **config.hcs** in section 3.3. 1395 1396#### V4L2 Driver Adaptation Instance 1397 1398This section describes how to adapt the RK3568 development board in the v4l2 framework. 1399 1400 Distinguish the code related to the V4L2 platform and place it in the **drivers/peripheral/camera/hal/adapter/platform/v4l2** directory, which contains the **device_manager**, **driver_adapter**, and **pipeline_core** directories. The **driver_adapter** directory stores the code related to the v4l2 protocol. They can be used to interact with the v4l2 underlying driver. The **Pipeline_core** directory in this directory and the code in **drivers/peripheral/camera/hal/pipeline_core** form the pipeline framework. **v4l2_source_node** and **uvc_node** are dedicated nodes for v4l2. The **device_manager** directory stores the code for interaction between **device_manager** and **pipeline** and between the south and v4l2 adapter. 1401 1402``` 1403 drivers/peripheral/camera/hal/adapter/platform/v4l2/src/ 1404 ├── device_manager 1405 │ ├── enumerator_manager.cpp 1406 │ ├── flash_controller.cpp 1407 │ ├── flash_manager.cpp 1408 │ ├── idevice_manager.cpp 1409 │ ├── include 1410 │ ├── isp_controller.cpp 1411 │ ├── isp_manager.cpp 1412 │ ├── sensor_controller.cpp 1413 │ ├── sensor_manager.cpp 1414 │ └── v4l2_device_manager.cpp 1415 ├── driver_adapter 1416 │ ├── BUILD.gn 1417 │ ├── include 1418 │ ├── main_test 1419 │ └── src 1420 └── pipeline_core 1421 └── nodes 1422``` 1423 1424 Distinguish the code related to the V4L2 chipset and place it in the **device/ ${product_company}/${device_name} /camera** directory. 1425 1426``` 1427 ├── BUILD.gn 1428 ├── camera_demo 1429 │ └── project_camera_demo.h 1430 ├── include 1431 │ └── device_manager 1432 ├── product.gni 1433 └── src 1434 ├── device_manager 1435 ├── driver_adapter 1436 └── pipeline_core 1437``` 1438 1439The **driver_adapter** directory contains the header files of the test cases related to the RK3568 driver adapter. The **Camera_demo** directory stores the chipset-related header files of the demo test cases in the camera HAL. **device_manager** stores the code for the camera sensor adapted RK3568 to read device capabilities. In the directory, **project_hardware.h** is critical and stores the list of devices supporting the chipset. The sample code is as follows: 1440 1441``` 1442 namespace OHOS::Camera { 1443 std::vector<HardwareConfiguration> hardware = { 1444 {CAMERA_FIRST, DM_M_SENSOR, DM_C_SENSOR, (std::string) "rkisp_v5"}, 1445 {CAMERA_FIRST, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, 1446 {CAMERA_FIRST, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"}, 1447 {CAMERA_SECOND, DM_M_SENSOR, DM_C_SENSOR, (std::string) "Imx600"}, 1448 {CAMERA_SECOND, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, 1449 {CAMERA_SECOND, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"} 1450 }; 1451 } // namespace OHOS::Camera 1452``` 1453 1454You can modify compilation options so that the compilation of V4L2 and other framework code can be distinguished based on different compilation chipsets. Add **device/${product_company}/${device_name}/camera/product.gni**. 1455 1456``` 1457 camera_product_name_path = "//vendor/${product_company}/${product_name}" 1458 camera_device_name_path = "//device/board/${product_company}/${device_name}" 1459 is_support_v4l2 = true 1460 if (is_support_v4l2) { 1461 is_support_mpi = false 1462 defines += [ "SUPPORT_V4L2" ] 1463 chipset_build_deps = "$camera_device_name_path/camera/:chipset_build" 1464 camera_device_manager_deps = 1465 "$camera_device_name_path/camera/src/device_manager:camera_device_manager" 1466 camera_pipeline_core_deps = 1467 "$camera_device_name_path/camera/src/pipeline_core:camera_pipeline_core" 1468 } 1469``` 1470 1471When **product.gni** is loaded by **//drivers/peripheral/camera/hal/camera.gni**, the V4L2 code needs to be compiled. In **//drivers/peripheral/camera/hal/camera.gni**, load the corresponding GNI file based on **product_name** and **device_name** input during compilation. 1472 1473 ``` 1474 import("//build/ohos.gni") 1475 if ("${product_name}" == "ohos-arm64") { 1476 import( 1477 "//drivers/peripheral/camera/hal/adapter/chipset/rpi/rpi3/device/camera/product.gni") 1478 } else if ("${product_name}" == "Hi3516DV300") { 1479 import( 1480 "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") 1481 ``` 1482 1483 Different chipsets are compiled based on **chipset_build_deps**, **camera_device_manager_deps**, and **camera_pipeline_core_deps** in **drivers/peripheral/camera/hal/BUILD.gn**. 1484 1485 print("product_name : , ${product_name}") 1486 group("camera_hal") { 1487 if (is_standard_system) { 1488 deps = [ 1489 "$camera_path/../interfaces/hdi_ipc/client:libcamera_client", 1490 "buffer_manager:camera_buffer_manager", 1491 "device_manager:camera_device_manager", 1492 "hdi_impl:camera_hdi_impl", 1493 "init:ohos_camera_demo", 1494 "pipeline_core:camera_pipeline_core", 1495 "utils:camera_utils", 1496 ] 1497 deps += [ "${chipset_build_deps}" ] 1498 } 1499 1500 1501The camera HAL layer shields the differences between the platform and chip and provides unified APIs for external systems (camera service or test program). The APIs are defined in **drivers/peripheral/camera/interfaces/include**. 1502 1503 ├── icamera_device_callback.h 1504 ├── icamera_device.h 1505 ├── icamera_host_callback.h 1506 ├── icamera_host.h 1507 ├── ioffline_stream_operator.h 1508 ├── istream_operator_callback.h 1509 ├── istream_operator.h 1510 1511During the test, you only need to test the provided external APIs to test the complete code of the camera HAL layer. For details about the APIs, see **README_zh.md** in **drivers/peripheral/camera/interfaces** and header file API definition. For details about the API call process, see the test demo **drivers/peripheral/camera/hal/init**. 1512 1513### FAQs of Camera Adaptation 1514 1515#### Modifying the SUBWINDOW_TYPE and Display Format 1516 1517Change the RGBA888 display mode from **video** to **normal** (specified by **SUBWINDOW_TYPE**). 1518 1519The OpenHarmony implements the camera of the Hi3516 platform earlier, which uses the PIXEL_FMT_YCRCB_420_SP format for display. However, the RK3568 needs to convert YUV420 preview streams into PIXEL_FMT_RGBA_8888 and send them to the screen for correct display. Modify the following content in the **foundation/ace/ace_engine/frameworks/core/components/camera/standard_system/camera.cpp** file. This file is compiled in **libace.z.so**. 1520 1521 1522 #ifdef PRODUCT_RK 1523 previewSurface_->SetUserData(SURFACE_FORMAT, std::to_string(PIXEL_FMT_RGBA_8888)); 1524 previewSurface_->SetUserData(CameraStandard::CameraManager::surfaceFormat, 1525 std::to_string(OHOS_CAMERA_FORMAT_RGBA_8888)); 1526 #else 1527 previewSurface_->SetUserData(SURFACE_FORMAT, std::to_string(PIXEL_FMT_YCRCB_420_SP)); 1528 previewSurface_->SetUserData(CameraStandard::CameraManager::surfaceFormat, 1529 std::to_string(OHOS_CAMERA_FORMAT_YCRCB_420_SP)); 1530 #endif 1531 1532The following content in the **foundation/multimedia/camera_standard/services/camera_service/src/hstream_repeat.cpp** file is compiled in **libcamera_service.z.so**: 1533 1534``` 1535void HStreamRepeat::SetStreamInfo(std::shared_ptr<Camera::StreamInfo> streamInfo) 1536 { 1537 int32_t pixelFormat; 1538 auto it = g_cameraToPixelFormat.find(format_); 1539 if (it != g_cameraToPixelFormat.end()) { 1540 pixelFormat = it->second; 1541 } else { 1542 #ifdef RK_CAMERA 1543 pixelFormat = PIXEL_FMT_RGBA_8888; 1544 #else 1545 pixelFormat = PIXEL_FMT_YCRCB_420_SP; 1546 #endif 1547``` 1548 1549In the preceding information, the Hi3516 platform uses the VO module driver to directly send display signals. Therefore, the subwindows mode configured in the ACE is **SUBWINDOW_TYPE_VIDEO**. Modify the **foundation/ace/ace_engine/frameworks/core/components/camera/standard_system/camera.cpp** file. This file is compiled in **libace.z.so**. 1550 1551 #ifdef PRODUCT_RK 1552 option->SetWindowType(SUBWINDOW_TYPE_NORMAL); 1553 #else 1554 option->SetWindowType(SUBWINDOW_TYPE_VIDEO); 1555 #endif 1556 1557#### Adding rk_codec_node 1558 1559RGB conversion and H.264 and JPEG encoding and decoding are performed on this node. Each node in the pipeline model of the camera HAL is in camera data rotation. Currently, the camera HAL V4L2 adapter supports data rotation of only one stream. Therefore, the photographing and recording streams must be copied from a single preview stream. Currently, OpenHarmony does not have a dedicated server for codec and RGB conversion and JPEG compression. In this case, you can only create a dedicated node in the camera HAL, that is, **rk_codec_node**. 1560Add the **rk_codec_node** connection model to HCS. 1561Modify the **vendor/hihope/rk3568/hdf_config/uhdf/camera/pipeline_core/config.hcs** file. 1562 1563 1564 normal_preview_snapshot :: pipeline_spec { 1565 name = "normal_preview_snapshot"; 1566 v4l2_source :: node_spec { 1567 name = "v4l2_source#0"; 1568 status = "new"; 1569 out_port_0 :: port_spec { 1570 name = "out0"; 1571 peer_port_name = "in0"; 1572 peer_port_node_name = "fork#0"; 1573 direction = 1; 1574 } 1575 } 1576 fork :: node_spec { 1577 name = "fork#0"; 1578 status = "new"; 1579 in_port_0 :: port_spec { 1580 name = "in0"; 1581 peer_port_name = "out0"; 1582 peer_port_node_name = "v4l2_source#0"; 1583 direction = 0; 1584 } 1585 out_port_0 :: port_spec { 1586 name = "out0"; 1587 peer_port_name = "in0"; 1588 peer_port_node_name = "RKCodec#0"; 1589 direction = 1; 1590 } 1591 out_port_1 :: port_spec { 1592 name = "out1"; 1593 peer_port_name = "in0"; 1594 peer_port_node_name = "RKCodec#1"; 1595 direction = 1; 1596 } 1597 } 1598 RKCodec_1 :: node_spec { 1599 name = "RKCodec#0"; 1600 status = "new"; 1601 in_port_0 :: port_spec { 1602 name = "in0"; 1603 peer_port_name = "out0"; 1604 peer_port_node_name = "fork#0"; 1605 direction = 0; 1606 } 1607 out_port_0 :: port_spec { 1608 name = "out0"; 1609 peer_port_name = "in0"; 1610 peer_port_node_name = "sink#0"; 1611 direction = 1; 1612 } 1613 } 1614 RKCodec_2 :: node_spec { 1615 name = "RKCodec#1"; 1616 1617 1618The preview and photographing streams are used as an example. **v4l2_source_node** is the data source and flows to **fork_node**. **rork_node** directly sends the preview data to the RKCodec node and copies the photographing data stream to the RKCodec node for conversion. The converted data is sent to the sink node and then to the consumer of the buffer. 1619 1620Add the compilation of **rk_codec_node.cpp** and related dependent libraries to **device/board/hihope/rk3568/camera/src/pipeline_core/BUILD.gn**. **librga** is the library for converting YUV images into RGB images, **libmpp** is the library for encoding and decoding YUV images into H.264 images, and **libjpeg** is the library for compressing YUV images into JPEG images. 1621 1622 1623 ohos_shared_library("camera_pipeline_core") { 1624 sources = [ 1625 "$camera_device_name_path/camera/src/pipeline_core/node/rk_codec_node.cpp", 1626 "$camera_path/adapter/platform/v4l2/src/pipeline_core/nodes/uvc_node/uvc_node.cpp", 1627 "$camera_path/adapter/platform/v4l2/src/pipeline_core/nodes/v4l2_source_node/v4l2_source_node.cpp", 1628 deps = [ 1629 "$camera_path/buffer_manager:camera_buffer_manager", 1630 "$camera_path/device_manager:camera_device_manager", 1631 "//device/soc/rockchip/hardware/mpp:libmpp", 1632 "//device/soc/rockchip/hardware/rga:librga", 1633 "//foundation/multimedia/camera_standard/frameworks/native/metadata:metadata", 1634 "//third_party/libjpeg:libjpeg_static", 1635 1636 1637 Main APIs of the **openharmony/device/board/hihope/rk3568/camera/src/pipeline_core/node/rk_codec_node.cpp** file: 1638 1639 1640 void RKCodecNode::DeliverBuffer(std::shared_ptr\<IBuffer>& buffer) 1641 { 1642 if (buffer == nullptr) { 1643 CAMERA_LOGE("RKCodecNode::DeliverBuffer frameSpec is null"); 1644 return; 1645 } 1646 1647 int32_t id = buffer->GetStreamId(); 1648 CAMERA_LOGE("RKCodecNode::DeliverBuffer StreamId %{public}d", id); 1649 if (buffer->GetEncodeType() == ENCODE_TYPE_JPEG) { 1650 Yuv420ToJpeg(buffer); 1651 } else if (buffer->GetEncodeType() == ENCODE_TYPE_H264) { 1652 Yuv420ToH264(buffer); 1653 } else { 1654 Yuv420ToRGBA8888(buffer); 1655 } 1656 1657The data stream generated by **fork_node** is delivered to the **DeliverBuffer** API of the rk_codec_node. The API performs conversion based on the value of **EncodeType**. The converted buffers are delivered to the next-level node for processing until they are delivered to the buffer consumer. 1658 1659#### Inconsistent H.264 Frame and Audio Timestamps 1660 1661Issue: When creating a recorder, ACE obtains both audio and video data and combines them into an .mp4 file. However, during the actual synthesis, the timestamps in the audio and video information must be consistent. If they are inconsistent, the Recorder fails. After the recording button is clicked in the camera app, the recording cannot be stopped properly. After the recording is forcibly stopped, the MP4 file is empty. 1662 1663Solution: Find the method of obtaining the audio timestamp of the audio module. 1664 1665``` 1666 int32_t AudioCaptureAsImpl::GetSegmentInfo(uint64_t &start) 1667 { 1668 CHECK_AND_RETURN_RET(audioCapturer_ != nullptr, MSERR_INVALID_OPERATION); 1669 AudioStandard::Timestamp timeStamp; 1670 auto timestampBase = AudioStandard::Timestamp::Timestampbase::MONOTONIC; 1671 CHECK_AND_RETURN_RET(audioCapturer_->GetAudioTime(timeStamp, timestampBase), MSERR_UNKNOWN); 1672 CHECK_AND_RETURN_RET(timeStamp.time.tv_nsec >= 0 && timeStamp.time.tv_sec >= 0, MSERR_UNKNOWN); 1673 if (((UINT64_MAX - timeStamp.time.tv_nsec) / SEC_TO_NANOSECOND) <= static_cast<uint64_t>(timeStamp.time.tv_sec)) { 1674 MEDIA_LOGW("audio frame pts too long, this shouldn't happen"); 1675 } 1676 start = timeStamp.time.tv_nsec + timeStamp.time.tv_sec * SEC_TO_NANOSECOND; 1677 MEDIA_LOGI("timestamp from audioCapturer: %{public}" PRIu64 "", start); 1678 return MSERR_OK; 1679 } 1680``` 1681 1682In the **audio_capture_as_impl.cpp** file, and the camera module uses **CLOCK_REALTIME**, that is, the actual system time. 1683 1684 1685 mppStatus_ = 1; 1686 buf_size = ((MpiEncTestData *)halCtx_)->frame_size; 1687 1688 ret = hal_mpp_encode(halCtx_, dma_fd, (unsigned char *)buffer->GetVirAddress(), &buf_size); 1689 SearchIFps((unsigned char *)buffer->GetVirAddress(), buf_size, buffer); 1690 1691 buffer->SetEsFrameSize(buf_size); 1692 clock_gettime(CLOCK_MONOTONIC, &ts); 1693 timestamp = ts.tv_nsec + ts.tv_sec * TIME_CONVERSION_NS_S; 1694 buffer->SetEsTimestamp(timestamp); 1695 CAMERA_LOGI("RKCodecNode::Yuv420ToH264 video capture on\n"); 1696 1697 Solution: Change the time type in **rk_codec_node.cpp** of camera HAL to **CLOCK_MONOTONIC**. 1698 1699#### Linux 4.19 Match Error After the Value of time_t Is Changed to 64-Bit 1700 1701Background: When RK3568 encounters this problem, the upper-layer 32-bit system is running, and the bottom-layer kernel is 64-bit kernel of Linux 4.19. In a 32-bit system environment, the value of **time_t** is of the long type, that is, 32-bit. However, after the value of **time_t** is changed to the 64-bit, an error is reported by camera V4l2 in the IOCTL. 1702 1703 TYPEDEF _Int64 time_t; 1704 TYPEDEF _Int64 suseconds_t; 1705 1706 The specific error and temporary solution are as follows: 1707 1708 1. When an error occurs, search for **camera_host** in Hilog. It is found that a **Not a tty** error is reported when the **VIDIOC_QUERYBUF** CMD is delivered in the **V4L2AllocBuffer** API. The sample code is as follows: 1709 1710``` 1711V4L2AllocBuffer error:ioctl VIDIOC_QUERYBUF failed: Not a tty 1712 1713``` 1714 1715 RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr\<FrameSpec>& frameSpec) 1716 { 1717 struct v4l2_buffer buf = {}; 1718 struct v4l2_plane planes[1] = {}; 1719 CAMERA_LOGD("V4L2AllocBuffer\n"); 1720 1721 if (frameSpec == nullptr) { 1722 CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n"); 1723 return RC_ERROR; 1724 } 1725 1726 switch (memoryType_) { 1727 case V4L2_MEMORY_MMAP: 1728 // to do something 1729 break; 1730 case V4L2_MEMORY_USERPTR: 1731 buf.type = bufferType_; 1732 buf.memory = memoryType_; 1733 buf.index = (uint32_t)frameSpec->buffer_->GetIndex(); 1734 1735 if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { 1736 buf.m.planes = planes; 1737 buf.length = 1; 1738 } 1739 CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index); 1740 1741 if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { 1742 CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno)); 1743 return RC_ERROR; 1744 2. Generally, the CMD called by the IOCTL system uses **sizeof** of the third parameter as the CMD value and transfers the value to the kernel to search for the corresponding switch case in the kernel. As shown below, **v4l2_buffer** is the main part of the **VIDIOC_QUERYBUF** macro value. If the size of **v4l2_buffer** changes, the value of **VIDIOC_QUERYBUF** also changes accordingly. 1745 1746``` 1747 #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) 1748 #define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers) 1749 #define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer) 1750 #define VIDIOC_G_FBUF _IOR('V', 10, struct v4l2_framebuffer) 1751``` 1752 1753 3. When the **CONFIG_COMPAT** macro is enabled in the kernel, the 32-bit system is compatible with the 64-bit kernel. For the IOCTL delivered by the 32-bit system, the API in the following is used to convert the CMD value from 32-bit to 64-bit. 1754 1755 long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) 1756 { 1757 struct video_device *vdev = video_devdata(file); 1758 long ret = -ENOIOCTLCMD; 1759 1760 if (!file->f_op->unlocked_ioctl) 1761 return ret; 1762 1763 if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) 1764 ret = do_video_ioctl(file, cmd, arg); 1765 else if (vdev->fops->compat_ioctl32) 1766 ret = vdev->fops->compat_ioctl32(file, cmd, arg); 1767 1768 4. In this case, a value of **VIDIOC_QUERYBUF** considered by the kernel is defined in the kernel. 1769 1770 #define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) 1771 #define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) 1772 #define VIDIOC_QUERYBUF32_TIME32 _IOWR('V', 9, struct v4l2_buffer32_time32) 17735. As mentioned above, the value of **time_t** in the upper-layer MUSL has been changed from 32-bit to 64-bit, and **time_t** is used in struct timeval in the v4l2_buffer structure. In this case, the size of v4l2_buffer at the application layer is different from that at the kernel layer because **kernel_time_t** defined by the kernel in **time.h** is used during compilation in struct timeval of the kernel. As a result, the **sizeof** calculation of v4l2_buffer at the application layer is inconsistent with that at the driver layer, and CMD error cannot be found after the kernel mode is called. 1774 1775``` 1776 struct v4l2_buffer { 1777 __u32 index; 1778 __u32 type; 1779 __u32 bytesused; 1780 __u32 flags; 1781 __u32 field; 1782 struct timeval timestamp; 1783 struct v4l2_timecode timecode; 1784 __u32 sequence; 1785``` 17866. The temporary solution is to change struct timeval in **videodev2.h** to a temporarily defined structure to ensure that the sizes of the upper and lower layers are the same. The sample code is as follows: 1787 1788``` 1789 struct timeval1 { 1790 long tv_sec; 1791 long tv_usec; 1792 } 1793 struct v4l2_buffer { 1794 __u32 index; 1795 __u32 type; 1796 __u32 bytesused; 1797 __u32 flags; 1798 __u32 field; 1799 struct timeval1 timestamp; 1800 struct v4l2_timecode timecode; 1801``` 1802 1803 Solution: 1804 1805 There are only two ways to completely solve this problem. 1. Upgrade the system to 64-bit to ensure that the size of the **time_t** variable in user mode is the same as that in kernel mode. 2. Upgrade the kernel to a version later than 5.10. 1806 This problem is solved in the **videodev2.h** file of kernel 5.10. As shown below, the 64-bit **time_t** value is considered during kernel compilation. 1807 1808``` 1809struct v4l2_buffer { 1810 __u32 index; 1811 __u32 type; 1812 __u32 bytesused; 1813 __u32 flags; 1814 __u32 field; 1815 #ifdef __KERNEL__ 1816 struct __kernel_v4l2_timeval timestamp; 1817 #else 1818 struct timeval timestamp; 1819 #endif 1820 struct v4l2_timecode timecode; 1821 } 1822 1823 struct __kernel_v4l2_timeval { 1824 long long ._sec; 1825 #if defined(__sparc__) && defined(__arch64__) 1826 int tv_usec; 1827 int __pad; 1828 #else 1829 long long tv_usec; 1830 #endif 1831 }; 1832``` 1833#### H.264 Key Frame Obtaining and Reporting 1834 1835 In addition to the encoded and decoded data, H.264 also needs to report the key frame information. So, how can we know whether a frame is a key frame? The information is required during MP4 encoding. You can determine a key frame as follows: Analyze the NALU header information. NALU type & 0x1f indicate the frame type. The NALU header starts with 0x00000001 or 0x000001. This figure shows the frame types when **nal_unit_type** is set to different values. Pay attention to the IDR frame information when **nal_unit_type** is set to **5**. 1836  1837 1838 1839 IDR frame analysis is coded in the **rk_cedec_node.cpp** file. 1840 1841 static constexpr uint32_t nalBit = 0x1F; 1842 #define NAL_TYPE(value) ((value) & nalBit) 1843 void RKCodecNode::SearchIFps(unsigned char* buf, size_t bufSize, std::shared_ptr\<IBuffer>& buffer) 1844 { 1845 size_t nalType = 0; 1846 size_t idx = 0; 1847 size_t size = bufSize; 1848 constexpr uint32_t nalTypeValue = 0x05; 1849 1850 if (buffer == nullptr || buf == nullptr) { 1851 CAMERA_LOGI("RKCodecNode::SearchIFps parameter == nullptr"); 1852 return; 1853 } 1854 1855 for (int i = 0; i < bufSize; i++) { 1856 int ret = findStartCode(buf + idx, size); 1857 if (ret == -1) { 1858 idx += 1; 1859 size -= 1; 1860 } else { 1861 nalType = NAL_TYPE(buf[idx + ret]); 1862 CAMERA_LOGI("ForkNode::ForkBuffers nalu == 0x%{public}x buf == 0x%{public}x \n", nalType, buf[idx + ret]); 1863 1864Each buffer converted by H.264 is transferred to the **SearchIFps** API to search for IDR frames. The **findStartCode()** API scans the content in the buffer byte by byte to find the NALU header. 1865 1866 ``` 1867 int RKCodecNode::findStartCode(unsigned char *data, size_t dataSz) 1868 { 1869 constexpr uint32_t dataSize = 4; 1870 constexpr uint32_t dataBit2 = 2; 1871 constexpr uint32_t dataBit3 = 3; 1872 1873 if (data == nullptr) { 1874 CAMERA_LOGI("RKCodecNode::findStartCode parameter == nullptr"); 1875 return -1; 1876 } 1877 1878 if ((dataSz > dataSize) && (data[0] == 0) && (data[1] == 0) && \ 1879 (data[dataBit2] == 0) && (data[dataBit3] == 1)) { 1880 return 4; // 4:start node 1881 } 1882 1883 return -1; 1884 } 1885 ``` 1886After the NALU header is found, **nal_unit_type** is found for &0x1F. If the value of **nal_unit_type** is **5**, key frame information is marked and reported through the **buffer->SetEsKeyFrame(1)** API. 1887 1888## TP 1889 1890### TP Driver Model 1891 1892This model mainly defines and implements the following types of HDIs of the input module, allowing upper-layer input services to perform operations for the input devices: 1893 1894- **Input Manager**: manages input devices, including enabling and disabling input devices and obtaining the device list. 1895- **Input Reporter**: reports input events, including registering and unregistering data reporting callbacks. 1896- **Input Controller**: controls input devices, including obtaining the device information and device type, and setting power supply status. 1897 1898**Figure 1** HDI architecture of the input module 1899 1900 1901 1902The source code directory structure is as follows: 1903 1904``` 1905/drivers/peripheral/input 1906├── hal # HAL code 1907│ └── include # HAL header files 1908│ └── src # HAL code implementation 1909├── interfaces # Driver capability APIs provided for upper-layer services 1910│ └── include # APIs exposed externally 1911├── test # Test code 1912│ └── unittest # Unit test code 1913``` 1914 1915For details, see [README](https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md) of the input subsystem. 1916 1917### TP HDF Driver Adaptation 1918 1919#### Files and Directories Involved in the TP Driver 1920 1921By default, the DAYU 200 platform supports the GT5688 TP IC. 1922 1923Files and directories involved in porting the touch driver on the development board: 1924 19251. Makefile: drivers\adapter\khdf\linux\model\input\Makefile 1926 19272. vendor\hihope\rk3568\hdf_config\khdf\device_info\device_info.hcs 1928 19293. vendor\hihope\rk3568\hdf_config\khdf\input\input_config.hcs 1930 19314. drivers\framework\model\input\driver\touchscreen 1932 1933The adaptation of the TP driver involves the TP driver and HCS configuration. 1934 1935The adaptation of the TP driver depends on the HDF input model. The HDF input model supports device registration, management, data forwarding layer, and HCS parsing in TP, key, and HID scenarios. The input model of the HDF can be abstracted into three layers: driver management layer, common driver layer, and component driver layer. 1936 1937The following figure shows the framework of the HDF input module from the perspective of functions. 1938 1939 1940 1941Due to the high abstraction and integration of the HDF input model, the adapted driver of the TP driver mainly involves the adaptation of the component driver layer. 1942 1943Before adaptation, you need to determine the resources required by the TP. 1944 1945For hardware resources, the TP module requires the following resources on the host: 1946 19471. Interrupt pin 1948 19492. Reset pin 1950 19513. Used I2C group and address of the slave device 1952 19534. TP initialization firmware (usually provided by IC vendors) 1954 19555. Touchscreen resolution 1956 1957For software resources, TP adaptation on the HDF depends on the following HDF basic modules: 1958 19591. HDF GPIO subsystem: sets GPIO pins and interrupt resources. 1960 19612. HDF I2C subsystem: used for I2C communication. 1962 19633. Input model 1964 1965The component driver is used based on the following structures: 1966 1967``` 1968static struct TouchChipOps g_gt911ChipOps = { 1969 .Init = ChipInit, 1970 .Detect = ChipDetect, 1971 .Resume = ChipResume, 1972 .Suspend = ChipSuspend, 1973 .DataHandle = ChipDataHandle, 1974 .UpdateFirmware = UpdateFirmware, 1975 .SetAbility = SetAbility, 1976}; 1977``` 1978 1979**ChipInit** initializes the component driver. 1980 1981**ChipDetect** checks the component validity after initialization. 1982 1983**SetAbility** sets key attributes. 1984 1985**ChipDataHandle** parses key values. 1986 1987**UpdateFirmware** upgrades firmware. 1988 1989**ChipSuspend** suspends components. 1990 1991**ChipResume** resumes components. 1992 1993Implement the preceding API callbacks based on the component features and register the structure with the input model. 1994 1995#### HCS Configuration 1996 1997Add a new component node to **device_info.hcs**. 1998 1999``` 2000device_touch_chip :: device { 2001 device0 :: deviceNode { 2002 policy = 0; 2003 priority = 180; 2004 preload = 0; // The driver is loaded by default. 2005 permission = 0660; 2006 moduleName = "HDF_TOUCH_GT911";// The value must be the same as that in the component driver. 2007 serviceName = "hdf_touch_gt911_service"; 2008 deviceMatchAttr = "zsj_gt911_5p5"; 2009 } 2010 } 2011``` 2012 2013Add component features to **input_config.hcs**. 2014 2015``` 2016chipConfig { 2017 template touchChip { 2018 match_attr = ""; 2019 chipName = "gt911"; 2020 vendorName = "zsj"; 2021 chipInfo = "AAAA11222"; // 4-ProjectName, 2-TP IC, 3-TP Module 2022 /* 0:i2c 1:spi*/ 2023 busType = 0; 2024 deviceAddr = 0x5D; 2025 /* 0:None 1:Rising 2:Failing 4:High-level 8:Low-level */ 2026 irqFlag = 2; 2027 maxSpeed = 400; 2028 chipVersion = 0; //parse Coord TypeA 2029 powerSequence { 2030 /* [type, status, dir , delay] 2031 <type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int 2032 <status> 0:off or low 1:on or high 2:no ops 2033 <dir> 0:input 1:output 2:no ops 2034 <delay> meanings delay xms, 20: delay 20ms 2035 */ 2036 powerOnSeq = [4, 0, 1, 5, 2037 3, 0, 1, 10, 2038 3, 1, 1, 60, 2039 4, 2, 0, 50]; 2040 suspendSeq = [3, 0, 2, 10]; 2041 resumeSeq = [3, 1, 2, 10]; 2042 powerOffSeq = [3, 0, 2, 10, 2043 1, 0, 2, 20]; 2044 } 2045 } 2046``` 2047 2048## Display Adaptation 2049 2050The following tasks need to be performed for display adaptation: graphics service HDI API adaptation, GPU adaptation, and LCD driver adaptation. 2051 2052### Display HDI 2053 2054[Display HDI](https://gitee.com/openharmony/drivers_peripheral/blob/master/display/README_zh.md) provides display driver capabilities for graphics services, including display layer management, display memory management, and hardware acceleration. Display HDI adaptation involves **gralloc** and **display_device**. 2055 2056#### Gralloc Adaptation 2057 2058The gralloc module provides the display memory management function. OpenHarmony provides the reference implementation of Hi3516D V300. Vendors can refer to and adapt the implementation based on the actual situation. The implementation is developed based on the DRM. [Source code link](https://gitee.com/openharmony/drivers_peripheral/tree/master/display/hal/default_standard) 2059 2060The DRM device node is defined in the **//drivers_peripheral/display/hal/default_standard/srd/display_gralloc/display_gralloc_gbm.c** file. You can modify the file as required. 2061 2062``` 2063const char *g_drmFileNode = "/dev/dri/card0"; 2064``` 2065 2066In this implementation, a HiSilicon private IOCTL command code **DRM_IOCTL_HISILICON_GEM_FD_TO_PHYADDR** is defined in the **//drivers_peripheral/display/hal/default_standard/src/display_gralloc/hisilicon_drm.h** file and called in the **//drivers_peripheral/display/hal/default_standard/src/display_gralloc/display_gralloc_gbm.c** file. This function is a HiSilicon private function and can be modified as required during adaptation. 2067 2068``` 2069... 2070 InitBufferHandle(bo, fd, info, priBuffer); 2071 priBuffer->hdl.phyAddr = GetPhysicalAddr(grallocManager->drmFd, fd); 2072 *buffer = &priBuffer->hdl; 2073... 2074``` 2075 2076#### Display Device Adaptation 2077 2078The display device module provides functions such as display device management, layer management, and hardware acceleration. 2079 2080OpenHarmony provides the [DRM-based Hi3516D V300 reference implementation](https://gitee.com/openharmony/drivers_peripheral/tree/master/display/hal/default_standard/src/display_device), which supports hardware composition by default. 2081 2082If the development board does not support hardware composition, skip the initialization of the GFX in the **drm_display.cpp** file. 2083 2084``` 2085drivers_peripheral/blob/master/display/hal/default_standard/src/display_device/drm/drm_display.cpp 2086int32_t DrmDisplay::Init() 2087{ 2088 ... 2089 ... 2090 ret = HdiDisplay::Init(); 2091 DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("init failed")); 2092 auto preComp = std::make_unique<HdiGfxComposition>(); 2093 DISPLAY_CHK_RETURN((preComp == nullptr), DISPLAY_FAILURE, 2094 DISPLAY_LOGE("can not new HdiGfxComposition errno %{public}d", errno)); 2095 ret = preComp->Init(); // GFX initialization, which needs to be skipped. 2096 DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not init HdiGfxComposition")); // Or do not check the return value. 2097 2098 ... 2099} 2100``` 2101 2102In addition, modify the **set_layers** method in the **//drivers_peripheral/display/hal/default_standard/src/display_device/hdi_gfx_composition.cpp** file to use the CPU composition display. 2103 2104``` 2105int32_t HdiGfxComposition::SetLayers(std::vector<HdiLayer *> &layers, HdiLayer &clientLayer) 2106{ 2107 DISPLAY_LOGD("layers size %{public}zd", layers.size()); 2108 mClientLayer = &clientLayer; 2109 mCompLayers.clear(); 2110 for (auto &layer : layers) { 2111 if (CanHandle(*layer)) { 2112#if 0 // CPU composition. 2113 layer->SetDeviceSelect(COMPOSITION_CLIENT); 2114#else 2115 if ((layer->GetCompositionType() != COMPOSITION_VIDEO) && 2116 (layer->GetCompositionType() != COMPOSITION_CURSOR)) { 2117 layer->SetDeviceSelect(COMPOSITION_DEVICE); 2118 } else { 2119 layer->SetDeviceSelect(layer->GetCompositionType()); 2120 } 2121#endif 2122 mCompLayers.push_back(layer); 2123 } 2124 } 2125 DISPLAY_LOGD("composer layers size %{public}zd", mCompLayers.size()); 2126 return DISPLAY_SUCCESS; 2127} 2128``` 2129 2130#### Test and Verification 2131 2132hello_composer test module: It is a test program provided by the Rosen graphics framework and checks whether the functions such as the display process and HDI API are normal. By default, the module is compiled with the system. 2133 2134Code path: 2135 2136``` 2137foundation/graphic/graphic/rosen/samples/composer/ 2138├── BUILD.gn 2139├── hello_composer.cpp 2140├── hello_composer.h 2141├── layer_context.cpp 2142├── layer_context.h 2143└── main.cpp 2144``` 2145 2146The verification is as follows: 2147 21481. Disable the render service. 2149 2150 ``` 2151 service_control stop render_service 2152 ``` 2153 21542. Disable the foundation process. 2155 2156 ``` 2157 service_control stop foundation 2158 ``` 2159 21603. Run **hello_composer** to test related APIs. 2161 2162 ``` 2163 ./hello_composer 2164 ``` 2165 2166Test using devicetest: test module provided by the HDI display module. It is used to test the HDI API, display buffer, and driver capabilities. During the test, the render service and foundation processes also need to be stopped. 2167 2168Code path: **/drivers/peripheral/display/test/unittest/standard** 2169 2170``` 2171├── BUILD.gn 2172├── common 2173│ ├── display_test.h 2174│ ├── display_test_utils.cpp 2175│ └── display_test_utils.h 2176├── display_device 2177│ ├── hdi_composition_check.cpp 2178│ ├── hdi_composition_check.h 2179│ ├── hdi_device_test.cpp 2180│ ├── hdi_device_test.h 2181│ ├── hdi_test_device_common.h 2182│ ├── hdi_test_device.cpp 2183│ ├── hdi_test_device.h 2184│ ├── hdi_test_display.cpp 2185│ ├── hdi_test_display.h 2186│ ├── hdi_test_layer.cpp 2187│ ├── hdi_test_layer.h 2188│ ├── hdi_test_render_utils.cpp 2189│ └── hdi_test_render_utils.h 2190└── display_gralloc 2191 ├── display_gralloc_test.cpp 2192 └── display_gralloc_test.h 2193``` 2194 2195### GPU 2196 2197Compiler Clang 2198 2199``` 2200prebuilts/clang/ohos/linux-x86_64/llvm 2201``` 2202 2203musl Library 2204 2205``` 2206./build.sh --product-name rk3568 --build-target musl_all 2207``` 2208 2209After the compilation is complete, the following header files and libraries are generated in the **out/{product_name}/obj/third_party/musl/usr/lib** directory: 2210 2211``` 221232-bit: arm-linux-ohos 2213 221464-bit: aarch64-linux-ohos 2215``` 2216 2217Source code directory: 2218 2219``` 2220third_party/musl 2221``` 2222 2223GPU compilation parameter reference: 2224 2225``` 2226TARGET_CFLAGS=" -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb --target=arm-linux-ohosmusl -fPIC -ftls-model=global-dynamic -mtls-direct-seg-refs -DUSE_MUSL" 2227``` 2228 2229## LCD 2230 2231By default, the DAYU 200 platform supports an LCD screen with the MIPI API. 2232 2233LCD adaptation mainly depends on the HDF display model. The display driver model is developed based on the HDF driver framework, platform API, and OSAL API. It can shield the differences between different kernel forms (LiteOS and Linux), apply to different chip platforms, and provide a unified driver platform for display components. 2234 2235The following figure shows the hierarchy of the HDF display driver model. 2236 2237 2238 2239The current driver model is mainly deployed in kernel mode and connects to the display common HAL layer to assist HDI implementation. The display driver exposes the display driver capability to the graphics service through the display-HDI layer. The display driver connects to the display panel component downwards to drive the screen to work properly and streamlines the entire display process. 2240 2241Therefore, the LCD adaptation mainly lies in the adaptation of the LCD panel component driver. 2242 2243The adaptation of the component driver consists of two parts: panel driver and HCS configuration. 2244 2245The following files are involved: 2246 2247``` 2248drivers/framework/model/display/driver/panel 2249 2250vendor/hihope/rk3568/hdf_config/khdf/device_info 2251 2252vendor/hihope/rk3568/hdf_config/khdf/input 2253``` 2254 2255### Panel Driver 2256 2257The component driver is used based on the following APIs: 2258 2259``` 2260struct PanelData { 2261 struct HdfDeviceObject *object; 2262 int32_t (*init)(struct PanelData *panel); 2263 int32_t (*on)(struct PanelData *panel); 2264 int32_t (*off)(struct PanelData *panel); 2265 int32_t (*prepare)(struct PanelData *panel); 2266 int32_t (*unprepare)(struct PanelData *panel); 2267 struct PanelInfo *info; 2268 enum PowerStatus powerStatus; 2269 struct PanelEsd *esd; 2270 struct BacklightDev *blDev; 2271 void *priv; 2272}; 2273``` 2274 2275Instantiate the data structure in the initialization API of the driver. 2276 2277 panelSimpleDev->panel.init = PanelSimpleInit; 2278 panelSimpleDev->panel.on = PanelSimpleOn; 2279 panelSimpleDev->panel.off = PanelSimpleOff; 2280 panelSimpleDev->panel.prepare = PanelSimplePrepare; 2281 panelSimpleDev->panel.unprepare = PanelSimpleUnprepare; 2282PanelSimpleInit initializes panel software. 2283 2284PanelSimpleOn turns on the screen. 2285 2286PanelSimpleOff turns off the screen. 2287 2288PanelSimplePrepare initializes the hardware time sequence for turning on the screen. 2289 2290PanelSimpleUnprepare initializes the hardware time sequence for turning off the screen. 2291 2292After instantiation, use the **RegisterPanel** API to register the panel driver with the display model. 2293 2294Note that the LCD on DAYU 200 uses the DRM display framework. 2295 2296### HCS Configuration 2297 2298``` 2299device4 :: deviceNode { 2300 policy = 0; 2301 priority = 100; 2302 preload = 0; 2303 moduleName = "LCD_PANEL_SIMPLE"; 2304 } 2305``` 2306 2307## Backlight 2308 2309The backlight driver model is developed based on the HDF framework. 2310 2311 2312 2313The RK3568 backlight is implemented by controlling the duty cycle by using PWM4. 2314 2315Code path of the native backlight driver: 2316 2317```c 2318linux-5.10/drivers/video/backlight/pwm_bl.c 2319linux-5.10/drivers/video/backlight/backlight.c 2320linux-5.10/drivers/pwm/pwm-rockchip.c 2321``` 2322 2323To use the backlight driver on the HDF framework, disable the native driver. 2324 2325```c 2326# CONFIG_BACKLIGHT_PWM is not set 2327``` 2328 2329### HDF Implementation 2330 2331Code Path 2332 2333```c 2334drivers/framework/model/display/driver/backlight/hdf_bl.c 2335``` 2336 2337HDF BL Entry Function 2338 2339```c 2340static int32_t BacklightInit(struct HdfDeviceObject *object) 2341{ 2342 if (object == NULL) { 2343 HDF_LOGE("%s: object is null!", __func__); 2344 return HDF_FAILURE; 2345 } 2346 HDF_LOGI("%s success", __func__); 2347 return HDF_SUCCESS; 2348} 2349 2350struct HdfDriverEntry g_blDevEntry = { 2351 .moduleVersion = 1, 2352 .moduleName = "HDF_BL", 2353 .Init = BacklightInit, 2354 .Bind = BacklightBind, 2355}; 2356 2357HDF_INIT(g_blDevEntry); 2358``` 2359 2360Code path: 2361 2362```c 2363drivers/framework/model/display/driver/backlight/pwm_bl.c 2364``` 2365 2366HDF PWM Entry Function 2367 2368```c 2369struct HdfDriverEntry g_pwmBlDevEntry = { 2370 .moduleVersion = 1, 2371 .moduleName = "PWM_BL", 2372 .Init = BlPwmEntryInit, 2373}; 2374 2375HDF_INIT(g_pwmBlDevEntry); 2376``` 2377 2378The backlight control APIs are as follows: 2379 2380```c 2381static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness) 2382{ 2383 int32_t ret; 2384 uint32_t duty; 2385 struct BlPwmDev *blPwmDev = NULL; 2386 2387 blPwmDev = ToBlDevPriv(blDev); 2388 if (blPwmDev == NULL) { 2389 HDF_LOGE("%s blPwmDev is null", __func__); 2390 return HDF_FAILURE; 2391 } 2392 if (blPwmDev->props.maxBrightness == 0) { 2393 HDF_LOGE("%s maxBrightness is 0", __func__); 2394 return HDF_FAILURE; 2395 } 2396 if (brightness == 0) { 2397 return PwmDisable(blPwmDev->pwmHandle); 2398 } 2399 duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness; 2400 ret = PwmSetDuty(blPwmDev->pwmHandle, duty); 2401 if (ret != HDF_SUCCESS) { 2402 HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret); 2403 return HDF_FAILURE; 2404 } 2405 return PwmEnable(blPwmDev->pwmHandle); 2406} 2407 2408static struct BacklightOps g_blDevOps = { 2409 .updateBrightness = BlPwmUpdateBrightness, 2410}; 2411``` 2412 2413Actually, the API implemented by the HDF PWM is used to connect to the kernel PWM. 2414 2415 2416 2417Register the backlight with the LCD HDF driver. 2418 2419Code Path 2420 2421```c 2422drivers/framework/model/display/driver/panel/ili9881c_boe.c 2423``` 2424 2425```c 2426ili9881cBoeDev->panel.blDev = GetBacklightDev("hdf_pwm"); 2427if (ili9881cBoeDev->panel.blDev == NULL) { 2428 HDF_LOGE("%s GetBacklightDev fail", __func__); 2429 goto FAIL; 2430} 2431``` 2432 2433### HCS Configuration 2434 2435HCS configuration of the driver 2436 2437```c 2438device_pwm_bl :: device { 2439 device0 :: deviceNode { 2440 policy = 0; 2441 priority = 95; 2442 preload = 0; 2443 moduleName = "PWM_BL"; 2444 deviceMatchAttr = "pwm_bl_dev"; 2445 } 2446} 2447device_backlight :: device { 2448 device0 :: deviceNode { 2449 policy = 2; 2450 priority = 90; 2451 preload = 0; 2452 permission = 0660; 2453 moduleName = "HDF_BL"; 2454 serviceName = "hdf_bl"; 2455 } 2456} 2457``` 2458 2459HCS configuration for PWM backlight 2460 2461```c 2462root { 2463 backlightConfig { 2464 pwmBacklightConfig { 2465 match_attr = "pwm_bl_dev"; 2466 pwmDevNum = 1; 2467 pwmMaxPeriod = 25000; 2468 backlightDevName = "hdf_pwm"; 2469 minBrightness = 0; 2470 defBrightness = 127; 2471 maxBrightness = 255; 2472 } 2473 } 2474} 2475``` 2476 2477### Testing 2478 2479Run **cat /sys/kernel/debug/pwm** to check whether the HDF PWM has applied for PWM4. 2480 2481If the application is successful, the following information is displayed: 2482 2483**requested**: The application is successful. 2484 2485**enabled**: PWM4 is enabled successfully. 2486 2487```c 2488# cat /sys/kernel/debug/pwm 2489 2490platform/fe6e0000.pwm, 1 PWM device 2491 pwm-0 ((null) ): requested enabled period: 25000 ns duty: 9705 ns polarity: normal 2492``` 2493 2494## **Wi-Fi** 2495 2496### HDF-based Wi-Fi Approach 2497 2498Get familiar with the HDF Wi-Fi framework and main APIs to be implemented by referring to [OpenHarmony HDF Wi-Fi Driver Analysis](https://mp.weixin.qq.com/s/iiE97pqPtzWIZadcjrQtsw), including the implementation of the HDF driver initialization API, Wi-Fi control-side API set, AP mode API set, STA mode API set, network-side API set, and event reporting API. 2499 2500Next, let's get familiar with the HCS file format and the code startup initialization process of the HDF Wi-Fi core driver framework. For details, see the Hi3881 code. 2501 2502HDF Wi-Fi framework 2503 2504  2505 2506### Code Process Analysis of the AP6275s Driver 2507 2508#### Analysis of the Driver Module Initialization Process 2509 2510 2511 2512The AP6275s is a Wi-Fi module driver of the SDIO device. It uses the standard Linux SDIO device driver. The kernel module initialization entry **module_init()** calls the **dhd_wifi_platform_load_sdio()** function for initialization. Here, **wifi_platform_set_power()** is called to power on the GPIO, **dhd_wlan_set_carddetect()** is called to detect the SDIO card, and **sdio_register_driver(&bcmsdh_sdmmc_driver)** is called to register the SDIO device driver. The SDIO bus has detected that the Wi-Fi module matches the device driver based on the device ID and vendor ID. Therefore, the **bcmsdh_sdmmc_probe()** function of the driver is called back immediately to initialize the Wi-Fi module chip. Finally, create the net_device network API wlan0 and register it with the Linux kernel protocol stack. 2513 2514l Create the wlan0 object of the net_device network API. 2515 2516**dhd_allocate_if()** calls **alloc_etherdev()** to create a **net_device** object, that is, the wlan0 network API. 2517 2518l Register wlan0 with the kernel protocol stack. 2519 2520Call the **dhd_register_if()** function. Here, call **register_netdev(net)** to register the wlan0 network API with the protocol stack. 2521 2522### HDF Wi-Fi Framework Adaptation Through Code Modification 2523 2524To use the Wi-Fi function of the system, the AP mode, STA mode, and P2P mode need to be implemented. The **wpa_supplicant** application interacts with the Wi-Fi driver through the HDF Wi-Fi framework to implement the STA mode and P2P mode, and the **hostapd** application interacts with the Wi-Fi driver through the HDF Wi-Fi framework to implement the AP mode and P2P mode. 2525 2526The AP6275s Wi-Fi 6 kernel driver depends on the platform capability, including the SDIO bus communication capability. The communication with the user mode depends on the HDF Wi-Fi framework capability. After ensuring that the preceding capabilities are normal, you can start the HDF adaptation and porting of the Wi-Fi driver. This part uses the open-source RK3568 code as the basic version for porting. 2527 2528The files and directories involved in the adaptation and porting of the AP6275s Wi-Fi 6 driver are as follows: 2529 25301). Compile configuration files. 2531 2532drivers/adapter/khdf/linux/model/network/wifi/Kconfig 2533 2534drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile 2535 25362). Access the Wi-Fi driver source code directory. 2537 2538The native driver code is stored in: 2539 2540linux-5.10/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd_wifi6/ 2541 2542The code files added and modified on the native driver are stored in the following directory: 2543 2544device/hihope/rk3568/wifi/bcmdhd_wifi6/ 2545 2546Directory structure: 2547 2548``` 2549./device/hihope/rk3568/wifi/bcmdhd_wifi6/hdf 2550├── hdf_bdh_mac80211.c 2551├── hdf_driver_bdh_register.c 2552├── hdfinit_bdh.c 2553├── hdf_mac80211_ap.c 2554├── hdf_mac80211_sta.c 2555├── hdf_mac80211_sta.h 2556├── hdf_mac80211_sta_event.c 2557├── hdf_mac80211_sta_event.h 2558├── hdf_mac80211_p2p.c 2559├── hdf_public_ap6275s.h 2560├── net_bdh_adpater.c 2561├── net_bdh_adpater.h 2562``` 2563 2564**hdf_bdh_mac80211.c** is used to fill in the functions required by **g_bdh6_baseOps**, **hdf_mac80211_ap.c** is used to fill in the functions required by **g_bdh6_staOps**, **hdf_mac80211_sta.c** is used to fill in the functions required by **g_bdh6_staOps**, and **hdf_mac80211_p2p.c** is used to fill in the functions required by **g_bdh6_p2pOps**. For details about the APIs required for basic Wi-Fi functions, see **openharmony/drivers/framework/include/wifi/wifi_mac80211_ops.h**. 2565 2566#### Driver File Compilation 2567 2568The HDF WLAN driver framework consists of seven parts: Module, NetDevice, NetBuf, BUS, HAL, Client, and Message. You can implement the following functions during HDF adaptation of the Wi-Fi driver: 2569 25701. Initialize the driver module that adapts the HDF WLAN framework. 2571 2572 The following figure shows the code process. 2573 2574  2575 2576 The code is stored in **device/hihope/rk3568/wifi/bcmdhd_wifi6/hdf_driver_bdh_register.c**. 2577 2578 ``` 2579 struct HdfDriverEntry g_hdfBdh6ChipEntry = { 2580 .moduleVersion = 1, 2581 .Bind = HdfWlanBDH6DriverBind, 2582 .Init = HdfWlanBDH6ChipDriverInit, 2583 .Release = HdfWlanBDH6ChipRelease, 2584 .moduleName = "HDF_WLAN_CHIPS" 2585 }; 2586 HDF_INIT(g_hdfBdh6ChipEntry); 2587 ``` 2588 2589 During driver initialization, the SDIO main control board scans and detects the card, initializes the Wi-Fi chip, and creates and initializes the main API. 2590 25912. Implement the HDF WLAN Base control-side APIs. 2592 2593 The code is stored in **hdf_bdh_mac80211.c**. 2594 2595 ``` 2596 static struct HdfMac80211BaseOps g_bdh6_baseOps = { 2597 .SetMode = BDH6WalSetMode, 2598 .AddKey = BDH6WalAddKey, 2599 .DelKey = BDH6WalDelKey, 2600 .SetDefaultKey = BDH6WalSetDefaultKey, 2601 .GetDeviceMacAddr = BDH6WalGetDeviceMacAddr, 2602 .SetMacAddr = BDH6WalSetMacAddr, 2603 .SetTxPower = BDH6WalSetTxPower, 2604 .GetValidFreqsWithBand = BDH6WalGetValidFreqsWithBand, 2605 .GetHwCapability = BDH6WalGetHwCapability, 2606 .SendAction = BDH6WalSendAction, 2607 .GetIftype = BDH6WalGetIftype, 2608 }; 2609 ``` 2610 2611 The preceding APIs are called in STA, AP, and P2P modes. 2612 26133. Implement APIs in HDF WLAN STA mode. 2614 2615 The following figure shows the call process in STA mode. 2616  2617 2618 The code is stored in **hdf_mac80211_sta.c**. 2619 2620 ``` 2621 struct HdfMac80211STAOps g_bdh6_staOps = { 2622 .Connect = HdfConnect, 2623 .Disconnect = HdfDisconnect, 2624 .StartScan = HdfStartScan, 2625 .AbortScan = HdfAbortScan, 2626 .SetScanningMacAddress = HdfSetScanningMacAddress, 2627 }; 2628 ``` 2629 26304. Implement APIs in HDF WLAN AP mode. 2631 2632 The following figure shows the call process in AP mode. 2633 2634  2635 2636The code is stored in **hdf_mac80211_ap.c**. 2637 2638``` 2639struct HdfMac80211APOps g_bdh6_apOps = { 2640 .ConfigAp = WalConfigAp, 2641 .StartAp = WalStartAp, 2642 .StopAp = WalStopAp, 2643 .ConfigBeacon = WalChangeBeacon, 2644 .DelStation = WalDelStation, 2645 .SetCountryCode = WalSetCountryCode, 2646 .GetAssociatedStasCount = WalGetAssociatedStasCount, 2647 .GetAssociatedStasInfo = WalGetAssociatedStasInfo 2648}; 2649``` 2650 26515. Implement APIs in HDF WLAN P2P mode. 2652 2653 The following figure shows the call process in P2P mode. 2654 2655  2656 2657 ``` 2658 struct HdfMac80211P2POps g_bdh6_p2pOps = { 2659 .RemainOnChannel = WalRemainOnChannel, 2660 .CancelRemainOnChannel = WalCancelRemainOnChannel, 2661 .ProbeReqReport = WalProbeReqReport, 2662 .AddIf = WalAddIf, 2663 .RemoveIf = WalRemoveIf, 2664 .SetApWpsP2pIe = WalSetApWpsP2pIe, 2665 .GetDriverFlag = WalGetDriverFlag, 2666 }; 2667 ``` 2668 26696. Implement the event reporting APIs of the HDF WLAN framework. 2670 2671 The Wi-Fi driver needs to report events to the **wpa_supplicant** and **hostapd** applications, such as hotspot scanning results and association completion events of new STAs. For details about all APIs for reporting HDF WLAN events, see **drivers/framework/include/wifi/hdf_wifi_event.h**. 2672 2673 The HDF WLAN APIs for reporting events are as follows. 2674 2675| API in Header File hdf_wifi_event.h | Description | 2676| ----------------------------------- | ------------------------ | 2677| HdfWifiEventNewSta() | Reports a new STA event. | 2678| HdfWifiEventDelSta () | Reports an STA deletion event. | 2679| HdfWifiEventInformBssFrame () | Reports a BSS scanning event. | 2680| HdfWifiEventScanDone () | Reports a scanning completion event. | 2681| HdfWifiEventConnectResult () | Reports a connection result event. | 2682| HdfWifiEventDisconnected () | Reports a disconnection event. | 2683| HdfWifiEventMgmtTxStatus () | Reports sending status events. | 2684| HdfWifiEventRxMgmt () | Reports a receiving status event. | 2685| HdfWifiEventCsaChannelSwitch () | Reports a CSA frequency band switching event. | 2686| HdfWifiEventTimeoutDisconnected ()| Reports a connection timeout event. | 2687| HdfWifiEventEapolRecv () | Reports an EAPOL receive event. | 2688| HdfWifiEventResetResult () | Reports a WLAN driver reset result event.| 2689| HdfWifiEventRemainOnChannel () | Reports a channel holding event. | 2690| HdfWifiEventCancelRemainOnChannel | Reports a channel unholding event. | 2691 2692### Summary of All Key Issues 2693 2694#### Method of Enabling the AP Mode During AP Module Debugging 2695 2696Use the BusyBox and hostapd to configure the AP function as follows: 2697 2698``` 2699ifconfig wlan0 up 2700ifconfig wlan0 192.168.12.1 netmask 255.255.255.0 2701busybox udhcpd /data/udhcpd.conf 2702./hostapd -d /data/hostapd.conf 2703``` 2704 2705#### Method of Enabling the ATA Mode During STA Module Debugging 2706 2707``` 2708wpa_supplicant -iwlan0 -c /data/l2tool/wpa_supplicant.conf -d & 2709./busybox udhcpc -i wlan0 -s /data/l2tool/dhcpc.sh 2710``` 2711 2712#### Solution to the Failure to Report the Hotspot Scanning Event to wap_supplicant 2713 2714When the **wpa_supplicant** application is started, the **-B** parameter cannot be added to start the application in the background. If the **-B** parameter is added to start the application in the background, the thread that calls **poll()** to wait for receiving events exits. As a result, the reported events cannot be received. 2715 2716In this case, use **wpa_supplicant -iwlan0 -c /data/wpa_supplicant.conf &** to start the application in the background. 2717 2718#### WPA2PSK Authentication Failure and Timeout 2719 2720According to the process analysis, the hostapd process does not receive the **WIFI_WPA_EVENT_EAPOL_RECV = 13** event. Originally, the driver does not send the received EAPOL packet to the hostapd process through the HDF Wi-Fi framework. After the driver receives the packet, the EAPOL packet is sent to the HDF Wi-Fi framework before **netif_rx()** is called to trigger a software interrupt. The authentication is successful. 2721 2722#### Location and Analysis of Connection Failure in P2P Mode 2723 2724During the debugging of the P2P connection API, it is found that the P2P direct connection page of the mobile phone shows the invited state and the connection fails. By capturing and comparing the packets indicating that the connection between the mobile phone and the Wi-Fi module is successful and the packets indicating that the connection fails after HDF adaptation, you can find that the mobile phone responds with an extra ACTION packet, indicating that the parameter is invalid. Then, the P2P connection is terminated. 2725 2726  2727 2728Check the content of the ACTION packet sent by the Wi-Fi module to the mobile phone. It is found that the MAC address in P2P Device Info is incorrect, as shown below. 2729 2730Correct frame content 2731 2732  2733 2734Incorrect frame content 2735 2736  2737 2738Analyze the padding code of the MAC address. The MAC address is filled by **wpa_supplicant** based on the MAC address of p2p0. Therefore, the MAC address of the wdev object (p2p-dev-wlan0) is updated to the p2p0 API, and the two values must be the same. For details, see the call process of **wl_get_vif_macaddr(cfg, 7, p2p_hnetdev->macAddr)**. 2739 2740### Connection Success Log 2741 2742#### Connection Success Log in STA Mode 2743 2744``` 2745WPA: Key negotiation completed with 50:eb:f6:02:8e6:d4 [PTK=CCMP GTK=CCMP] 2746 06 wlan0: State: GROUP_HANDSHAKEc -> COMPLETED 2747wlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=] 2748WifiWpaReceived eEapol done 2749``` 2750 2751#### Connection Success Log in AP Mode 2752 2753``` 2754wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port 2755wlan0: STA 96:27:b3:95:b7:6e WPA: pairwise key handshake completed (RSN) 2756WifiWpaReceiveEapol done 2757``` 2758 2759#### Connection Success Log in P2P Mode 2760 2761``` 2762P2P: cli_channels: 2763EAPOL: External notificationtion - portValid=1 2764EAPOL: External notification:tion - EAP success=1 2765EAPOL: SUPP_PAE entering state AUTHENTIwCATING 2766EAPOL: SUPP_BE enterilng state SUCCESS 2767EAP: EAP ent_ering state DISABLED 2768EAPOL: SUPP_PAE entering state AUTHENTICATED 2769EAPOL:n Supplicant port status: Authoorized 2770EAPOL: SUPP_BE entertaining IDLE 2771WifiWpaReceiveEapol donepleted - result=SUCCESS 2772 2773\# ifconfig 2774 2775lo Link encap:Local Loopback 2776 inet addr:127.0.0.1 Mask:255.0.0.0 2777 inet6 addr: ::1/128 Scope: Host 2778 UP LOOPBACK RUNNING MTU:65536 Metric:1 2779 RX packets:12 errors:0 dropped:0 overruns:0 frame:0 2780 TX packets:12 errors:0 dropped:0 overruns:0 carrier:0 2781 collisions:0 txqueuelen:1000 2782 RX bytes:565 TX bytes:565 2783 2784wlan0 Link encap:Ethernet HWaddr 10:2c:6b:11:61:e0 Driver bcmsdh_sdmmc 2785 inet6 addr: fe80::122c:6bff:fe11:61e0/64 Scope: Link 2786 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 2787 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 2788 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 2789 collisions:0 txqueuelen:1000 2790 RX bytes:0 TX bytes:0 2791 2792p2p0 Link encap:Ethernet HWaddr 12:2c:6b:11:61:e0 2793 inet6 addr: fe80::102c:6bff:fe11:61e0/64 Scope: Link 2794 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 2795 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 2796 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 2797 collisions:0 txqueuelen:1000 2798 RX bytes:0 TX bytes:0 2799 2800p2p-p2p0-0 Link encap:Ethernet HWaddr 12:2c:6b:11:21:e0 Driver bcmsdh_sdmmc 2801 inet6 addr: fe80::102c:6bff:fe11:21e0/64 Scope: Link 2802 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 2803 RX packets:0 errors:0 dropped:9 overruns:0 frame:0 2804 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 2805 collisions:0 txqueuelen:1000 2806 RX bytes:0 TX bytes:0 2807``` 2808 2809## Bluetooth 2810 2811### HCI API 2812 2813The overall Bluetooth hardware architecture consists of two parts: host (computer or MCU) and host controller (actual Bluetooth chipset). The communication between the host and controller complies with the HCI, as shown in the following figure. 2814 2815 2816 2817HCI defines how to exchange commands, events, and asynchronous and synchronous packets. Asynchronous connectionless links (ACLs) are used for data transmission, while synchronous connection oriented links (SCOs) are used for voice with headsets and hands-free profiles. 2818 2819### Hardware Connection 2820 2821According to the description of the RK3568 chip, the RK3568 chip does not integrate the Wi-Fi/Bluetooth function. The RK3568 chip needs to connect to an external Bluetooth chip to support the Bluetooth function, which complies with the preceding logical architecture. Regarding the physical connection between the host and the controller, you can consult the development board specifications. 2822 2823 2824 2825Pins 28-36 are UART (serial port). In addition, several pins are used for power supply and hibernation control. 2826 2827### Bluetooth VENDORLIB Adaptation 2828 2829#### What Is vendorlib? 2830 2831vendorlib is deployed on the host, and may be considered as a Bluetooth chip driver layer on the host, to shield technical details of different Bluetooth chips. From the perspective of code, the main functions are as follows: 2832 28331. Provide a channel (file descriptor of the serial port) between Bluetooth chips for the protocol stack. 2834 28352. Provide specific control methods for specific chips. 2836 2837#### Interpretation of vendorlib at the Code Level 2838 2839**bt_vendor_lib.h**: 2840 2841``` 2842foundation/communication/bluetooth/services/bluetooth_standard/hardware/include 2843``` 2844 2845This file defines the interaction APIs between the protocol stack and **vendor_lib**, which are divided into two groups: 2846 28471. Implemented by vendorlib and called by the protocol stack 2848 2849```c 2850typedef struct { 2851 /** 2852 * Set to sizeof(bt_vendor_interface_t) 2853 */ 2854 size_t size; 2855 /** 2856 * Caller will open the interface and pass in the callback routines 2857 * to the implementation of this interface. 2858 */ 2859 int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr); 2860 2861 /** 2862 * Vendor specific operations 2863 */ 2864 int (*op)(bt_opcode_t opcode, void* param); 2865 2866 /** 2867 * Closes the interface 2868 */ 2869 void (*close)(void); 2870} bt_vendor_interface_t; 2871``` 2872 2873The basic process of starting the protocol stack is as follows: 2874 28751.1. The protocol stack dynamically opens **libbt_vendor.z.so** and calls the **init** function to initialize **vendorlib**. 2876 28771.2. The protocol stack calls the OP function to call the OP codes **BT_OP_POWER_ON**, **BT_OP_HCI_CHANNEL_OPEN**, and **BT_OP_INIT**. In principle, if **BT_OP_INIT** is successful, the chip initialization is complete. 2878 28792. Implemented by the protocol stack and called by **vendorlib** (callback function) 2880 2881```c 2882typedef struct { 2883 /** 2884 * set to sizeof(bt_vendor_callbacks_t) 2885 */ 2886 size_t size; 2887 2888 /* notifies caller result of init request */ 2889 init_callback init_cb; 2890 2891 /* buffer allocation request */ 2892 malloc_callback alloc; 2893 2894 /* buffer free request */ 2895 free_callback dealloc; 2896 2897 /* hci command packet transmit request */ 2898 cmd_xmit_callback xmit_cb; 2899} bt_vendor_callbacks_t; 2900``` 2901 2902**init_cb** is called after **BT_OP_INIT** is complete. 2903 2904**alloc/dealloc** is used to apply for or release message controls when HCI messages are sent. 2905 2906**xmit_cb** sends HCI commands. 2907 2908Important functions implemented by **vendor_lib**: 2909 29101. **init** function 2911 2912```c 2913static int init(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr) 2914{ 2915 /* * ... */ 2916 userial_vendor_init(); 2917 upio_init(); 2918 2919 vnd_load_conf(VENDOR_LIB_CONF_FILE); 2920 2921 /* store reference to user callbacks */ 2922 bt_vendor_cbacks = (bt_vendor_callbacks_t *)p_cb; 2923 /* This is handed over from the stack */ 2924 return memcpy_s(vnd_local_bd_addr, BD_ADDR_LEN, local_bdaddr, BD_ADDR_LEN); 2925} 2926``` 2927 2928The **init** function is the first function called by **vendorlib**. **vendorlib** only needs to save the callback and MAC addresses of the protocol stack. 2929 29302. **BT_OP_POWER_ON** 2931 2932This operation needs to pull up the level of the power pin. In this function, the rfill device is used for processing, and the driver is not directly called to pull up the level. 2933 2934```c 2935int upio_set_bluetooth_power(int on) 2936{ 2937 int sz; 2938 int fd = -1; 2939 int ret = -1; 2940 char buffer = '0'; 2941 2942 switch (on) { 2943 case UPIO_BT_POWER_OFF: 2944 buffer = '0'; 2945 break; 2946 2947 case UPIO_BT_POWER_ON: 2948 buffer = '1'; 2949 break; 2950 default: 2951 return 0; 2952 } 2953 2954 /* check if we have rfkill interface */ 2955 if (is_rfkill_disabled()) { 2956 return 0; 2957 } 2958 2959 if (rfkill_id == -1) { 2960 if (init_rfkill()) { 2961 return ret; 2962 } 2963 } 2964 2965 fd = open(rfkill_state_path, O_WRONLY); 2966 if (fd < 0) { 2967 return ret; 2968 } 2969 2970 sz = write(fd, &buffer, 1); 2971 /* ... */ 2972 return ret; 2973} 2974``` 2975 29763. **BT_OP_HCI_CHANNEL_OPEN** processing 2977 2978```c 2979case BT_OP_HCI_CHANNEL_OPEN: { // BT_VND_OP_USERIAL_OPEN 2980 int(*fd_array)[] = (int(*)[])param; 2981 int fd, idx; 2982 fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg); 2983 if (fd != -1) { 2984 for (idx = 0; idx < HCI_MAX_CHANNEL; idx++) 2985 (*fd_array)[idx] = fd; 2986 retval = 1; 2987 } 2988 /* retval contains numbers of open fd of HCI channels */ 2989 break; 2990``` 2991 2992The **userial_vendor_open** function opens the serial port device (UART) to obtain the file descriptor (FD). The FD is returned through the **param** parameter of OP. 2993 2994The name of the serial port device in the system must have been predefined in the development board. In this version, the device on the development board is **/dev/ttyS8**. 2995 29964. **BT_OP_INIT** processing 2997 2998The operation code requires initialization of the Bluetooth chip, and specific processing to be performed is closely related to the Bluetooth chip. The AP6257S chip used in this debugging is used as an example. During initialization, the Bluetooth firmware is delivered. 2999 3000After the initialization is complete, the **init_cb** callback function (see **bt_vendor_callbacks_t**) must be called to notify the protocol stack of the initialization result. Otherwise, the protocol stack thread is blocked and Bluetooth functions cannot be used properly. The protocol stack performs the following operations: 3001 3002After calling **BT_OP_INIT**, the protocol stack waits for the semaphore. The semaphore is set by the **init_cb** function. 3003 3004```c 3005static int HciInitHal() 3006{ 3007 int result = BT_NO_ERROR; 3008 3009 g_waitHdiInit = SemaphoreCreate(0); 3010 int ret = g_hdiLib->hdiInit(&g_hdiCallbacks); 3011 if (ret == SUCCESS) { 3012 SemaphoreWait(g_waitHdiInit); 3013 } 3014} 3015``` 3016 3017### vendorlib Porting Problem 3018 30191. Name of the .so file of vendorlib 3020 3021 The .so file name of vendorlib must be **libbt_vendor.z.so** because it is used when the protocol stack opens the dynamic link library (DLL). 3022 30232. Firmware problem 3024 3025 Pay attention to the chip firmware during development. The firmware of some Bluetooth chips may not need to be upgraded, but it's a must for some chips. During the adaptation of the AP6257S, the firmware is not delivered at the beginning. As a result, the received Bluetooth signals are poor. Pay attention to the following points when delivering firmware: 3026 3027 2.1. For the AP6257S chip, the Bluetooth chip does not have a flash memory. Therefore, the firmware must be delivered again after the chip is powered on and off. 3028 3029 2.2. The firmware is processed based on the chip requirements. It is recommended that the reference code of the vendor be found. Take the Broadcom series chips as an example. The firmware delivery process is complex and is driven by a state machine. There are nine states in total: 3030 3031```c 3032/ Hardware Configuration State */ 3033enum { 3034 HW_CFG_START = 1, 3035 HW_CFG_SET_UART_CLOCK, 3036 HW_CFG_SET_UART_BAUD_1, 3037 HW_CFG_READ_LOCAL_NAME, 3038 HW_CFG_DL_MINIDRIVER, 3039 HW_CFG_DL_FW_PATCH, 3040 HW_CFG_SET_UART_BAUD_2, 3041 HW_CFG_SET_BD_ADDR, 3042 HW_CFG_READ_BD_ADDR 3043}; 3044``` 3045 3046Initialize the state machine after receiving the **BT_OP_INIT** message, send the **HCI_REST** command, and switch the state to **HW_CFG_START**. 3047 3048```c 3049void hw_config_start(void) 3050{ 3051 HC_BT_HDR *p_buf = NULL; 3052 uint8_t *p; 3053 hw_cfg_cb.state = 0; 3054 hw_cfg_cb.fw_fd = -1; 3055 hw_cfg_cb.f_set_baud_2 = FALSE; 3056 3057 if (bt_vendor_cbacks) { 3058 p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + 3059 HCI_CMD_PREAMBLE_SIZE); 3060 } 3061 3062 if (p_buf) { 3063 p_buf->event = MSG_STACK_TO_HC_HCI_CMD; 3064 p_buf->offset = 0; 3065 p_buf->layer_specific = 0; 3066 p_buf->len = HCI_CMD_PREAMBLE_SIZE; 3067 3068 p = (uint8_t *)(p_buf + 1); 3069 UINT16_TO_STREAM(p, HCI_RESET); 3070 *p = 0; 3071 3072 hw_cfg_cb.state = HW_CFG_START; 3073 bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf); 3074 } else { 3075 if (bt_vendor_cbacks) { 3076 HILOGE("vendor lib fw conf aborted [no buffer]"); 3077 bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL); 3078 } 3079 } 3080} 3081``` 3082 3083After receiving the **HCI_RESET** completion event returned by the chip, the system switches to the next state machine and sends the next COMMAND until the state machine completes firmware delivery. 3084 3085For details, see the **hw_config_cback** function. 3086 30873. Pay attention to inter-system API differences. 3088 3089 The APIs may vary slightly by system, which requires special attention. For example, the definitions of the functions for **vendorlib** to call **xmit_cb** to send HCI commands are slightly different on different systems. 3090 3091Certain system: 3092 3093```c 3094/* define callback of the cmd_xmit_cb 3095 * 3096 3097The callback function which HCI lib will call with the return of command 3098 3099complete packet. Vendor lib is responsible for releasing the buffer passed 3100 3101in at the p_mem parameter by calling dealloc callout function. 3102*/ 3103typedef void (*tINT_CMD_CBACK)(void* p_mem); 3104typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void* p_buf, tINT_CMD_CBACK p_cback); 3105``` 3106 3107OpenHarmony: 3108 3109```c 3110/** 3111 3112hci command packet transmit callback 3113 3114Vendor lib calls cmd_xmit_cb function in order to send a HCI Command 3115 3116packet to BT Controller. 3117* 3118 3119The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of 3120 3121HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command 3122 3123packet. */ 3124 3125typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf); 3126``` 3127 3128As shown above, after a command is sent in vendorlib, the certain system notifies the chip of the returned message through a callback, while OpenHarmony notifies the chip of the returned message through the **BT_OP_EVENT_CALLBACK** operation code (see the definition of **bt_opcode_t**). vendorlib needs to parse the message code in the packet to determine which message is to be processed by the chip, and then calls the corresponding processing function. 3129 3130```c 3131void hw_process_event(HC_BT_HDR *p_buf) 3132{ 3133 uint16_t opcode; 3134 uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; 3135 STREAM_TO_UINT16(opcode, p); 3136 switch (opcode) { 3137 case HCI_VSC_WRITE_BD_ADDR: 3138 #if (USE_CONTROLLER_BDADDR == TRUE) 3139 case HCI_READ_LOCAL_BDADDR: 3140 #endif 3141 case HCI_READ_LOCAL_NAME: 3142 case HCI_VSC_DOWNLOAD_MINIDRV: 3143 case HCI_VSC_WRITE_FIRMWARE: 3144 case HCI_VSC_LAUNCH_RAM: 3145 case HCI_RESET: 3146 case HCI_VSC_WRITE_UART_CLOCK_SETTING: 3147 case HCI_VSC_UPDATE_BAUDRATE: 3148 hw_config_cback(p_buf); 3149 break; 3150``` 3151 3152The returned messages are also different. OpenHarmony returns the number of bytes in the message. If the value is less than or equal to **0**, the message fails to be sent. 3153 31544. snoop log 3155 3156 Like other systems, OpenHarmony logs HCI records, which are saved in the **/data/log/bluetooth/snoop.log** file. This allows you to use Wireshark or other packet analysis tools to examine the interaction process between the host and controller, facilitating fault analysis. 3157 3158## Sensor 3159 3160 Sensor driver model developed based on the HDF driver framework 3161 3162 3163 3164RK3568 supports the accel sensor. The OpenHarmony mainline version has the overall driver framework. You only need to implement the specific component driver. 3165 3166### Implementation of the mcx5566xa HDF Driver 3167 3168The RK3568 platform supports the acceleration sensor MXC6655XA. For details about the configuration, see the datasheet of the acceleration sensor. Before porting the HDF, ensure that the compilation of the sensor in the kernel is disabled. 3169 3170The configuration file is stored in **kernel/linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig**. 3171 3172```c 3173# CONFIG_GS_MXC6655XA is not set 3174``` 3175 3176Code path: 3177 3178```c 3179drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.c 3180drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.h 3181``` 3182 3183Compile the macros. 3184 3185```c 3186CONFIG_DRIVERS_HDF_SENSOR_ACCEL_MXC6655XA=y 3187``` 3188 3189Implementation of the MXC6655XA accelerometer driver entry function 3190 3191```c 3192struct HdfDriverEntry g_accelMxc6655xaDevEntry = { 3193 .moduleVersion = 1, 3194 .moduleName = "HDF_SENSOR_ACCEL_MXC6655XA", 3195 .Bind = Mxc6655xaBindDriver, 3196 .Init = Mxc6655xaInitDriver, 3197 .Release = Mxc6655xaReleaseDriver, 3198}; 3199 3200HDF_INIT(g_accelMxc6655xaDevEntry); 3201``` 3202 3203Next, let's look at the differentiated adaptation function. 3204 3205```c 3206struct AccelOpsCall { 3207int32_t (*Init)(struct SensorCfgData *data); 3208int32_t (*ReadData)(struct SensorCfgData *data); 3209}; 3210``` 3211 3212API for obtaining data on the X, Y, and Z axises: 3213 3214```c 3215int32_t ReadMxc6655xaData(struct SensorCfgData *cfg, struct SensorReportEvent *event) 3216{ 3217 int32_t ret; 3218 struct AccelData rawData = { 0, 0, 0 }; 3219 static int32_t tmp[ACCEL_AXIS_NUM]; 3220 3221 CHECK_NULL_PTR_RETURN_VALUE(cfg, HDF_ERR_INVALID_PARAM); 3222 CHECK_NULL_PTR_RETURN_VALUE(event, HDF_ERR_INVALID_PARAM); 3223 3224 ret = ReadMxc6655xaRawData(cfg, &rawData, &event->timestamp); 3225 if (ret != HDF_SUCCESS) { 3226 HDF_LOGE("%s: MXC6655XA read raw data failed", __func__); 3227 return HDF_FAILURE; 3228 } 3229 3230 event->sensorId = SENSOR_TAG_ACCELEROMETER; 3231 event->option = 0; 3232 event->mode = SENSOR_WORK_MODE_REALTIME; 3233 3234 rawData.x = rawData.x * MXC6655XA_ACC_SENSITIVITY_2G; 3235 rawData.y = rawData.y * MXC6655XA_ACC_SENSITIVITY_2G; 3236 rawData.z = rawData.z * MXC6655XA_ACC_SENSITIVITY_2G; 3237 3238 tmp[ACCEL_X_AXIS] = (rawData.x * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; 3239 tmp[ACCEL_Y_AXIS] = (rawData.y * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; 3240 tmp[ACCEL_Z_AXIS] = (rawData.z * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; 3241 3242 ret = SensorRawDataToRemapData(cfg->direction, tmp, sizeof(tmp) / sizeof(tmp[0])); 3243 if (ret != HDF_SUCCESS) { 3244 HDF_LOGE("%s: MXC6655XA convert raw data failed", __func__); 3245 return HDF_FAILURE; 3246 } 3247 3248 event->dataLen = sizeof(tmp); 3249 event->data = (uint8_t *)&tmp; 3250 3251 return ret; 3252} 3253``` 3254 3255Initialization: 3256 3257```c 3258static int32_t InitMxc6655xa(struct SensorCfgData *data) 3259{ 3260 int32_t ret; 3261 3262 CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); 3263 ret = SetSensorRegCfgArray(&data->busCfg, data->regCfgGroup[SENSOR_INIT_GROUP]); 3264 if (ret != HDF_SUCCESS) { 3265 HDF_LOGE("%s: MXC6655XA sensor init config failed", __func__); 3266 return HDF_FAILURE; 3267 } 3268 return HDF_SUCCESS; 3269} 3270``` 3271 3272### HCS Configuration 3273 3274HCS configuration of the MXC6655XA accel sensor driver: 3275 3276```c 3277device_sensor_mxc6655xa :: device { 3278 device0 :: deviceNode { 3279 policy = 1; 3280 priority = 120; 3281 preload = 0; 3282 permission = 0664; 3283 moduleName = "HDF_SENSOR_ACCEL_MXC6655XA"; 3284 serviceName = "hdf_accel_mxc6655xa"; 3285 deviceMatchAttr = "hdf_sensor_accel_mxc6655xa_driver"; 3286 } 3287} 3288``` 3289 3290Register group configuration information of the MXC6655XA accel sensor: 3291 3292```c 3293#include "../sensor_common.hcs" 3294root { 3295 accel_mxc6655xa_chip_config : sensorConfig { 3296 match_attr = "hdf_sensor_accel_mxc6655xa_driver"; 3297 sensorInfo :: sensorDeviceInfo { 3298 sensorName = "accelerometer"; 3299 vendorName = "memsi_mxc6655xa"; // max string length is 16 bytes 3300 sensorTypeId = 1; // enum SensorTypeTag 3301 sensorId = 1; // user define sensor id 3302 power = 230; 3303 } 3304 sensorBusConfig :: sensorBusInfo { 3305 busType = 0; // 0:i2c 1:spi 3306 busNum = 5; 3307 busAddr = 0x15; 3308 regWidth = 1; // 1byte 3309 } 3310 sensorIdAttr :: sensorIdInfo { 3311 chipName = "mxc6655xa"; 3312 chipIdRegister = 0x0f; 3313 chipIdValue = 0x05; 3314 } 3315 sensorDirection { 3316 direction = 5; // chip direction range of value:0-7 3317 /* <sign> 1:negative 0:positive 3318 <map> 0:AXIS_X 1:AXIS_Y 2:AXIS_Z 3319 */ 3320 /* sign[AXIS_X], sign[AXIS_Y], sign[AXIS_Z], map[AXIS_X], map[AXIS_Y], map[AXIS_Z] */ 3321 convert = [ 3322 0, 0, 0, 0, 1, 2, 3323 1, 0, 0, 1, 0, 2, 3324 0, 0, 1, 0, 1, 2, 3325 0, 1, 0, 1, 0, 2, 3326 1, 0, 1, 0, 1, 2, 3327 0, 0, 1, 1, 0, 2, 3328 0, 1, 1, 0, 1, 2, 3329 1, 1, 1, 1, 0, 2 3330 ]; 3331 } 3332 sensorRegConfig { 3333 /* regAddr: register address 3334 value: config register value 3335 len: size of value 3336 mask: mask of value 3337 delay: config register delay time (ms) 3338 opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit 3339 calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift 3340 shiftNum: shift bits 3341 debug: 0-no debug 1-debug 3342 save: 0-no save 1-save 3343 */ 3344 /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */ 3345 initSeqConfig = [ 3346 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, 3347 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 3348 ]; 3349 enableSeqConfig = [ 3350 0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0, 3351 0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0, 3352 0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0 3353 ]; 3354 disableSeqConfig = [ 3355 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 3356 ]; 3357 } 3358 } 3359} 3360``` 3361 3362### Testing 3363 3364The three-axis data of the sensor can be obtained during the UT test. 3365 3366Test code path: 3367 3368```c 3369drivers/peripheral/sensor/test/unittest/common/hdf_sensor_test.cpp 3370``` 3371 3372Build UT code: 3373 3374```c 3375./build.sh --product-name rk3568 --build-target hdf_test_sensor 3376``` 3377 3378Push **hdf_test_sensor.bin** to the **system/bin** directory, add the execute permission to the file, and execute the file. 3379 3380If the following information is displayed, the sensor test is successful. 3381 3382```c 3383SensorTestDataCallback enter 3384sensor id :[1], data[1]: 0.001877 3385sensor id :[1], data[2]: 0.160823 3386sensor id :[1], data[3]: 0.046122 3387``` 3388 3389## Vibrator 3390 3391### Vibrator Model 3392 3393The vibrator driver model provides and implements vibrator-related HDIs. It supports the time sequence configuration in the static HCS and the duration configuration through dynamic parameters. The vibrator hardware service calls **StartOnce** to start one-shot vibration for a given duration and calls **StartEffect** to start vibration with a specified effect. 3394 3395**Figure 1** Architecture of the vibrator driver module 3396 3397 3398 3399The RK3568 supports the linear vibrator. The OpenHarmony mainline version has the overall driver framework. You only need to implement the specific component driver. 3400 3401### HDF Driver Implementation 3402 3403Code path: 3404 3405```c 3406drivers/framework/model/misc/vibrator/driver/chipset/vibrator_linear_driver.c 3407``` 3408 3409Implementation of the linear vibrator accelerometer driver entry function: 3410 3411```c 3412struct HdfDriverEntry g_linearVibratorDriverEntry = { 3413 .moduleVersion = 1, 3414 .moduleName = "HDF_LINEAR_VIBRATOR", 3415 .Bind = BindLinearVibratorDriver, 3416 .Init = InitLinearVibratorDriver, 3417 .Release = ReleaseLinearVibratorDriver, 3418}; 3419 3420HDF_INIT(g_linearVibratorDriverEntry); 3421``` 3422 3423### HCS Configuration 3424 3425HCS configuration of the driver: 3426 3427```c 3428 vibrator :: host { 3429 hostName = "vibrator_host"; 3430 device_vibrator :: device { 3431 device0 :: deviceNode { 3432 policy = 2; 3433 priority = 100; 3434 preload = 0; 3435 permission = 0664; 3436 moduleName = "HDF_VIBRATOR"; 3437 serviceName = "hdf_misc_vibrator"; 3438 deviceMatchAttr = "hdf_vibrator_driver"; 3439 } 3440 } 3441 device_linear_vibrator :: device { 3442 device0 :: deviceNode { 3443 policy = 1; 3444 priority = 105; 3445 preload = 0; 3446 permission = 0664; 3447 moduleName = "HDF_LINEAR_VIBRATOR"; 3448 serviceName = "hdf_misc_linear_vibrator"; 3449 deviceMatchAttr = "hdf_linear_vibrator_driver"; 3450 } 3451 } 3452 } 3453``` 3454 3455HCS configuration of the linear vibrator: 3456 3457```c 3458root { 3459 linearVibratorConfig { 3460 boardConfig { 3461 match_attr = "hdf_linear_vibrator_driver"; 3462 vibratorChipConfig { 3463 busType = 1; // 0:i2c 1:gpio 3464 gpioNum = 154; 3465 startReg = 0; 3466 stopReg = 0; 3467 startMask = 0; 3468 } 3469 } 3470 } 3471} 3472``` 3473 3474### UT Test 3475 3476Test code path: 3477 3478```c 3479drivers/peripheral/misc/vibrator/test/unittest/common/hdf_vibrator_test.cpp 3480``` 3481 3482Build UT code: 3483 3484```c 3485./build.sh --product-name rk3568 --build-target hdf_test_vibrator 3486``` 3487 3488Push **hdf_test_vibrator.bin** to the **system/bin** directory, add the execute permission to the file, and execute the file. 3489 3490``` 3491[ RUN ] HdfVibratorTest.CheckVibratorInstanceIsEmpty 3492[ OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms) 3493[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration001 3494[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration001 (2001 ms) 3495[ RUN ] HdfVibratorTest.ExecuteVibratorEffect001 3496[ OK ] HdfVibratorTest.ExecuteVibratorEffect001 (5001 ms) 3497``` 3498