diff --git a/en/device-dev/Readme-EN.md b/en/device-dev/Readme-EN.md index 389ab1daec818dfef68b3ec3770233a8bc51e16d..048e956eb631431d1806199f7bb5f106f9034f76 100644 --- a/en/device-dev/Readme-EN.md +++ b/en/device-dev/Readme-EN.md @@ -19,7 +19,13 @@ - Mini System SoC Porting Cases - [Mini-System Devices with Screens — Bestechnic SoC Porting Case](porting/porting-bes2600w-on-minisystem-display-demo.md) - [Combo Solution — ASR Chip Porting Case](porting/porting-asr582x-combo-demo.md) - + - [IoT Solution - Chipsea CST85 Chip Porting Case](porting/porting-cst85f01-combo-demo.md) + - [Mini System STM32F407 SoC Porting Case](porting/porting-stm32f407-on-minisystem-eth.md) + - [Combo Solution – W800 Chip Porting Case](porting/porting-w800-combo-demo.md) + - Small System SoC Porting Cases + - [Mini-System Devices – STM32MP1 SoC Porting Case](porting/porting-stm32mp15xx-on-smallsystem.md) + - Standard System SoC Porting Cases + - [Standard System Solution – Rockchip RK3568 Porting Case](porting/porting-dayu200-on_standard-demo.md) - Subsystem Development - Kernel - [Kernel for the Mini System](kernel/kernel-mini-overview.md) @@ -47,10 +53,8 @@ - [Mini- and Small-System Devices](guide/device-wlan-led-control.md) - [Standard-System Devices](guide/device-clock-guide.md) - Debugging - - [Test Case Development](subsystems/subsys-testguide-test.md) + - [Test Case Development](device-test/Readme-EN.md) - [R&D Tools](subsystems/subsys-toolchain-hdc-guide.md) -- XTS Certification - - [XTS Test Case Development](subsystems/subsys-xts-guide.md) - Tools - [Docker Environment](get-code/gettools-acquire.md) - [IDE](get-code/gettools-ide.md) diff --git a/en/device-dev/dev-board-on-the-master.md b/en/device-dev/dev-board-on-the-master.md index 92f664523ab6a4b7a0f9757c7cadb5f9a09075d0..d06cbadbb9f3bc19b920949fb25a1ecccf8ed306 100644 --- a/en/device-dev/dev-board-on-the-master.md +++ b/en/device-dev/dev-board-on-the-master.md @@ -1,24 +1,25 @@ # OpenHarmony Development Boards List -Currently, the OpenHarmony community supports 17 types of development boards, as listed in the table below. +Currently, OpenHarmony supports 22 development board models, as listed in the table below. ## Standard-System Development Boards | Board Model| Chip Model| Function Description and Use Case| Application Scenario| Code Repository and Daily Build| | ---------- | -------- | -------- | ------------ | -------------------------------------- | -|HiHope HH-SCDAYU200| RK3568 |Function description:
Bolstered by the Rockchip RK3568, the HH-SCDAYU200 development board integrates the dual-core GPU and efficient NPU. Its quad-core 64-bit Cortex-A55 processor uses the advanced 22 nm fabrication process and is clocked at up to 2.0 GHz. The board is packed with Bluetooth, Wi-Fi, audio, video, and camera features, with a wide range of expansion ports to accommodate various video input and outputs. It comes with dual GE auto-sensing RJ45 ports, so it can be used in multi-connectivity products, such as network video recorders (NVRs) and industrial gateways.
Use case:
DAYU200 Adaptation Case|Entertainment, easy travel, and smart home, such as kitchen hoods, ovens, and treadmills.|Code repositories:
[device_soc_rockchip](https://gitee.com/openharmony/device_soc_rockchip)
[device_board_hihope](https://gitee.com/openharmony/device_board_hihope)
[vendor_hihope](https://gitee.com/openharmony/vendor_hihope)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|HiHope HH-SCDAYU200| RK3568 |Function description:
Bolstered by the Rockchip RK3568, the HH-SCDAYU200 development board integrates the dual-core GPU and efficient NPU. Its quad-core 64-bit Cortex-A55 processor uses the advanced 22 nm fabrication process and is clocked at up to 2.0 GHz. The board is packed with Bluetooth, Wi-Fi, audio, video, and camera features, with a wide range of expansion ports to accommodate various video input and outputs. It comes with dual GE auto-sensing RJ45 ports, so it can be used in multi-connectivity products, such as network video recorders (NVRs) and industrial gateways.
Use case:
[DAYU200 Adaptation Case](porting/porting-dayu200-on_standard-demo.md)|Entertainment, easy travel, and smart home, such as kitchen hoods, ovens, and treadmills.|Code repositories:
[device_soc_rockchip](https://gitee.com/openharmony/device_soc_rockchip)
[device_board_hihope](https://gitee.com/openharmony/device_board_hihope)
[vendor_hihope](https://gitee.com/openharmony/vendor_hihope)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | |Hispark_Phoenix|Hi3751V351|Function description:
Hi3751 V351 is a main processor designed for FHD smart televisions, conforming to a wide range of global television standards. It supports NTSC/PAL/SECAM demodulation, DTMB/DVB-C/ATSC/ISDB-T demodulation, and DVB-T/T2/S/S2 extension. The SoC running AOSP has a built-in multi-core Arm Cortex-A53 CPU, multi-core Arm Mali-T450 GPU, and 1 GB DDR SDRAM. It can handle USB playback, video formats such as MPEG2, H.264, H.265, RMVB, and AVS+, as well as audio decoding and sound processing [including in-house super wide sound (SWS) processing]. It also supports CVBS/YPbPr/VGA/HDMI 1.4/USB inputs, LVDS/mini-LVDS interfaces, and mainstream TCONLESS panels.|Smart TVs, smart home central control screens, smart displays, commercial display advertising screens, interactive whiteboards, industrial control screens, printer screens, white good screens, and fitness device display screens.|Code repositories:
[device_soc_hisilicon](https://gitee.com/openharmony/device_soc_hisilicon)
[device_board_hisilicon](https://gitee.com/openharmony/device_board_hisilicon)
[vendor_hisilicon](https://gitee.com/openharmony/vendor_hisilicon)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| |Unionpi Tiger|Amlogic A311D|Function description:
Unionpi Tiger is a piece of intelligent hardware used in scenarios such as image processing, audio and video processing, and deep learning. Its main processor Amlogic A311D supports the GPU and neural network acceleration subsystem, 4K video codec engine, industry-leading HDR image processing, and all standards-based audio/video input/output interfaces. The CPU of the active system uses the big-core and small-core design with a clock speed of up to 2.2 GHz. It integrates four Cortex-A73 cores, two Cortex-A53 cores, and a standalone 5.0T NPU.|Smart home, AI facial recognition, industrial control, smart vehicle-mounted devices, multimedia processing, and AI edge computing.|Code repositories:
[device_soc_amlogic](https://gitee.com/openharmony/device_soc_amlogic)
[device_board_unionman](https://gitee.com/openharmony/device_board_unionman)
[vendor_unionman](https://gitee.com/openharmony/vendor_unionman)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| |MILOS_Standard0|NXP i.MX8M Mini|Function description:
MILOS_Standard0 is powered by the NXP i.MX8M Mini processor and clocked at up to 1.8 GHz. It integrates a wide range of peripheral input and output ports, including LVDS, MIPI-DSI output, and MIPI-CSI camera ports, as well as GE, multi-channel USB, and multi-serial communications ports.|High-performance instruments and meters (industry and medical care), industrial control and human-machine interaction devices, intelligent transportation, smart fire fighting, and smart buildings.|Code repositories:
[device_soc_nxp](https://gitee.com/openharmony/device_soc_nxp)
[device_board_osware](https://gitee.com/openharmony/device_board_osware)
[vendor_osware](https://gitee.com/openharmony/vendor_osware)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| |Yangfan development board|RK3399|Function description:
The Yangfan development board is built based on the RK3399 chip. The RK3399 CPU uses the big.LITTLE core architecture, with two Cortex-A72 big cores and four Cortex-A53 small cores. It provides significant improvements over its predecessor in terms of integers, floating point numbers, memory, overall performance, power consumption, and core area. The RK3399 GPU leverages the next-generation quad-core Arm Mali-T860 processor. The various bandwidth compression technologies (such as intelligent overlay, ASTC, and local pixel storage) and a wide array of graphics and computing interfaces make it more capable of complex graphics use cases.|Interactive advertising machines, interactive digital signage, intelligent self-service terminals, intelligent retail terminals, industrial control hosts, and robot devices.|Code repositories:
[device_soc_rockchip](https://gitee.com/openharmony/device_soc_rockchip)
[device_board_isoftstone](https://gitee.com/openharmony/device_board_isoftstone)
[vendor_isoftstone](https://gitee.com/openharmony/vendor_isoftstone)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| |ZLG development board|T507|Function description:
The ZLG development board is equipped with the Quanzhi industrial-grade T507 chip, which integrates the quad-core CortexTM A53 CPU, G31 MP2 GPU, multi-channel video outputs (RGB/2 x LVDS/HDMI/CVBS OUT), and multi-channel video inputs (MIPI CSI/BT.656/BT.1120). It allows for 4K@60 fps H.265 decoding, 4K@25 fps H.264 decoding, DI, and 3D noise reduction. All these features, combined with its automatic color tuning system and trapezoid correction module, contribute to a smooth user experience and professional visual effects.|Industrial control, smart cockpits, smart home, smart electricity, and online education.|Code repositories:
[device_soc_allwinner](https://gitee.com/openharmony/device_soc_allwinner)
[device_board_isoftstone](https://gitee.com/openharmony/device_board_isoftstone)
[vendor_isoftstone](https://gitee.com/openharmony/vendor_isoftstone)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| +|Venus series smart TV khdvk_3566b development board|RK3566|Function description:
The khdvk_3566b development board comes with the Rockchip RK3566 Cortex-A55 quad-core processor. Thanks to a wide range of universal display ports and port types, it can be connected to a myriad of peripherals to meet the requirements of various man-machine interaction scenarios.|Tablets, learning machines, and facial recognition devices, including turnstiles, devices for facial recognition payment, industrial robots, medical devices, license plate recognition devices, advertising machines, digital signage, smart self-service terminals, and smart retail terminals.|Code repositories:
[device_soc_rockchip](https://gitee.com/openharmony/device_soc_rockchip)
[device_board_kaihong](https://gitee.com/openharmony/device_board_kaihong)
[vendor_kaihong](https://gitee.com/openharmony/vendor_kaihong)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| ## Small-System Development Boards | Board Model| Chip Model| Function Description and Use Case| Application Scenario| Code Repository and Daily Build| | ---------- | -------- | -------- | ------------ | -------------------------------------- | -|Hispark_Taurus|Hi3516DV300|Function description:
Hi3516D V300 is the next-generation system on chip (SoC) for smart HD IP cameras. It integrates the next-generation image signal processor (ISP), H.265 video compression encoder, and high-performance NNIE engine, and delivers high performance in terms of low bit rate, high image quality, intelligent processing and analysis, and low power consumption.|Smart devices with screens, such as refrigerators with screens and head units.|Code repositories:
[device_soc_hisilicon](https://gitee.com/openharmony/device_soc_hisilicon)
[device_board_hisilicon](https://gitee.com/openharmony/device_board_hisilicon)
[vendor_hisilicon](https://gitee.com/openharmony/vendor_hisilicon)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| -|BearPi-HM Micro|STM32MP157A|Function description:
The BearPi-HM Micro development board is equipped with the STM32MP157 chip, works with a 4.3-inch LCD capacitive touchscreen, and comes with Wi-Fi circuits and standards-based E53 interfaces. The E53 interfaces can be used to connect to smart humidifiers, smart desk lamps, smart security facilities, and intelligent smoke detectors.
Use case:
BearPi-HM Mircro Adaptation Case|Smart home, smart hardware, and central control screens.|Code repositories:
[device_soc_st](https://gitee.com/openharmony/device_soc_st)
[device_board_bearpi](https://gitee.com/openharmony/device_board_bearpi)
[vendor_bearpi](https://gitee.com/openharmony/vendor_bearpi)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|Hispark_Taurus|Hi3516DV300|Function description:
Hi3516DV300 is the next-generation system on chip (SoC) for smart HD IP cameras. It integrates the next-generation image signal processor (ISP), H.265 video compression encoder, and high-performance NNIE engine, and delivers high performance in terms of low bit rate, high image quality, intelligent processing and analysis, and low power consumption.|Smart devices with screens, such as refrigerators with screens and head units.|Code repositories:
[device_soc_hisilicon](https://gitee.com/openharmony/device_soc_hisilicon)
[device_board_hisilicon](https://gitee.com/openharmony/device_board_hisilicon)
[vendor_hisilicon](https://gitee.com/openharmony/vendor_hisilicon)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| +|BearPi-HM Micro|STM32MP157A|Function description:
The BearPi-HM Micro development board is equipped with the STM32MP157 chip, works with a 4.3-inch LCD capacitive touchscreen, and comes with Wi-Fi circuits and standards-based E53 interfaces. The E53 interfaces can be used to connect to smart humidifiers, smart desk lamps, smart security facilities, and intelligent smoke detectors.
Use case:
[BearPi-HM Mircro Adaptation Case](porting/porting-stm32mp15xx-on-smallsystem.md)|Smart home, smart hardware, and central control screens.|Code repositories:
[device_soc_st](https://gitee.com/openharmony/device_soc_st)
[device_board_bearpi](https://gitee.com/openharmony/device_board_bearpi)
[vendor_bearpi](https://gitee.com/openharmony/vendor_bearpi)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | ## Mini-System Development Boards @@ -28,8 +29,11 @@ Currently, the OpenHarmony community supports 17 types of development boards, as |Multi-modal V200Z-R|BES2600|Function description:
The multi-modal V200Z-R development board is a high-performance, multi-functional, and cost-effective AIoT SoC powered by the BES2600WM chip of Bestechnic. It integrates a quad-core Arm processor with a frequency of up to 1 GHz as well as dual-mode Wi-Fi and dual-mode Bluetooth. The board supports the 802.11 a/b/g/n/ and BT/BLE 5.2 standards. It is able to accommodate RAM of up to 42 MB and flash memory of up to 32 MB, and supports the MIPI display serial interface (DSI) and camera serial interface (CSI). It is applicable to various AIoT multi-modal VUI and GUI interaction scenarios.
Use case:
[Multi-modal V200Z-R Adaptation Case](porting/porting-bes2600w-on-minisystem-display-demo.md)|Smart hardware, and smart devices with screens, such as speakers and watches.|Code repositories:
[device_soc_bestechnic](https://gitee.com/openharmony/device_soc_bestechnic)
[device_board_fnlink](https://gitee.com/openharmony/device_board_fnlink)
[vendor_bestechnic](https://gitee.com/openharmony/vendor_bestechnic)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | |Langguo LANGO200|ASR582X|Function description:
The LANGO200 IoT development board integrates the high-performance WiFi-BLE dual-mode chip ASR5822, external storage chip, voice playing chip, and analog-to-digital converter. It supports common peripheral interfaces like SPI, and can be connected to an external OLED display and infrared remote control.
Use case:
[LANGO200 Adaptation Case](porting/porting-asr582x-combo-demo.md)|Connection modules for smart home products.|Code repositories:
[device_soc_asrmicro](https://gitee.com/openharmony/device_soc_asrmicro)
[device_board_lango](https://gitee.com/openharmony/device_board_lango)
[vendor_asrmicro](https://gitee.com/openharmony/vendor_asrmicro)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | |Goodix GR5515-STARTER-KIT|GR5515|Function description:
The GR5515-STARTER-KIT development board is a Bluetooth 5.1-capable single-mode Bluetooth low energy (BLE) SoC and comes with multifunctional keys and LED indicators.|Smart hardware, such as watches, wristbands, and price tags.|Code repositories:
[device_soc_goodix](https://gitee.com/openharmony/device_soc_goodix)
[device_board_goodix](https://gitee.com/openharmony/device_board_goodix)
[vendor_goodix](https://gitee.com/openharmony/vendor_goodix)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| -|Niobe407|STM32F407IGT6|Function description:
The Niobe407 development board is powered by the STM32F407 chip, which integrates the Arm Cortex-M4 CPU with the FPU and adaptive real-time accelerator and is cloced at up to 168 MHz.
Use case:
Niobe407 Adaptation Case|Smart transportation and industrial control.|Code repositories:
[device_soc_st](https://gitee.com/openharmony/device_soc_st)
[device_board_talkweb](https://gitee.com/openharmony/device_board_talkweb)
[vendor_talkweb](https://gitee.com/openharmony/vendor_talkweb)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|Niobe407|STM32F407IGT6|Function description:
The Niobe407 development board is powered by the STM32F407 chip, which integrates the Arm Cortex-M4 CPU with the FPU and adaptive real-time accelerator and is clocked at up to 168 MHz.
Use case:
[Niobe407 Adaptation Case](porting/porting-stm32f407-on-minisystem-eth.md)|Smart transportation and industrial control.|Code repositories:
[device_soc_st](https://gitee.com/openharmony/device_soc_st)
[device_board_talkweb](https://gitee.com/openharmony/device_board_talkweb)
[vendor_talkweb](https://gitee.com/openharmony/vendor_talkweb)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | |B91 Generic Starter Kit|TLSR9x|Function description:
The B91 Generic Starter Kit developed by Telink is a hardware platform that can be used to evaluate TLSR9 chipsets. You can leverage it to develop applications that comply with the 2.4 GHz interface standards, such as BLE, BLE Mesh, Zigbee 3.0, Thread, and 2.4 GHz proprietary protocols.|Connection modules for smart home products.|Code repositories:
[device_soc_telink](https://gitee.com/openharmony/device_soc_telink)
[device_board_telink](https://gitee.com/openharmony/device_board_telink)
[vendor_telink](https://gitee.com/openharmony/vendor_telink)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds| -|cst85_wblink|cst85f01|Function description:
The cst85_wblink development board is a high-performance, multi-functional, and cost-effective AIoT SoC development board developed by CHIPSEA based on the cst85f01 chip. The cst85_wblink development board integrates dual-band Wi-Fi and dual-mode Bluetooth and supports standard 802.11 a/b/g/n protocols and Bluetooth/BLE 5.0 protocols. It comes in multiple memory flavors, with up to 992 KB of RAM and up to 16 MB of flash memory. In addition, it supports MIPI DSI and CSI, which makes it a great choice for developing Wi-Fi and Bluetooth applications for IoT and smart devices.
Use case:
cst85_wblink Adaptation Case|IoT and smart home.|Code repositories:
[device_soc_chipsea](https://gitee.com/openharmony/device_soc_chipsea)
[device_board_chipsea](https://gitee.com/openharmony/device_board_chipsea)
[vendor_chipsea](https://gitee.com/openharmony/vendor_chipsea)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | -|Neptune100| W800 |Function description:
Based on the WinnerMicro W800 chip, the Neptune 100 development board is a Wi-Fi & Bluetooth dual-mode SoC development board. It supports the standard 802.11 b/g/n protocols and has a complete built-in TCP/IP protocol stack. Its built-in Bluetooth baseband processor supports the Bluetooth/BLE4.2 protocols. It provides various digital interfaces, including the built-in QFlash, SPI, UART, GPIO, I2C, I2S and 7816 interfaces. The development board provides reliable security with support for a wide array of security features: hardware encryption and decryption algorithms, built-in DSP, floating-point arithmetic unit, security engine, code security permission configuration, built-in 2 MB flash memory, firmware encryption storage, firmware signature, secure debugging, and secure upgrade.
Use case:
Neptune 100 Adaptation Case|IoT, smart home, and connection products.|Code repositories:
[device_soc_winnermicro](https://gitee.com/openharmony/device_soc_winnermicro)
[device_board_hihope](https://gitee.com/openharmony/device_board_hihope)
[vendor_hihope](https://gitee.com/openharmony/vendor_hihope)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|cst85_wblink|cst85f01|Function description:
The cst85_wblink development board is a high-performance, multi-functional, and cost-effective AIoT SoC development board developed by CHIPSEA based on the cst85f01 chip. The cst85_wblink development board integrates dual-band Wi-Fi and dual-mode Bluetooth and supports standard 802.11 a/b/g/n protocols and Bluetooth/BLE 5.0 protocols. It comes in multiple memory flavors, with up to 992 KB of RAM and up to 16 MB of flash memory. In addition, it supports MIPI DSI and CSI, which makes it a great choice for developing Wi-Fi and Bluetooth applications for IoT and smart devices.
Use case:
[cst85_wblink Adaptation Case](porting/porting-cst85f01-combo-demo.md)|IoT and smart home.|Code repositories:
[device_soc_chipsea](https://gitee.com/openharmony/device_soc_chipsea)
[device_board_chipsea](https://gitee.com/openharmony/device_board_chipsea)
[vendor_chipsea](https://gitee.com/openharmony/vendor_chipsea)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|Neptune100| W800 |Function description:
Based on the WinnerMicro W800 chip, the Neptune 100 development board is a Wi-Fi & Bluetooth dual-mode SoC development board. It supports the standard 802.11 b/g/n protocols and has a complete built-in TCP/IP protocol stack. Its built-in Bluetooth baseband processor supports the Bluetooth/BLE4.2 protocols. It provides various digital interfaces, including the built-in QFlash, SPI, UART, GPIO, I2C, I2S and 7816 interfaces. The development board provides reliable security with support for a wide array of security features: hardware encryption and decryption algorithms, built-in DSP, floating-point arithmetic unit, security engine, code security permission configuration, built-in 2 MB flash memory, firmware encryption storage, firmware signature, secure debugging, and secure upgrade.
Use case:
[Neptune 100 Adaptation Case](porting/porting-w800-combo-demo.md)|IoT, smart home, and connection products.|Code repositories:
[device_soc_winnermicro](https://gitee.com/openharmony/device_soc_winnermicro)
[device_board_hihope](https://gitee.com/openharmony/device_board_hihope)
[vendor_hihope](https://gitee.com/openharmony/vendor_hihope)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | |RK2206| RK2206 |Function description:
The RK2206 development board uses the high-performance and cost-effective RK2206 chip as its main control board. It runs the OpenHarmony mini system and provides built-in Wi-Fi/AP and NFC features as well as LCD and E53 interfaces. The E53 interfaces are compatible with various sensor modules, facilitating diversified IoT applications. The RK2006 development board has been deployed in more than 20 mature use cases and demonstrated in comprehensive courseware.|Smart city, smart home, smart teaching, smart vehicle-mounted devices, and smart healthcare.|Code repositories:
[device_soc_rockchip](https://gitee.com/openharmony/device_soc_rockchip)
[device_board_lockzhiner](https://gitee.com/openharmony/device_board_lockzhiner)
[vendor_lockzhiner](https://gitee.com/openharmony/vendor-lockzhiner)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|HPM6750EVK2 development board| HPM6700 |Function description:
The HPM6700/6400 series MCU is a high-performance real-time RISC-V microcontroller from Shanghai Xianji Semiconductor Technology Co., Ltd.. It provides high computing power, efficient control, and various multimedia functions for industrial automation and edge computing applications.|Industrial control and edge computing.|Code repositories:
[device_soc_hpmicro](https://gitee.com/openharmony/device_soc_hpmicro)
[device_board_hpmicro](https://gitee.com/openharmony/device_board_hpmicro)
[vendor_hpmicro](https://gitee.com/openharmony/vendor_hpmicro)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|NiobeU4| ESP32U4WDH |Function description:
NiobeU4 is an IoT device development suite developed based on ESP32U4WDH. It integrates 2.4 GHz Wi-Fi and dual-mode Bluetooth and features ultra-high RF performance, stability, universality, reliability, and ultra-low power consumption. It works at 13.56 MHz and supports NFC contactless communication.|Contactless reader/writer applications that require low power consumption, voltage, and cost; lithium battery power supply and charge/discharge management; out-of-the-box intelligent hardware solution for developers to verify and develop their own software and functions.|Code repositories:
[device_soc_esp](https://gitee.com/openharmony/device_soc_esp)
[device_board_openvalley ](https://gitee.com/openharmony/device_board_openvalley)
[vendor_openvalley](https://gitee.com/openharmony/vendor_openvalley)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | +|BK7235 development board| BK7235 |Function description:
BK7235 is a highly integrated Wi-Fi 6+BLE 5.2 combo SoC developed by Beken for IoT applications. It features diverse array of resources, powerful performance, and high security.|IoT, smart home, and connection products.|Code repositories:
[device_soc_beken](https://gitee.com/openharmony/device_soc_beken)
[device_board_beken](https://gitee.com/openharmony/device_board_beken)
[vendor_beken](https://gitee.com/openharmony/vendor_beken)
Daily build:
http://ci.openharmony.cn/dailys/dailybuilds | diff --git a/en/device-dev/device-test/Readme-EN.md b/en/device-dev/device-test/Readme-EN.md new file mode 100644 index 0000000000000000000000000000000000000000..4ca9e2c13290f008cf73f231874cf598cb1edc0b --- /dev/null +++ b/en/device-dev/device-test/Readme-EN.md @@ -0,0 +1,6 @@ +# Device Test + +- [Development Self-Test Framework User Guide](developer_test.md) +- [xDevice User Guide](xdevice.md) +- [XTS Test Case Development Guide](xts.md) + diff --git a/en/device-dev/device-test/developer_test.md b/en/device-dev/device-test/developer_test.md new file mode 100644 index 0000000000000000000000000000000000000000..4faa7c331566e469bc01ac7698fa2a3de228d9d8 --- /dev/null +++ b/en/device-dev/device-test/developer_test.md @@ -0,0 +1,975 @@ +# Development Self-Test Framework User Guide + + +## Overview + +OpenHarmony provides you with a comprehensive development self-test framework **developer_test**. As a part of the OpenHarmony test toolset, the development self-test framework is provided for you to test the development by yourself. You can develop relevant test cases based on your test requirements to discover defects at the development phase, greatly improving the code quality. + +This document describes how to use the development self-test framework of OpenHarmony. + + +### Introduction + +After adding or modifying code, OpenHarmony developers want to quickly verify whether the modified code functions properly, and the system already has a large number of automated test cases of existing functions, such as TDD cases and XTS cases. The development self-test framework aims to help you improve your self-test efficiency so that you can quickly execute the specified automated test cases and conducting development tests at the development phase. + + +### Constraints + +When executing test cases using the framework, you must connect to the OpenHarmony device in advance. + + +## Environment Preparations + +The development self-test framework depends on the Python environment. It is required that the Python version be 3.7.5 or later. Before using the framework, you can refer to the following document for configuration. + +Click [here](https://gitee.com/openharmony/docs/blob/master/en/device-dev/get-code/sourcecode-acquire.md) to obtain the source code. + +### Basic Self-Test Framework Environment + +| Environment Dependency | Version | Description | +| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| Operating system | Ubuntu 18.04 or later | Code compilation environment. | +| Linux extension component| libreadline-dev | Plugin used to read commands. | +| python | 3.7.5 or later | Language used by the test framework. | +| Python plugins | pyserial 3.3 or later, paramiko 2.7.1 or later, setuptools 40.8.0 or later, and rsa4.0 or later| - **pserial**: supports Python serial port communication.
- **paramiko**: allows Python to use SSH.
- **setuptools**: allows Python packages to be created and distributed easily.
- **rsa**: implements RSA encryption in Python.| +| NFS Server | haneWIN NFS Server 1.2.50 or later or NFS v4 or later | Devices can be connected using serial ports. Mini- and small-system devices are used. | +| HDC | 1.1.0 | A tool that enables devices to be connected through the HarmonyOS Device Connector (HDC). | + + + +1. Run the following command to install the Linux extended component readline: + + ```bash + sudo apt-get install libreadline-dev + ``` + The installation is successful if the following information is displayed: + ``` + Reading package lists... Done + Building dependency tree + Reading state information... Done + libreadline-dev is already the newest version (7.0-3). + 0 upgraded, 0 newly installed, 0 to remove and 11 not upgraded. + ``` + +2. Run the following command to install the **setuptools** plugin: + ```bash + pip3 install setuptools + ``` + The installation is successful if the following information is displayed: + ``` + Requirement already satisfied: setuptools in d:\programs\python37\lib\site-packages (41.2.0) + ``` + +3. Run the following command to install the **paramiko** plugin: + ```bash + pip3 install paramiko + ``` + The installation is successful if the following information is displayed: + ``` + Installing collected packages: pycparser, cffi, pynacl, bcrypt, cryptography, paramiko + Successfully installed bcrypt-3.2.0 cffi-1.14.4 cryptography-3.3.1 paramiko-2.7.2 pycparser-2.20 pynacl-1.4.0 + ``` + +4. Run the following command to install the **ras** plugin: + ```bash + pip3 install rsa + ``` + The installation is successful if the following information is displayed: + ``` + Installing collected packages: pyasn1, rsa + Successfully installed pyasn1-0.4.8 rsa-4.7 + ``` + +5. Run the following command to install the **pyserial** plugin: + ```bash + pip3 install pyserial + ``` + The installation is successful if the following information is displayed: + ``` + Requirement already satisfied: pyserial in d:\programs\python37\lib\site-packages\pyserial-3.4-py3.7.egg (3.4) + ``` + +6. Install the NFS server if the device outputs results only through the serial port. + + > This step is applicable to small-system or mini-system devices. + + - Windows OS: Install the **haneWIN NFS Server1.2.50** package. + - Linux OS: Run the following command to install the NFS server: + ```bash + sudo apt install nfs-kernel-server + ``` + The installation is successful if the following information is displayed: + ``` + Reading package lists... Done + Building dependency tree + Reading state information... Done + nfs-kernel-server is already the newest version (1:1.3.4-2.1ubuntu5.3). + 0 upgraded, 0 newly installed, 0 to remove and 11 not upgraded. + ``` + +7. If the device supports HDC connection, install the HDC tool. For details about the installation process, see [HDC-OpenHarmony device connector](https://gitee.com/openharmony/developtools_hdc_standard/blob/master/README.md). + + +### Environment Dependency Check + +| Check Item | Operation | Requirement | +| -------------------------------------------------- | --------------------------------------------------- | ------------------------- | +| Check whether Python is installed successfully. | Run the **python --version** command. | The Python version is 3.7.5 or later. | +| Check whether Python plugins are successfully installed. | Go to the **test/developertest** directory and run **start.bat** or **start.sh**.| The **>>>** prompt is displayed.| +| Check the NFS server status (for the devices that support only serial port output).| Log in to the development board through the serial port and run the **mount** command to mount the NFS. | The file directory can be mounted. | +| Check whether the HDC is successfully installed. | Run the **hdc_std -v** command. | The HDC version is 1.1.0 or later. | + + +## Test Case Preparation + +The test framework supports multiple types of tests and provides different test case templates for them._ + +**TDD Test (C++)** + +Naming rules for source files + +The source file name of test cases must be the same as that of the test suite. The file names must use lowercase letters and in the *Function*_*Sub-function*_**test** format. More specific sub-functions can be added as required. +Example: +``` +calculator_sub_test.cpp +``` + +Test case example +```c++ +/* + * Copyright (c) 2021 XXXX Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "calculator.h" +#include + +using namespace testing::ext; + +class CalculatorSubTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void CalculatorSubTest::SetUpTestCase(void) +{ + // Set a setup function, which will be called before all test cases. +} + +void CalculatorSubTest::TearDownTestCase(void) +{ + // Set a teardown function, which will be called after all test cases. +} + +void CalculatorSubTest::SetUp(void) +{ + // Set a setup function, which will be called before all test cases. +} + +void CalculatorSubTest::TearDown(void) +{ + // Set a teardown function, which will be called after all test cases. +} + +/** + * @tc.name: integer_sub_001 + * @tc.desc: Verify the sub function. + * @tc.type: FUNC + * @tc.require: issueNumber + */ +HWTEST_F(CalculatorSubTest, integer_sub_001, TestSize.Level1) +{ + // Step 1 Call the function to obtain the test result. + int actual = Sub(4, 0); + + // Step 2 Use an assertion to compare the obtained result with the expected result. + EXPECT_EQ(4, actual); +} +``` +The procedure is as follows: + +1. Add comment information to the test case file header. + +``` +/* + * Copyright (c) 2021 XXXX Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +``` + +2. Add the test framework header file and namespace. + +```c++ +#include + +using namespace testing::ext; +``` + +3. Add the header file of the test class. + +```c++ +#include "calculator.h" +``` + +4. Define the test suite (test class). + +```c++ +class CalculatorSubTest : public testing::Test { +public: + static void SetUpTestCase(void); + static void TearDownTestCase(void); + void SetUp(); + void TearDown(); +}; + +void CalculatorSubTest::SetUpTestCase(void) +{ + // Set a setup function, which will be called before all test cases. +} + +void CalculatorSubTest::TearDownTestCase(void) +{ + // Set a teardown function, which will be called after all test cases. +} + +void CalculatorSubTest::SetUp(void) +{ + // Set a setup function, which will be called before all test cases. +} + +void CalculatorSubTest::TearDown(void) +{ + // Set a teardown function, which will be called after all test cases. +}== +``` +> **NOTE** +> +> When defining a test suite, ensure that the test suite name is the same as the target to build and uses the upper camel case style. + +5. Add implementation of the test cases, including test case comments and logic. + +```c++ +/** + * @tc.name: integer_sub_001 + * @tc.desc: Verify the sub function. + * @tc.type: FUNC + * @tc.require: issueNumber + */ +HWTEST_F(CalculatorSubTest, integer_sub_001, TestSize.Level1) +{ + // Step 1 Call the function to obtain the test result. + int actual = Sub(4, 0); + + // Step 2 Use an assertion to compare the obtained result with the expected result. + EXPECT_EQ(4, actual); +} +``` +> **NOTE** +> +> @tc.require: The format must be started with **AR/SR** or **issue**, for example, **issueI56WJ7**. + +The following test case templates are provided for your reference. + +| Type | Description | +| --------------- | ------------------------------------------------ | +| HWTEST(A,B,C) | Use this template if the test case execution does not depend on setup or teardown. | +| HWTEST_F(A,B,C) | Use this template if the test case execution (excluding parameters) depends on setup or teardown.| +| HWTEST_P(A,B,C) | Use this template if the test case execution (including parameters) depends on setup or teardown. | + +In the template names: + +- **A** indicates the test suite name. + +- **B** indicates the test case name, which is in the *Function*_*No.* format. The *No.* is a three-digit number starting from **001**. + +- *C* indicates the test case level. There are five test case levels: guard-control level 0 and non-guard-control level 1 to level 4. Of levels 1 to 4, a smaller value indicates a more important function verified by the test case. + + +**NOTE** + +- The expected result of each test case must have an assertion. + +- The test case level must be specified. + +- It is recommended that the test be implemented step by step according to the template. + +- The comment must contain the test case name, description, type, and requirement number, which are in the @tc.*xxx*: *value* format. The test case type @**tc.type** can be any of the following: + + +| Test Case Type| Code| +| ------------ | -------- | +| Function test | FUNC | +| Performance test | PERF | +| Reliability test | RELI | +| Security test | SECU | +| Fuzzing test | FUZZ | + +**TDD Test (JS)** + +- Naming rules for source files + + +The source file name of a test case must be in the *Function**Sub-function***Test** format, and each part must use the upper camel case style. More specific sub-functions can be added as required. +Example: +``` +AppInfoTest.js +``` + +- Test case example + +```js +/* + * Copyright (C) 2021 XXXX Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import app from '@system.app' + +import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index' + +describe("AppInfoTest", function () { + beforeAll(function() { + // Set a setup function, which will be called before all test cases. + console.info('beforeAll caled') + }) + + afterAll(function() { + // Set a teardown function, which will be called after all test cases. + console.info('afterAll caled') + }) + + beforeEach(function() { + // Set a setup function, which will be called before all test cases. + console.info('beforeEach caled') + }) + + afterEach(function() { + // Set a teardown function, which will be called after all test cases. + console.info('afterEach caled') + }) + + /* + * @tc.name:appInfoTest001 + * @tc.desc:verify app info is not null + * @tc.type: FUNC + * @tc.require: issueNumber + */ + it("appInfoTest001", 0, function () { + // Step 1 Call the function to obtain the test result. + var info = app.getInfo() + + // Step 2 Use an assertion to compare the obtained result with the expected result. + expect(info != null).assertEqual(true) + }) +}) +``` +The procedure is as follows: +1. Add comment information to the test case file header. + ``` + /* + * Copyright (C) 2021 XXXX Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + ``` +2. Import the APIs and JSUnit test library to test. + ```js + import app from '@system.app' + + import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index' + ``` +3. Define the test suite (test class). + ```js + describe("AppInfoTest", function () { + beforeAll(function() { + // Set a setup function, which will be called before all test cases. + console.info('beforeAll caled') + }) + + afterAll(function() { + // Set a teardown function, which will be called after all test cases. + console.info('afterAll caled') + }) + + beforeEach(function() { + // Set a setup function, which will be called before all test cases. + console.info('beforeEach caled') + }) + + afterEach(function() { + // Set a teardown function, which will be called after all test cases. + console.info('afterEach caled') + }) + ``` +4. Add implementation of the test cases. + ```JS + /* + * @tc.name:appInfoTest001 + * @tc.desc:verify app info is not null + * @tc.type: FUNC + * @tc.require: issueNumber + */ + it("appInfoTest001", 0, function () { + // Step 1 Call the function to obtain the test result. + var info = app.getInfo() + + // Step 2 Use an assertion to compare the obtained result with the expected result. + expect(info != null).assertEqual(true) + }) + ``` + > **NOTE** + > + > @tc.require: The format must be started with **issue**, for example, **issueI56WJ7**. + +**Fuzzing Test** + +[Fuzzing case specifications](https://gitee.com/openharmony/test_developertest/blob/master/libs/fuzzlib/README_zh.md) + + +**Benchmark Test** + +[Benchmark case specifications](https://gitee.com/openharmony/test_developertest/blob/master/libs/benchmark/README_zh.md) + +## **Test Case Building** + +When a test case is executed, the test framework searches for the build file of the test case in the test case directory and builds the test case located. The following describes how to write build files (GN files) in different programming languages. + +**TDD Test** + +The following provides templates for different languages for your reference. + +- **Test case build file example (C++)** + +``` +# Copyright (c) 2021 XXXX Device Co., Ltd. + +import("//build/test.gni") + +module_output_path = "developertest/calculator" + +config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ "../../../include" ] +} + +ohos_unittest("CalculatorSubTest") { + module_out_path = module_output_path + + sources = [ + "../../../include/calculator.h", + "../../../src/calculator.cpp", + ] + + sources += [ "calculator_sub_test.cpp" ] + + configs = [ ":module_private_config" ] + + deps = [ "//third_party/googletest:gtest_main" ] +} + +group("unittest") { + testonly = true + deps = [":CalculatorSubTest"] +} +``` +The procedure is as follows: + +1. Add comment information for the file header. + ``` + # Copyright (c) 2021 XXXX Device Co., Ltd. + ``` +2. Import the build template. + ``` + import("//build/test.gni") + ``` +3. Specify the file output path. + ``` + module_output_path = "developertest/calculator" + ``` + > **NOTE**
The output path is ***Part name*/*Module name***. + +4. Configure the directories for dependencies. + + ``` + config("module_private_config") { + visibility = [ ":*" ] + + include_dirs = [ "../../../include" ] + } + ``` + > **NOTE** + > + > Generally, the dependency directories are configured here and directly referenced in the build script of the test case. + +5. Set the output build file for the test cases. + + ``` + ohos_unittest("CalculatorSubTest") { + } + ``` +6. Write the build script (add the source file, configuration, and dependencies) for the test cases. + ``` + ohos_unittest("CalculatorSubTest") { + module_out_path = module_output_path + sources = [ + "../../../include/calculator.h", + "../../../src/calculator.cpp", + "../../../test/calculator_sub_test.cpp" + ] + sources += [ "calculator_sub_test.cpp" ] + configs = [ ":module_private_config" ] + deps = [ "//third_party/googletest:gtest_main" ] + } + ``` + + > **NOTE** + > + > Set the test type based on actual requirements. The following test types are available: + > + > - **ohos_unittest**: unit test + > - **ohos_moduletest**: module test + > - **ohos_systemtest**: system test + > - **ohos_performancetest**: performance test + > - **ohos_securitytest**: security test + > - **ohos_reliabilitytest**: reliability test + > - **ohos_distributedtest**: distributed test + +7. Group the test case files by test type. + + ``` + group("unittest") { + testonly = true + deps = [":CalculatorSubTest"] + } + ``` + > **NOTE** + > + > Grouping test cases by test type allows you to execute a specific type of test cases when required. + +- **Test case build file example (JavaScript)** + +``` +# Copyright (C) 2021 XXXX Device Co., Ltd. + +import("//build/test.gni") + +module_output_path = "developertest/app_info" + +ohos_js_unittest("GetAppInfoJsTest") { + module_out_path = module_output_path + + hap_profile = "./config.json" + certificate_profile = "//test/developertest/signature/openharmony_sx.p7b" +} + +group("unittest") { + testonly = true + deps = [ ":GetAppInfoJsTest" ] +} +``` + +The procedure is as follows: + +1. Add comment information for the file header. + +``` +# Copyright (C) 2021 XXXX Device Co., Ltd. +``` + +2. Import the build template. + +``` +import("//build/test.gni") +``` + +3. Specify the file output path. + +``` +module_output_path = "developertest/app_info" +``` +> **NOTE** +> +> The output path is ***Part name*/*Module name***. + +4. Set the output build file for the test cases. + +``` +ohos_js_unittest("GetAppInfoJsTest") { +} +``` +> **NOTE** +> - Use the **ohos_js_unittest** template to define the JavaScript test suite. Pay attention to the difference between JavaScript and C++. +> - The file generated for the JavaScript test suite must be in .hap format and named after the test suite name defined here. The test suite name must end with **JsTest**. + +5. Configure the **config.json** file and signature file, which are mandatory. + +``` +ohos_js_unittest("GetAppInfoJsTest") { + module_out_path = module_output_path + + hap_profile = "./config.json" + certificate_profile = "//test/developertest/signature/openharmony_sx.p7b" +} +``` +**config.json** is the configuration file required for HAP build. You need to set **target** based on the tested SDK version. Default values can be retained for other items. The following is an example: + +```json +{ + "app": { + "bundleName": "com.example.myapplication", + "vendor": "example", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 4, + "target": 5 // Set it based on the tested SDK version. In this example, SDK5 is used. + } + }, + "deviceConfig": {}, + "module": { + "package": "com.example.myapplication", + "name": ".MyApplication", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "name": "com.example.myapplication.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "MyApplication", + "type": "page", + "launchType": "standard" + } + ], + "js": [ + { + "pages": [ + "pages/index/index" + ], + "name": "default", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } + } +``` + +6. Group the test case files by test type. + +``` +group("unittest") { + testonly = true + deps = [ ":GetAppInfoJsTest" ] +} +``` +> **NOTE** +> +> Grouping test cases by test type allows you to execute a specific type of test cases when required. + +**Fuzzing Test** + +[Fuzzing case specifications](https://gitee.com/openharmony/test_developertest/blob/master/libs/fuzzlib/README_zh.md) + +**Benchmark Test** + +[Benchmark case specifications](https://gitee.com/openharmony/test_developertest/blob/master/libs/benchmark/README_zh.md) + + +**Configuring ohos.build** + +Configure the part build file to associate with specific test cases. +``` +"partA": { + "module_list": [ + + ], + "inner_list": [ + + ], + "system_kits": [ + + ], + "test_list": [ // Test under configuration module calculator. + "//system/subsystem/partA/calculator/test:unittest", + "//system/subsystem/partA/calculator/test:fuzztest", + "//system/subsystem/partA/calculator/test:benchmarktest" + } +``` +> **NOTE**
**test_list** contains the test cases of the corresponding module. + +## Configuring Test Resources + +Test resources include external file resources, such as image files, video files, and third-party libraries, required for test case execution. + +Perform the following steps: + +1. Create a **resource** directory under the **test** directory of the part, create a corresponding module directory under the **resource** directory, and store the resource files required in this module directory. + +2. In the module directory under **resource**, create the **ohos_test.xml** file in the following format: + +```xml + + + + + + + +``` + +3. In the build file of the test cases, configure **resource_config_file** to point to the resource file **ohos_test.xml**. + +``` +ohos_unittest("CalculatorSubTest") { + resource_config_file = "//system/subsystem/partA/test/resource/calculator/ohos_test.xml" +} +``` +>**NOTE** +>- **target_name** indicates the test suite name defined in the **BUILD.gn** file in the **test** directory. **preparer** indicates the action to perform before the test suite is executed. +>- **src="res"** indicates that the test resources are in the **resource** directory under the **test** directory. **src="out"** indicates that the test resources are in the **out/release/$(*part*)** directory. + +## Test Case Execution + +### Configuration File + +Before executing test cases, you need to modify the configuration based on the device used. + +#### Modifying user_config.xml +```xml + + + + false + + false + + true + + + + + + + + + + + + + + + + + cmd + 115200 + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + +``` +>**NOTE** +> +>If HDC is connected to the device before the test cases are executed, you only need to configure the device IP address and port number, and retain the default settings for other parameters. + +### Command Description + +1. Start the test framework. + ``` + start.bat + ``` +2. Select the product. + + After the test framework starts, you are asked to select a product. Select the development board to test. + + If you need to manually add a product, add it within the **\** tag to **config/framework_config.xml**. + +3. Execute test cases. + + Run the following command to execute test cases: + ``` + run -t UT -ts CalculatorSubTest -tc interger_sub_00l + ``` + In the command: + ``` + -**t [TESTTYPE]**: specifies the test type, which can be **UT**, **MST**, **ST**, **PERF**, **FUZZ**, or **BENCHMARK**. This parameter is mandatory. + -**tp [TESTPART]**: specifies the part to test. This parameter can be used independently. + -**tm [TESTMODULE]**: specifies the module to test. This parameter must be specified together with **-tp**. + -**ts [TESTSUITE]**: specifies a test suite. This parameter can be used independently. + -**tc [TESTCASE]**: specifies a test case. This parameter must be specified together with **-ts**. + -**h**: displays help information. + ``` + +#### Executing Test Cases on Windows + +Test cases cannot be built on Windows. You need to run the following command to build test cases on Linux: +``` +./build.sh --product-name {product_name} --build-target make_test +``` + +>**NOTE** +>- **product-name**: specifies the name of the product to be compiled. +>- **build-target**: specifies the test case to build. **make_test** indicates all test cases. You can specify the test cases based on requirements. + +After the build is complete, the test cases are automatically saved in **out/ohos-arm-release/packages/phone/tests**. + +##### Setting Up the Execution Environment +1. On Windows, create the **Test** directory in the test framework and then create the **testcase** directory in the **Test** directory. + +2. Copy **developertest** and **xdevice** from the Linux environment to the **Test** directory on Windows, and copy the test cases to the **testcase** directory. + + >**NOTE** + > + >Port the test framework and test cases from the Linux environment to the Windows environment for subsequent execution. + +3. Modify the **user_config.xml** file. + ```xml + + + false + + + + D:\Test\testcase\tests + + ``` + >**NOTE** + > + >**\** indicates whether to build test cases. **\** indicates the path for searching for test cases. + +#### Executing Test Cases on Linux + +If you directly connect to a Linux host, you can directly run commands to execute test cases. + +##### Mapping the Remote Port +To enable test cases to be executed on a remote Linux server or a Linux VM, map the port to enable communication between the device and the remote server or VM. Configure port mapping as follows: +1. On the HDC server, run the following commands: + ``` + hdc_std kill + hdc_std -m -s 0.0.0.0:8710 + ``` + >**NOTE** + > + >The IP address and port number are default values. + +2. On the HDC client, run the following command: + ``` + hdc_std -s xx.xx.xx.xx:8710 list targets + ``` + >**NOTE** + > + >Enter the IP address of the device to test. + +## Viewing the Test Result + +### Test Report Logs + +After the test cases are executed, the test result will be automatically generated. You can view the detailed test result in the related directory. + +### Test Result +You can obtain the test result in the following directory: +``` +test/developertest/reports/xxxx_xx_xx_xx_xx_xx +``` +>**NOTE** +> +>The folder for test reports is automatically generated. + +The folder contains the following files: +| Type | Description | +| ------------------------------------ | ------------------ | +| result/ | Test cases in standard format.| +| log/plan_log_xxxx_xx_xx_xx_xx_xx.log | Test case logs. | +| summary_report.html | Test report summary. | +| details_report.html | Detailed test report. | + +### Test Framework Logs +``` +reports/platform_log_xxxx_xx_xx_xx_xx_xx.log +``` + +### Latest Test Report +``` +reports/latest +``` diff --git a/en/device-dev/device-test/figures/FAQ-1.PNG b/en/device-dev/device-test/figures/FAQ-1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..bebb315141c4cc3c004b502bb288705c8af9ebd8 Binary files /dev/null and b/en/device-dev/device-test/figures/FAQ-1.PNG differ diff --git a/en/device-dev/device-test/figures/L0-1.PNG b/en/device-dev/device-test/figures/L0-1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..de07672f8c2e4039fb8db712dfbf5bf6bcf9dec1 Binary files /dev/null and b/en/device-dev/device-test/figures/L0-1.PNG differ diff --git a/en/device-dev/device-test/figures/NFS-2.PNG b/en/device-dev/device-test/figures/NFS-2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..f28bc54cac3eb231ce16681cf8f1736823cd2cae Binary files /dev/null and b/en/device-dev/device-test/figures/NFS-2.PNG differ diff --git a/en/device-dev/device-test/figures/en-us_image_0000001154351160.jpg b/en/device-dev/device-test/figures/en-us_image_0000001154351160.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ace4cefd36637675f235df3cd596eca3ed218e6c Binary files /dev/null and b/en/device-dev/device-test/figures/en-us_image_0000001154351160.jpg differ diff --git a/en/device-dev/device-test/figures/en-us_image_0000001200230833.gif b/en/device-dev/device-test/figures/en-us_image_0000001200230833.gif new file mode 100644 index 0000000000000000000000000000000000000000..9cd37267672d3bea422b98d95c413e26df330de8 Binary files /dev/null and b/en/device-dev/device-test/figures/en-us_image_0000001200230833.gif differ diff --git a/en/device-dev/device-test/figures/result-1.PNG b/en/device-dev/device-test/figures/result-1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..64c730feade073029102459e92c01f8ff885f51d Binary files /dev/null and b/en/device-dev/device-test/figures/result-1.PNG differ diff --git a/en/device-dev/device-test/xdevice.md b/en/device-dev/device-test/xdevice.md new file mode 100644 index 0000000000000000000000000000000000000000..0b33efa70e1d06c45673c287a4c0373f1db6f8cc --- /dev/null +++ b/en/device-dev/device-test/xdevice.md @@ -0,0 +1,701 @@ +# xDevice User Guide + + +## Overview + +As an open-source OS, OpenHarmony supports product development in many chip scenarios. To ensure compatibility of the OpenHarmony ecosystem, OpenHarmony provides the [compatibility test service](https://www.openharmony.cn/certification/document/guid). For related products, API tests are required for verification. However, executing a large number of automated test cases requires a scheduling and execution framework that supports capabilities such as generating visual test reports. Therefore, we designed and developed the xDevice test scheduling and execution framework. + +### Introduction + +The xDevice test scheduling and execution framework is a core component of the test infrastructure of OpenHarmony. It provides related services required for scheduling and executing automated test cases, supports scheduling and execution of a large number of automated test cases, as well as supports the generation of visual test reports. The binary package of xDevice will be compiled together with the XTS suite of OpenHarmony. You can obtain the xDevice tool from the XTS archiving path. + +Based on the device type, xDevice mainly tests tasks in the following scenarios: + +- Perform XTS tests for mini-system devices (such as the Hi3861 development board). +- Perform XTS tests for small-system devices (such as the Hi3516 development board). +- Perform XTS tests for standard-system devices (such as the RK3568 development board). + +### Implementation Principles + +The xDevice tool includes the following functional modules: + +- **command**: enables command-based interactions between users and the test platform. It parses and processes user commands. +- **config**: sets test framework configurations and provides different configuration options for the serial port connection and USB connection modes. +- **driver**: functions as a test case executor, which defines main test steps, such as test case distribution, execution, and result collection. +- **report**: parses test results and generates test reports. +- **scheduler**: schedules various test case executors in the test framework. +- **environment**: configures the test framework environment, enabling device discovery and device management. +- **testkit**: provides test tools to implement JSON parsing, network file mounting, etc. +- **log**: records task logs and device logs. + +In addition to the preceding functional modules, the framework depends on user-defined configuration files, which are classified into two types: + +**Test Task Configuration File** + +The test task configuration file provided by the framework is **user_config.xml**. You can modify the configuration file based on your environment information. + +The environment configuration is as follows: + +```xml + + + + + + + + + + + + + + cmd + 115200 + 8 + 1 + 20 + + + + deploy + 115200 + + + + + + + + cmd + 115200 + 8 + 1 + 1 + + + + + + + + + +``` + +Set the test case directory. + +```xml + + + + + + + + + + + + + + + +``` + +Set the resource directory. + +```xml + + + + +``` + +Set the log level. + +```xml + +INFO +``` + +**Test Suite Configuration File** + +The test support suite executed by the device is specified by the test configuration file. + +Each test suite has a test configuration file, which mainly specifies the test support suites (kits) that need to be used. In addition, the setup and teardown operations are supported. + +The following is a configuration file example: + +```json +{ + // Description of the test support suite. + "description": "Configuration for aceceshi Tests", + + // Specify the device for executing the current test support suite. + "environment": { + "type": "device", + "label": "wifiiot" + }, + + // Specify the driver executed by the device. + "driver": { + "type": "OHJSUnitTest", + "test-timeout": "700000", + "bundle-name": "com.open.harmony.acetestfive", + "package-name": "com.open.harmony.acetestfive", + "shell-timeout": "700000" + }, + // The kit is mainly used to support test execution activities, including the setup operation before the test and the teardown operation after the test. + "kits": [ + { + "type": "ShellKit", + "run-command": [ + "remount", + "mkdir /data/data/resource", + "chmod -R 777 /data/data/resource", + "settings put secure adb_install_need_confirm 0" + ], + "teardown-command": [ + "remount", + "rm -rf /data/data/resource" + ] + }, + ] +} +``` + +### Test Commands + +Test commands can be classified into three groups: **help**, **list**, and **run**. Among them, **run** commands are most commonly used in the instruction sequence. + +------ + +You can run **help** commands to obtain help information about the test framework commands. + +```text +help: + use help to get information. +usage: + run: Display a list of supported run command. + list: Display a list of supported device and task record. +Examples: + help run + help list +``` + +**NOTE** + +**help run**: displays the description of **run** commands. + +**help list**: displays the description of **list** commands. + +------ + +You can run **list** commands to display device information and related task information. + +```text +list: + This command is used to display device list and task record. +usage: + list + list history + list +Introduction: + list: display device list + list history: display history record of a serial of tasks + list : display history record about task what contains specific id +Examples: + list + list history + list 6e****90 +``` + + **NOTE** + + **list**: displays device information. + + **list history**: displays historical task information. + + **list *\***: displays historical information about tasks with specified IDs. + +------ + +Run the **run** commands to execute test tasks. + +```text +run: + This command is used to execute the selected testcases. + It includes a series of processes such as use case compilation, execution, and result collection. +usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE + [TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN] + [-rp REPORT_PATH [REPORT_PATH ...]] + [-respath RESOURCE_PATH [RESOURCE_PATH ...]] + [-tcpath TESTCASES_PATH [TESTCASES_PATH ...]] + [-ta TESTARGS [TESTARGS ...]] [-pt] + [-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]] + [-e EXECTYPE] [-t [TESTTYPE [TESTTYPE ...]]] + [-td TESTDRIVER] [-tl TESTLEVEL] [-bv BUILD_VARIANT] + [-cov COVERAGE] [--retry RETRY] [--session SESSION] + [--dryrun] [--reboot-per-module] [--check-device] + [--repeat REPEAT] + action task +Specify tests to run. + positional arguments: + action Specify action + task Specify task name,such as "ssts", "acts", "hits" +``` + +The table below describes how to use **run** commands. + +| xDevice Command | Function | Example | +| :----------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| run xts | Runs all XTS modules of the specified type, for example, **acts**, **hits**, and **ssys**. | run acts | +| run -l xxx | Runs specified module test suites. Multiple module test suites are separated using semicolons (;). | run -l ActsWifiServiceTest;ActsLwipTest | +| run -sn | Specifies the SNs of the executed devices. Multiple SNs are separated using semicolons (;). | run acts -sn 10.117.183.37:17001
run acts -sn 88Y02******57723;VEG02******16642 | +| run -rp | Specifies the report generation path. By default, subdirectories will be created under **reports** of the work directory using the timestamp or task ID.| run acts -rp /suites/hits/xdevice_reports/2020.09.28-14.21.26 | +| run -respath | Specifies the test resource path. The default value is **resource**. | run acts -respath /cloud/zidane/xts/release/suites/resource | +| run -ta | Specifies module running parameters. You can specify test cases in the running module. Multiple cases are separated using commas (,). Currently, the JS driver test suite is supported.| run acts -ta class:ohos.hardware.soundtrigger.SoundTriggerTest#testKeyphraseParcelUnparcel_noUsers | +| run --retry | Executes failed test cases of the previous task to generate a new test report. | run –retryrun --retry --session 2020-10-30-17-15-11 (task directory name)| + +### Test Report + +After the test framework executes the **run** commands, the console outputs the corresponding logs, and the execution report is generated. The report is generated in the path specified by the **-rp** parameter if set. If the parameter is not set, the report will be generated in the default directory. + +```text +Structure of the report directory (the default or the specified one) + ├── result (test case execution results of the module) + │ ├── *Module name*.xml + │ ├── ... ... + │ + ├── log (running logs of devices and tasks) + │ ├── *Device 1*.log + │ ├── ... ... + │ ├── **.log + ├── summary_report.xml (task summary report) + ├── summary_report.html (task summary visual report) + ├── details_report.html (case execution visual report) + ├── failures_report.html (failed case visual report, which will not be generated if no case fails) + ├── summary.ini (Records information such as the used device, start time, and end time.) + ├── task_info.record (Records executed commands and list of failed cases.) + ├── xxxx.zip (compression file generated by compressing the preceding files) + ├── summary_report.hash (file obtained by encrypting the compression file using SHA-256) + └── ... ... +``` + + +## Environment Preparations + + +### Environment Requirements + +- Python version: 3.7 or later +- pyserial: 3.3 or later +- paramiko: 2.7.1 or later +- rsa: 4.0 or later + + +### Installing the xDevice + +- Install the basic framework of xDevice. + + 1. Go to the root directory of xDevice. + + ```bash + cd testfwk_xdevice + ``` + + 2. Open the console and run the following command: + + ```bash + python setup.py install + ``` + +- Install the OpenHarmony driver plugin **ohos**. + + 1. Go to the **plugin/ohos** directory. + + ```bash + cd testfwk_xdevice/plugin/ohos + ``` + + 2. Open the console and run the following command as the current user: + + ```bash + python setup.py install + ``` + +### Verifying the Environment + +Check whether xDevice is installed successfully. + +1. Go to the root directory of xDevice. + + ```bash + cd testfwk_xdevice + ``` + +2. Open the console and run the following command: + + ```bash + python -m pip list + ``` + +3. Check whether the **xdevice** and **xdevice-ohos** libraries are successfully installed. + + ```text + xdevice 0.0.0 + xdevice-ohos 0.0.0 + ``` + +Check whether xDevice runs properly. + +1. Go to the root directory of xDevice. + + ```bash + cd testfwk_xdevice + ``` + +2. Open the console and run the following command: + + ```bash + python -m xdevice + ``` + +3. Check whether the following information is displayed on the console: + + ```text + [2022-10-13 15:43:31,284] [30076] [Main] [INFO] [*************** xDevice Test Framework 2.11.0.1091 Starting ***************] + [2022-10-13 15:43:31,286] [30076] [ManagerLite] [WARNING] [wifiiot local com cannot be empty, please check] + [2022-10-13 15:43:31,286] [30076] [ManagerLite] [WARNING] [ipcamera local com cannot be empty, please check] + [2022-10-13 15:43:31,287] [30076] [ManagerLite] [WARNING] [device com or ip cannot be empty, please check] + >>> + ``` + + +## Mini-System Device XTS Test Guide (wifiiot) + +1. Identify the serial port usage and modify the **user_config.xml** file in the root directory. + + The COM port whose **type** is **cmd** corresponds to the AT command serial port on the board. The port is used to send commands to the device. In the example, the **ChA(COM20)** serial port is used. + + The COM port whose **type** is **deploy** corresponds to the log output serial port on the board. The port is used to burn the image and print logs. In the example, the **ChB(COM18)** serial port is used. + + If the AT command serial port is the same as the log output serial port, the serial ports can be set to the same port. That is, in the **user_config** file, the COM port whose **type** is **cmd** and the COM port whose **type** is **deploy** can be set to the same port, for example, **COM18**. + + ![L0-1](figures/L0-1.PNG) + + The following is an example of the modified **user_config.xml** file: + + ```xml + + + + + com20 + cmd + 115200 + 8 + 1 + 20 + + + com18 + deploy + 115200 + + + + + + + + + + + + + + + + + + DEBUG + + ``` + +2. Create a **testcase** directory in the root directory of xDevice to store test suite files. XTS test suites are obtained from the daily builds of the system. + + Daily builds: http://ci.openharmony.cn/dailys/dailybuilds + + The following is an example of the test suite configuration file in JSON format: + + ```json + { + "description": "Config for ActsAllTest test cases", + "environment": [ + { + "type": "device", + "label": "wifiiot" + } + ], + "kits": [ + { + "type": "DeployKit", + "timeout": "20000", + "burn_file": "acts/Hi3861_wifiiot_app_allinone.bin" + } + ], + "driver": { + "type": "CTestLite" + } + } + ``` + +3. Execute test cases. + + Go to the root directory of xDevice, open the xDevice console, and run the following command: + + ```bash + python -m xdevice + ``` + + Run the test suite command. + + ```text + run -l ActsAllTest + ``` + + The command output is as follows. + + ![result-1](figures/result-1.PNG) + +## Small-System Device XTS Test Guide (ipcamera) + +1. Identify the serial port usage. + + The COM port whose **type** is **cmd** corresponds to the AT command serial port on the board. The port is used to send commands to the device. In the example, the **ChA(COM20)** serial port is used. + + L0-1 + + IP camera devices have two connection modes. One is to connect through the local serial port, and the other is to connect through the IP address of the local area network. + +2. Configure the NFS server. + + There are two NFS mounting modes. One is to mount through the remote PC, and the other is to mount through the local area network. + + To configure the NFS service on the local area network, perform the following steps: + + 1. Download and install the NFS server. Download address: https://www.hanewin.net/nfs-e.htm + + 2. Configure output and edit the output table file. + + Add an NFS sharing path, for example, **D:\HS\NFS_Share_File -public –alldirs**. Note that the FTP IP address 192.168.1.10 is the IP address of the development board. + + + + 3. Stop the NFS server and restart the NFS server to make the added sharing path take effect. + + 4. Find the mapped network port of the IP camera device on the PC, and manually set the IP address to 192.168.1.11 on the PC. + +3. Modify the **user_config.xml** file in the root directory. The following is an example: + + ```xml + + + + + com20 + cmd + 115200 + 8 + 1 + 1 + + + + 10.176.49.47 + 10003 + + + + + + 10.176.48.202 + 1022 + /data/data/local/ + root + xxx + true + + + 192.168.1.11 + 2049 + D:\test + false + + + + + + DEBUG + + ``` + +4. Create a **testcase** directory in the root directory of xDevice to store test suite files. XTS test suites are obtained from the daily builds of the system. + + Daily builds: http://ci.openharmony.cn/dailys/dailybuilds + + The following is an example of the test suite configuration file in JSON format: + + ```json + { + "description": "Config for kernel test cases", + "environment": [ + { + "type": "device", + "label": "ipcamera" + } + ], + "kits": [ + { + "type": "MountKit", + "server": "NfsServer", + "mount": [ + { + "source": "testcases/kernel", + "target": "/test_root/kernel" + } + ] + } + ], + "driver": { + "type": "CppTestLite", + "excute": "/test_root/kernel/ActsKernelIPCTest.bin" + } + } + ``` + +5. Execute test cases. + + Go to the root directory of xDevice, open the xDevice console, and run the following command: + + ```bash + python -m xdevice + ``` + + Run the test suite command. + + ```text + run -l kernel + ``` + + The command output is as follows. + + ![result-1](figures/result-1.PNG) + +## Standard-System Device XTS Test Guide (RK3568) + +1. Configure the HDC tool, and download the latest **ohos_sdk** from daily builds. + + Daily builds: http://ci.openharmony.cn/dailys/dailybuilds + + After downloading the tool, configure HDC into the environment variables on the PC. To do so, right-click the **Computer** or **My Computer** desktop icon and select **Properties**. Choose **Advanced system settings**. Select the **Advanced** tab, and click **Environment Variables**. In the **Environment Variables** dialog box, select the path of the environment variable. + +2. Run the following command to check whether the device is properly connected: + + ```bash + hdc_std list targets + ``` + +3. Modify the **user_config.xml** file. The following is an example: + + ```xml + + + + + + xxx;xxx + + + + + + + + + DEBUG + + ``` + +4. Create a **testcase** directory in the root directory of xDevice to store test suite files. XTS test suites are obtained from the daily builds of the system. + + Daily builds: http://ci.openharmony.cn/dailys/dailybuilds + + The following is an example of the test suite configuration file in JSON format: + + ```json + { + "description": "Configuration for hjunit demo Tests", + "driver": { + "type": "OHJSUnitTest", + "test-timeout": "180000", + "bundle-name": "ohos.acts.bundle.stage.test", + "module-name": "phone", + "shell-timeout": "600000", + "testcase-timeout": 70000 + }, + "kits": [ + { + "test-file-name": [ + "ActBmsStageEtsTest.hap" + ], + "type": "AppInstallKit", + "cleanup-apps": true + }, + { + "type": "ShellKit", + "teardown-command":[ + "bm uninstall -n ohos.acts.bundle.stage.test" + ] + } + ] + } + ``` + +5. Execute test cases. + + Go to the root directory of xDevice, open the xDevice console, and run the following command: + + ```bash + python -m xdevice + ``` + + Run the test suite command. + + ```text + run -l ActBmsStageEtsTest + ``` + + The command output is as follows. + + ![result-1](figures/result-1.PNG) + +## FAQs + +### The **hdc list targets** command can find a device, but xDevice cannot identify the device. + +**Issue Description** + +The following error information is displayed. + +![FAQ-1](figures/FAQ-1.PNG) + +**Possible Causes** + +The **HDC_SERVER_PORT** variable has been set and the HDC port has been modified. By default, xDevice uses port **8710**. If the port has been modified, the xDevice framework cannot identify the device. + +**Solution** + +Check whether the **HDC_SERVER_PROT** variable is set. If yes, change the port number to **8710** and restart xDevice. diff --git a/en/device-dev/device-test/xts.md b/en/device-dev/device-test/xts.md new file mode 100644 index 0000000000000000000000000000000000000000..9fbfd198d14529490b8960a17b6d55a9d798b639 --- /dev/null +++ b/en/device-dev/device-test/xts.md @@ -0,0 +1,506 @@ +# XTS Test Case Development Guide + + +## Introduction + +The X test suite (XTS) subsystem contains a set of OpenHarmony compatibility test suites, including the currently supported application compatibility test suite (ACTS) and the device compatibility test suite (DCTS) that will be supported in the future. + +This subsystem contains the ACTS and **tools** software package. + +- The **acts** directory stores the source code and configuration files of ACTS test cases. The ACTS helps device vendors detect the software incompatibility as early as possible and ensures that the software is compatible with OpenHarmony during the entire development process. + +- The **tools** software package stores the test case development framework related to **acts**. + + +## System Types + +The following system types are supported: + +- Mini system + The mini system fits into the devices that come with MCU processors, such as Arm Cortex-M and 32-bit RISC-V, and memory greater than or equal to 128 KiB. This system provides a variety of lightweight network protocols, a lightweight graphics framework, and a wide range of read/write components with the Internet of Things (IoT) bus. Typical products include connection modules, sensors, and wearables for smart home. + +- Small system + The small system fits into the devices that come with application processors, such as Arm Cortex-A, and memory greater than or equal to 1 MiB. This system provides higher security capabilities, a standard graphics framework, and video encoding and decoding capabilities. Typical products include smart home IP cameras, electronic cat eyes, and routers, and event data recorders (EDRs) for easy travel. + +- Standard system + The standard system fits into the devices that come with application processors, such as Arm Cortex-A, and memory greater than or equal to 128 MiB. This system provides a complete application framework supporting enhanced interaction, 3D GPU, hardware composer, diverse components, and rich animations. The standard system applies to high-end refrigerator displays. + + +## Contents + + +``` +/test/xts +├── acts # Test code +│ └── subsystem # Source code of subsystem test cases for the standard system +│ └── subsystem_lite # Source code of subsystems test cases for mini and small systems +│ └── BUILD.gn # Build configuration of test cases for the standard system +│ └── build_lite +│ └── BUILD.gn # Build configuration of test cases for mini and small systems +└── tools # Test tool code +``` + + +## Constraints + +Test cases for the mini system must be developed based on C, and those for the small system must be developed based on C++. + + +## How to Use + +**Table 1** Case levels + +| Level| Basic Definition| Test Scope| +| -------- | -------- | -------- | +| Level0 | Smoke| Verifies basic functionalities of key features and basic DFX attributes with the most common input. The pass result indicates that the features are runnable.| +| Level1 | Basic| Verifies basic functionalities of key features and basic DFX attributes with common input. The pass result indicates that the features are testable.| +| Level2 | Major| Verifies basic functionalities of key features and basic DFX attributes with common input and errors. The pass result indicates that the features are functional and ready for beta testing.| +| Level3 | Minor| Verifies functionalities of all key features, and all DFX attributes with common and uncommon input combinations or normal and abnormal preset conditions.| +| Level4 | Rare| Verifies functionalities of key features under extremely abnormal presets and uncommon input combinations.| + +**Table 2** Case scales + +| Case Scale| Test Object| Test Environment| +| -------- | -------- | -------- | +| LargeTest | Service functionalities, all-scenario features, and mechanical power environment (MPE) and scenario-level DFX| Devices close to real devices| +| MediumTest | Modules, subsystem functionalities after module integration, and DFX| Single device that is actually used. You can perform message simulation, but do not mock functions.| +| SmallTest | Modules, classes, and functions| Local PC. Use a large number of mocks to replace dependencies with other modules.| + +**Table 3** Test types + +| Test Type| Definition| +| -------- | -------- | +| Function | Tests the correctness of both service and platform functionalities provided by the tested object for end users or developers.| +| Performance | Tests the processing capability of the tested object under specific preset conditions and load models. The processing capability is measured by the service volume that can be processed in a unit time, for example, call per second, frame per second, or event processing volume per second.| +| Power | Tests the power consumption of the tested object in a certain period of time under specific preset conditions and load models.| +| Reliability | Tests the service performance of the tested object under common and uncommon input conditions, or specified service volume pressure and long-term continuous running pressure. The test covers stability, pressure handling, fault injection, and Monkey test items.| +| Security | Tests the capability of defending against security threats, including but not limited to unauthorized access, use, disclosure, damage, modification, and destruction, to ensure information confidentiality, integrity, and availability. Tests the privacy protection capability to ensure that the collection, use, retention, disclosure, and disposal of users' private data comply with laws and regulations. Tests the compliance with various security specifications, such as security design, security requirements, and security certification of the Ministry of Industry and Information Technology (MIIT).| +| Global | Tests the internationalized data and localization capabilities of the tested object, including multi-language display, various input/output habits, time formats, and regional features, such as currency, time, and culture taboos.| +| Compatibility | Tests backward compatibility of an application with its own data, the forward and backward compatibility with the system, and the compatibility with different user data, such as audio file content of the player and smart SMS messages. Tests system backward compatibility with its own data and the compatibility of common applications in the ecosystem. Tests software compatibility with related hardware.| +| User | Tests user experience of the object in real user scenarios. All conclusions and comments should come from the users, which are all subjective evaluation in this case.| +| Standard | Tests the compliance with industry and company-specific standards, protocols, and specifications. The standards here do not include any security standards that should be classified into the security test.| +| Safety | Tests the safety property of the tested object to avoid possible hazards to personal safety, health, and the object itself.| +| Resilience | Tests the resilience property of the tested object to ensure that it can withstand and maintain the defined running status (including downgrading) when being attacked, and recover from and adapt defense to the attacks to approach mission assurance.| + + +## Test Case Development Guide + +You should select the appropriate programming language and your target test framework to develop test cases. + +**Table 4** Test frameworks and test case languages for different systems + +| System| Test Framework| Language| +| -------- | -------- | -------- | +| Mini system| hctest | c | +| Small system| hcpptest | c++ | +| Standard system| HJSUnit and HCPPTest| JavaScript and C++| + +### C-based Test Case Development and Compilation (for the Mini System) + +**Developing test cases for the mini system** + +The HCTest framework is used to support test cases developed with the C language. HCTest is enhanced and adapted based on the open-source test framework Unity. + +1. Access the **test/xts/acts** repository where the test cases will be stored. + + ``` + ├── acts + │ └──subsystem_lite + │ │ └── module_hal + │ │ │ └── BUILD.gn + │ │ │ └── src + │ └──build_lite + │ │ └── BUILD.gn + ``` + +2. Write the test case in the **src** directory. + + 1. Import the test framework header file. + + ``` + #include "hctest.h" + ``` + + 2. Use the **LITE_TEST_SUIT** macro to define names of the subsystem, module, and test suite. + + + ``` + /** + * @brief register a test suite named "IntTestSuite" + * @param test subsystem name + * @param example module name + * @param IntTestSuite test suite name + */ + LITE_TEST_SUIT(test, example, IntTestSuite); + ``` + + 3. Define SetUp and TearDown. + + Format: Test suite name+SetUp, Test suite name+TearDown. + + The SetUp and TearDown functions must exist, but function bodies can be empty. + + 4. Use the **LITE_TEST_CASE** macro to write the test case. + + Three parameters are involved: test suite name, test case name, and test case properties (including type, granularity, and level). + ``` + LITE_TEST_CASE(IntTestSuite, TestCase001, Function | MediumTest | Level1) + { + //do something + }; + ``` + 5. Use the **RUN_TEST_SUITE** macro to register the test suite. + + + ``` + RUN_TEST_SUITE(IntTestSuite); + ``` + +3. Create a configuration file (**BUILD.gn**) of the test module. + Create a **BUILD.gn** (example) build file in each test module directory. Specify the name of the built static library and its dependent header file and library in the build file. The format is as follows: + + + ``` + import("//test/xts/tools/lite/build/suite_lite.gni") + hctest_suite("ActsDemoTest") { + suite_name = "acts" + sources = [ + "src/test_demo.c", + ] + include_dirs = [ ] + cflags = [ "-Wno-error" ] + } + ``` + +4. Add build options to the **BUILD.gn** file in the **acts** directory. + You need to add the test module to the **test/xts/acts/build_lite/BUILD.gn** script in the **acts** directory. + + + ``` + lite_component("acts") { + ... + if(board_name == "liteos_m") { + features += [ + ... + "//xts/acts/subsystem_lite/module_hal:ActsDemoTest" + ] + } + } + ``` + +5. Run build commands. + Test suites are built along with the version build. The ACTS is built together with the debug version. + + > **NOTE** + > The ACTS build middleware is a static library, which will be linked to the image. + +### C-based Test Case Execution (for the Mini System) + +**Executing test cases for the mini system** + +Burn the image into the development board. + +**Executing the test** + +1. Use a serial port tool to log in to the development board and save information about the serial port. + +2. Restart the device and view serial port logs. + +**Analyzing the test result** + +View the serial port logs, whose format is as follows: + +The log for each test suite starts with **Start to run test suite:** and ends with **xx Tests xx Failures xx Ignored**. + + +### C++-based Test Case Development and Compilation (for Standard and Small Systems) + +**Developing test cases for small-system devices** (For examples of the standard system, go to the **global/i18n_standard** directory.) + +The HCPPTest framework is enhanced and adapted based on the open-source framework Googletest. + +1. Access the **test/xts/acts** repository where the test cases will be stored. + + ``` + ├── acts + │ └──subsystem_lite + │ │ └── module_posix + │ │ │ └── BUILD.gn + │ │ │ └── src + │ └──build_lite + │ │ └── BUILD.gn + ``` + +2. Write the test case in the **src** directory. + 1. Import the test framework header file. + + The following statement includes **gtest.h**: + + + ``` + #include "gtest/gtest.h" + ``` + + 2. Define SetUp and TearDown. + + + ``` + using namespace std; + using namespace testing::ext; + class TestSuite: public testing::Test { + protected: + // Preset action of the test suite, which is executed before the first test case + static void SetUpTestCase(void){ + } + // Test suite cleanup action, which is executed after the last test case + static void TearDownTestCase(void){ + } + // Preset action of the test case + virtual void SetUp() + { + } + // Cleanup action of the test case + virtual void TearDown() + { + } + }; + ``` + + 3. Use the **HWTEST** or **HWTEST_F** macro to write the test case. + + **HWTEST**: definition of common test cases, including the test suite name, test case name, and case annotation. + + **HWTEST_F**: definition of SetUp and TearDown test cases, including the test suite name, test case name, and case annotation. + + Three parameters are involved: test suite name, test case name, and test case properties (including type, granularity, and level). + + + ``` + HWTEST_F(TestSuite, TestCase_0001, Function | MediumTest | Level1) { + // do something + } + ``` + +3. Create a configuration file (**BUILD.gn**) of the test module. + Create a **BUILD.gn** build file in each test module directory. Specify the name of the built static library and its dependent header file and library in the build file. Each test module is independently built into a **.bin** executable file, which can be directly pushed to the development board for testing. + + Example: + + ``` + import("//test/xts/tools/lite/build/suite_lite.gni") + hcpptest_suite("ActsDemoTest") { + suite_name = "acts" + sources = [ + "src/TestDemo.cpp" + ] + + include_dirs = [ + "src", + ... + ] + deps = [ + ... + ] + cflags = [ "-Wno-error" ] + } + + ``` + +4. Add build options to the **BUILD.gn** file in the **acts** directory. + Add the test module to the **test/xts/acts/build_lite/BUILD.gn** script in the **acts** directory. + + + ``` + lite_component("acts") { + ... + else if(board_name == "liteos_a") { + features += [ + ... + "//xts/acts/subsystem_lite/module_posix:ActsDemoTest" + ] + } + } + ``` + +5. Run build commands. + Test suites are built along with the version build. The ACTS is built together with the debug version. + > **NOTE** + > + > The ACTS for the small system is independently built to an executable file (.bin) and archived in the **suites\acts** directory of the build result. + + +### C++-based Test Case Execution (for Standard and Small Systems) + +**Executing test cases for the small system** + +Currently, test cases are shared by the NFS and mounted to the development board for execution. + +**Setting up the environment** + +1. Use a network cable or wireless network to connect the development board to your PC. + +2. Configure the IP address, subnet mask, and gateway for the development board. Ensure that the development board and the PC are on the same network segment. + +3. Install and register the NFS server on the PC and start the NFS service. + +4. Run the **mount** command for the development board to ensure that the development board can access NFS shared files on the PC. + Format: **mount** *NFS server IP address***:/***NFS shared directory* **/***development board directory* **nfs** + + Example: + + ``` + mount 192.168.1.10:/nfs /nfs nfs + ``` + +**Executing test cases** + +Execute **ActsDemoTest.bin** to trigger test case execution, and analyze serial port logs generated after the execution is complete. + +### JavaScript-based Test Case Development (for the Standard System) + +The HJSUnit framework is used to support automated tests of OpenHarmony applications that are developed using the JavaScript language based on the JS application framework. + +**Basic syntax of test cases** + +The test cases are developed with the JavaScript language and must meet the programming specifications of the language. + +**Table 7** Basic syntax of test cases + +| Syntax| Description| Requirement| +| -------- | -------- | -------- | +| beforeAll | Presets a test-suite-level action executed only once before all test cases are executed. You can pass the action function as the only parameter.| Optional| +| afterAll | Presets a test-suite-level clear action executed only once after all test cases are executed. You can pass the clear function as the only parameter.| Optional| +| beforeEach | Presets a test-case-level action executed before each test case is executed. The number of execution times is the same as the number of test cases defined by **it**. You can pass the action function as the only parameter.| Optional| +| afterEach | Presets a test-case-level clear action executed after each test case is executed. The number of execution times is the same as the number of test cases defined by **it**. You can pass the clear function as the only parameter.| Optional| +| describe | Defines a test suite. You can pass two parameters: test suite name and test suite function. **describe** supports embedding. **beforeAll**, **beforeEach**, **afterEach**, and **afterAll** can be defined in each **describe**.| Mandatory| +| it | Defines a test case. You can pass three parameters: test case name, filter parameter, and test case function.
**NOTE**
**Filter parameter**: The filter parameter is a 32-bit parameter of the **Int** type. **1** of bit 0 indicates not to filter. **1** of bits 0-10 indicates the test case type. **1** of bits 16-18 indicates the test case scale. **1** of bits 24-28 indicates the test level.
**Test case type**: Bits 0-10 indicate the following respectively: FUNCTION test, PERFORMANCE test, POWER test, RELIABILITY test, SECURITY test, GLOBAL test, COMPATIBILITY test, USER test, STANDARD test, SAFETY test, and RESILIENCE test.
**Test case scale**: Bits 16-18 indicate the following respectively: SMALL test, MEDIUM test, and LARGE test.
**Test level**: Bits 24-28 indicate the following respectively: LEVEL0-0 test, LEVEL1-1 test, LEVEL2-2 test, LEVEL3-3 test, and LEVEL4-4 test.| Mandatory| + +Use the standard syntax of Jasmine to write test cases. The ES6 specification is supported. + +1. Store the test cases in the **entry/src/main/js/test** directory, whose structure is as follows: + + ``` + ├── BUILD.gn + │ └──entry + │ │ └──src + │ │ │ └──main + │ │ │ │ └──js + │ │ │ │ │ └──default + │ │ │ │ │ │ └──pages + │ │ │ │ │ │ │ └──index + │ │ │ │ │ │ │ │ └──index.js # Entry file + │ │ │ │ │ └──test # Test code + │ │ │ └── resources # HAP resources + │ │ │ └── config.json # HAP configuration file + ``` + +2. Start the JS test framework and load test cases. The following is an example for **index.js**: + + ``` + // Start the JS test framework and load test cases. + import {Core, ExpectExtend} from 'deccjsunit/index' + + export default { + data: { + title: "" + }, + onInit() { + this.title = this.$t('strings.world'); + }, + onShow() { + console.info('onShow finish') + const core = Core.getInstance() + const expectExtend = new ExpectExtend({ + 'id': 'extend' + }) + core.addService('expect', expectExtend) + core.init() + const configService = core.getDefaultService('config') + configService.setConfig(this) + require('../../../test/List.test') + core.execute() + }, + onReady() { + }, + } + ``` + +3. Write a unit test case by referring to the following example: + + ``` + // Use HJSUnit to perform the unit test. + describe('appInfoTest', function () { + it('app_info_test_001', 0, function () { + var info = app.getInfo() + expect(info.versionName).assertEqual('1.0') + expect(info.versionCode).assertEqual('3') + }) + }) + ``` + +### JavaScript-based Test Case Packaging (for the Standard System) + +For details about HAP package compilation, see [JS application development guide for the standard system](https://developer.harmonyos.com/en/docs/documentation/doc-guides/build_overview-0000001055075201). + + +## Full Compilation (for the Standard System) + +1. Perform full compilation. + Command: + + + ``` + ./build.sh suite=acts system_size=standard + ``` + + Test case output directory: **out/release/suites/acts/testcases** + + Test framework and case output directory: **out/release/suites/acts** (The test suite execution framework is compiled during case compilation.) + + +## Full Test Case Execution (for Small and Standard Systems) + +**Setting up a test environment** + +Install Python 3.7 or a later version on a Windows environment and ensure that the Windows environment is properly connected to the test device. + +**Test execution directory** (corresponding to the **out/release/suites/acts** directory generated during compilation) + +``` +├── testcase # Directory for storing test suite files +│ └──xxx.hap # HAP file executed by the test suite +│ └──xxx.json # Execution configuration file of the test suite +├── tools # Test framework tool directory +├── run.bat # File for starting the test suite on the Windows platform +├── report # Directory for storing the test reports +``` + +**Executing test cases** + +1. On the Windows environment, locate the directory in which the test cases are stored (**out/release/suites/acts**, copied from the Linux server), go to the directory in the Windows command line interface (CLI), and run **acts\run.bat**. + +2. Enter the command for executing the test case. + - Execute all test cases. + + ``` + run acts + ``` + + **Figure 1** Running process + + ![en-us_image_0000001200230833](figures/en-us_image_0000001200230833.gif) + + - Execute the test cases of a module (view specific module information in **\acts\testcases\**). + + ``` + run –l ActsSamgrTest + ``` + + **Figure 2** Viewing the running command + + ![en-us_image_0000001154351160](figures/en-us_image_0000001154351160.jpg) + + Wait until the execution is complete. + +3. View test reports. + Go to **acts\reports\**, obtain the current execution record, and open **summary_report.html** to view the test report. diff --git a/en/device-dev/driver/Readme-EN.md b/en/device-dev/driver/Readme-EN.md index 82740883afa4d57a48d30a073ea35ad63c4f7281..37a5a507785590d211014563eaa16dc9e24cea70 100644 --- a/en/device-dev/driver/Readme-EN.md +++ b/en/device-dev/driver/Readme-EN.md @@ -4,6 +4,7 @@ - HDF - [HDF Overview](driver-hdf-overview.md) - [Driver Development](driver-hdf-development.md) + - [Driver Loading](driver-hdf-load.md) - [Driver Service Management](driver-hdf-servicemanage.md) - [Driver Message Mechanism Management](driver-hdf-message-management.md) - [Driver Configuration Management](driver-hdf-manage.md) diff --git a/en/device-dev/driver/driver-hdf-development.md b/en/device-dev/driver/driver-hdf-development.md index 306da57b83bbf185eab2b4365d7585dd06533991..e251299eb09c9b7f04fa1592af095775ed972320 100644 --- a/en/device-dev/driver/driver-hdf-development.md +++ b/en/device-dev/driver/driver-hdf-development.md @@ -3,9 +3,9 @@ ## Driver Model -The Hardware Driver Foundation (HDF) is designed upon a component-based driver model. This model enables refined driver management and streamlines driver development and deployment. In the HDF, the same type of device drivers are placed in a host. You can develop and deploy the drivers separately. One driver can have multiple nodes. +The Hardware Driver Foundation (HDF) is designed based on a modular driver model to enable refined driver management and streamline driver development and deployment. The HDF allows the same type of device drivers to be placed in a host. The host manages the start and loading of a group of devices. You can deploy dependent drivers to the same host, and deploy independent drivers to different hosts. -The figure below shows the HDF driver model. +The figure below shows the HDF driver model. A device refers to a physical device. A DeviceNode is a component of a device. A device has at least one DeviceNode. Each DeviceNode can publish a device service. Each DevicdNode has a unique driver to interact with the hardware. **Figure 1** HDF driver model @@ -14,17 +14,15 @@ The figure below shows the HDF driver model. ## How to Develop -The HDF-based driver development process involves driver implementation, build, and configuration. The procedure is as follows: +The HDF-based driver development involves driver implementation, write of the driver compilation script, and driver configuration. The procedure is as follows: 1. Implement a driver. Write the driver code and register the driver entry with the HDF. - - Write the driver service code. + - Write the driver service code.
The following is an example: - The following is an example: - - ``` + ```c #include "hdf_device_desc.h" // Header file that defines the driver development APIs provided by the HDF. #include "hdf_log.h" // Header file that defines the log APIs provided by the HDF. @@ -34,14 +32,14 @@ The HDF-based driver development process involves driver implementation, build, int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) { HDF_LOGD("Sample driver bind success"); - return 0; + return HDF_SUCCESS; } // Initialize the driver service. int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) { HDF_LOGD("Sample driver Init success"); - return 0; + return HDF_SUCCESS; } // Release the driver resources. @@ -51,10 +49,9 @@ The HDF-based driver development process involves driver implementation, build, return; } ``` - - Register the driver entry with the HDF. - ``` + ```c // Define a driver entry object. It must be a global variable of the HdfDriverEntry type (defined in hdf_device_desc.h). struct HdfDriverEntry g_sampleDriverEntry = { .moduleVersion = 1, @@ -64,36 +61,36 @@ The HDF-based driver development process involves driver implementation, build, .Release = HdfSampleDriverRelease, }; - // Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls the Bind() function and then the Init() function. If the Init() function fails to be called, the HDF will call Release() to release driver resources and exit the driver model. + // Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls Bind() and then Init(). If Init() fails to be called, the HDF will call Release() to release driver resources and exit the driver model. HDF_INIT(g_sampleDriverEntry); ``` -2. Build the driver. +2. Write the driver compilation script. - LiteOS - Modify **Makefile** and **BUILD.gn** files. + Modify **makefile** and **BUILD.gn** files. - **Makefile**: - Use the **Makefile** template provided by the HDF to compile the driver code. + Use the **makefile** template provided by the HDF to compile the driver code. - ``` - include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk # (Mandatory) Import the HDF predefined content. + ```c + include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # (Mandatory) Import the HDF predefined content. MODULE_NAME := # File to be generated. LOCAL_INCLUDE: = # Directory of the driver header files. LOCAL_SRCS : = # Source code files of the driver. LOCAL_CFLAGS : = # Custom build options. - include $(HDF_DRIVER) # Import the Makefile template to complete the build. + include $(HDF_DRIVER) # Import the makefile template to complete the build. ``` - Add the path of the generated file to **hdf_lite.mk** in the **drivers/adapter/khdf/liteos** directory to link the file to the kernel image. The following is an example: + Add the path of the generated file to **hdf_lite.mk** in the **drivers/hdf_core/adapter/khdf/liteos** directory to link the file to the kernel image.
The following is an example: - ``` + ```c LITEOS_BASELIB += -lxxx # Static library generated by the link. - LIB_SUBDIRS += # Directory in which Makefile is located. + LIB_SUBDIRS += # Directory in which makefile is located. ``` - **BUILD.gn**: @@ -101,9 +98,9 @@ The HDF-based driver development process involves driver implementation, build, Add **BUILD.gn**. The content of **BUILD.gn** is as follows: - ``` + ```c import("//build/lite/config/component/lite_component.gni") - import("//drivers/adapter/khdf/liteos/hdf.gni") + import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni") module_switch = defined(LOSCFG_DRIVERS_HDF_xxx) module_name = "xxx" hdf_driver(module_name) { @@ -114,42 +111,42 @@ The HDF-based driver development process involves driver implementation, build, } config("public") { # Define the head file configuration of the dependencies. include_dirs = [ - "xxx/xxx/xxx", # Directory of dependency header files. + "xxx/xxx/xxx", # Directory of dependency header files. ] } ``` - Add the **BUILD.gn** directory to **/drivers/adapter/khdf/liteos/BUILD.gn**. + Add the **BUILD.gn** directory to **/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**. - ``` + ```c group("liteos") { public_deps = [ ":$module_name" ] deps = [ - "xxx/xxx", # Directory where the BUILD.gn of the driver is located. It is a relative path to /drivers/adapter/khdf/liteos. + "xxx/xxx", # Directory where the BUILD.gn of the driver is located. It is a relative path to /drivers/hdf_core/adapter/khdf/liteos. ] } ``` - Linux - To define the driver control macro, add the **Kconfig** file to the driver directory **xxx** and add the path of the **Kconfig** file to **drivers/adapter/khdf/linux/Kconfig**. + To define the driver control macro, add the **Kconfig** file to the driver directory **xxx** and add the path of the **Kconfig** file to **drivers/hdf_core/adapter/khdf/linux/Kconfig**. - ``` + ```c source "drivers/hdf/khdf/xxx/Kconfig" # Kernel directory to which the HDF module is soft linked. ``` - Add the driver directory to **drivers/adapter/khdf/linux/Makefile**. + Add the driver directory to **drivers/hdf_core/adapter/khdf/linux/Makefile**. - ``` + ```c obj-$(CONFIG_DRIVERS_HDF) += xxx/ ``` Add a **Makefile** to the driver directory **xxx** and add code compiling rules of the driver to the **Makefile** file. - ``` + ```c obj-y += xxx.o ``` @@ -161,16 +158,14 @@ The HDF-based driver development process involves driver implementation, build, - (Mandatory) Set driver device information. - The HDF loads a driver based on the driver device description defined by the HDF. Therefore, the driver device description must be added to the **device_info.hcs** file defined by the HDF. + The HDF loads a driver based on the driver device description defined by the HDF. Therefore, the driver device description must be added to the **device_info.hcs** file defined by the HDF.
The following is an example: - The following is an example: - ``` root { device_info { match_attr = "hdf_manager"; - template host { // Host template. If a node (for example, sample_host) uses the default values in this template, the node fields can be omitted. + template host { // Host template. If a node (for example, sample_host) uses the default values in this template, the node fields can be omitted. hostName = ""; priority = 100; uid = ""; // User ID (UID) of the user-mode process. It is left empty by default. If you do not set the value, this parameter will be set to the value of hostName, which indicates a common user. @@ -191,14 +186,14 @@ The HDF-based driver development process involves driver implementation, build, sample_host :: host{ hostName = "host0"; // Host name. The host node is used as a container to hold a type of drivers. priority = 100; // Host startup priority (0-200). A smaller value indicates a higher priority. The default value 100 is recommended. The hosts with the same priority start based on the time when the priority was configured. The host configured first starts first. - caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // Linux capabilities of the user-mode process. + caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // Linux capabilities of a user-mode process. device_sample :: device { // Sample device node. device0 :: deviceNode { // DeviceNode of the sample driver. policy = 1; // Policy for publishing the driver service. For details, see Driver Service Management. priority = 100; // Driver startup priority (0-200). A smaller value indicates a higher priority. The default value 100 is recommended. The drivers with the same priority start based on the time when the priority was configured. The driver configured first starts first. - preload = 0; // The driver is loaded on demand. For details, see "NOTE" at the end of this document. - permission = 0664; // Permission for the driver to create a device node. - moduleName = "sample_driver"; // Driver name. The value of this field must be the same as that of moduleName in the HdfDriverEntry structure. + preload = 0; // The value 0 means to load the driver by default during the startup of the system. + permission = 0664; // Permission for the DeviceNode created. + moduleName = "sample_driver"; // Driver name. The value must be the same as that of moduleName in the HdfDriverEntry structure. serviceName = "sample_service"; // Name of the service published by the driver. The service name must be unique. deviceMatchAttr = "sample_config"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. } @@ -218,59 +213,30 @@ The HDF-based driver development process involves driver implementation, build, > > - The process UIDs are configured in **base/startup/init_lite/services/etc/passwd**, and the process GIDs are configured in **base/startup/init_lite/services/etc/group**. For details, see [Adding a System Service User Group]( https://gitee.com/openharmony/startup_init_lite/wikis). > - > - If CAP_DAC_OVERRIDE needs to be configured for a service module, enter **caps = ["DAC_OVERRIDE"]** instead of **caps = ["CAP_DAC_OVERRIDE"]**. + > - The **caps** value is in the caps = ["xxx"] format. To configure **CAP_DAC_OVERRIDE**, set this parameter to **caps = ["DAC_OVERRIDE"]**. Do not set it to **caps = ["CAP_DAC_OVERRIDE"]**. + > + > - **preload** specifies the driver loading policy. For details, see [Driver Loading](../driver/driver-hdf-load.md). - (Optional) Set driver private information. - If the driver has private configuration, add a driver configuration file to set default driver configuration. When loading the driver, the HDF obtains and saves the driver private information in **property** of **HdfDeviceObject**, and passes the information to the driver using **Bind()** and **Init()** (see step 1). + If the driver has private configuration, add a driver configuration file to set default driver configuration. When loading the driver, the HDF obtains and saves the driver private information in **property** of **HdfDeviceObject**, and passes the information to the driver using **Bind()** and **Init()** (see step 1).
The following is an example of the driver configuration: - The following is an example of the driver private configuration: - ``` - root { + ``` + root { SampleDriverConfig { sample_version = 1; sample_bus = "I2C_0"; match_attr = "sample_config"; // The value must be the same as that of deviceMatchAttr in device_info.hcs. } } - ``` - - After the configuration, add the configuration file to the board-level configuration entry file **hdf.hcs**. (You can use DevEco to perform one-click configuration. For details, see the description about the driver development suite.) - - The following is an example: - - ``` - #include "device_info/device_info.hcs" - #include "sample/sample_config.hcs" - ``` - - + ``` + + Add the configuration file to the **hdf.hcs** file.
The following is an example: -​ - - -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> Drivers can be loaded on demand or in sequence. -> -> - On-demand loading -> -> ``` -> typedef enum { -> DEVICE_PRELOAD_ENABLE = 0, -> DEVICE_PRELOAD_ENABLE_STEP2, -> DEVICE_PRELOAD_DISABLE, -> DEVICE_PRELOAD_INVALID -> } DevicePreload; -> ``` -> -> - If **preload** in the configuration file is set to **0** (**DEVICE_PRELOAD_ENABLE**), the driver is loaded by default during the system boot process. -> -> - If **preload** is set to **1** (**DEVICE_PRELOAD_ENABLE_STEP2**), the driver is loaded after a quick start is complete. If the system does not support quick start, the value **1** has the same meaning as **DEVICE_PRELOAD_ENABLE**. -> -> - If **preload** is set to **2** (**DEVICE_PRELOAD_DISABLE**), the driver service is dynamically loaded. When a user-mode process requests the driver service, the HDF attempts to dynamically load the driver if the driver service is not available. > For more details, see [Driver Messaging Mechanism](../driver/driver-hdf-message-management.md). -> -> - Sequential loading (**preload** set to **0 (DEVICE_PRELOAD_ENABLE)**) -> -> In the configuration file, the **priority** field (ranging from 0 to 200) determines the loading sequence of a host and a driver. For drivers in different hosts, a smaller host priority value indicates a higher driver loading priority; for drivers in the same host, a smaller driver priority value indicates a higher driver loading priority. + + ``` + #include "device_info/device_info.hcs" + #include "sample/sample_config.hcs" + ``` diff --git a/en/device-dev/driver/driver-hdf-load.md b/en/device-dev/driver/driver-hdf-load.md new file mode 100644 index 0000000000000000000000000000000000000000..8c99bf1088e9a40b50528123b701df2121d77b33 --- /dev/null +++ b/en/device-dev/driver/driver-hdf-load.md @@ -0,0 +1,30 @@ +# Driver Loading + +## Overview + +The HDF loads the drivers that match the configured device list. + +## Driver Loading Policies + +The HDF supports on-demand loading and sequential loading of drivers. The **preload** field in the configuration file (see [Driver Development](../driver/driver-hdf-development.md)) specifies the loading policy of a driver. The values are as follows: + +```c +typedef enum { + DEVICE_PRELOAD_ENABLE = 0, + DEVICE_PRELOAD_ENABLE_STEP2 = 1, + DEVICE_PRELOAD_DISABLE = 2, + DEVICE_PRELOAD_INVALID +} DevicePreload; +``` + +### On-Demand Loading + +- The value **0** (**DEVICE_PRELOAD_ENABLE**) means to load the driver by default during the system boot process. + +- The value **1** (**DEVICE_PRELOAD_ENABLE_STEP2**) means to load the driver after a quick start is complete. If the system does not support quick start, the value **1** has the same meaning as **DEVICE_PRELOAD_ENABLE**. + +- The value **2** (**DEVICE_PRELOAD_DISABLE**) means to load the driver dynamically. When a user-mode process requests the driver service, the HDF attempts to dynamically load the driver if the driver service is not available. For more details, see [Driver Messaging Mechanism](../driver/driver-hdf-message-management.md). + +### Sequential Loading (Default) + +The **priority** field (ranging from 0 to 200) in the configuration file determines the loading sequence of a host and a driver. For the drivers in different hosts, the drivers in the host with a smaller priority value are loaded first. For the drivers in the same host, the driver with a smaller priority value is loaded first. diff --git a/en/device-dev/driver/driver-hdf-manage.md b/en/device-dev/driver/driver-hdf-manage.md index 938fe06a0c51cdb09c2e1e430d02b1844b1cde19..791d863ef954b332af497be8954dd16e59858dbf 100644 --- a/en/device-dev/driver/driver-hdf-manage.md +++ b/en/device-dev/driver/driver-hdf-manage.md @@ -3,7 +3,7 @@ ## HDF Configuration Overview -HDF Configuration Source (HCS) is the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, simplifying configuration management. +HDF Configuration Source (HCS) provides the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, thereby facilitating configuration management. HDF Configuration Generator (HC-GEN) is a tool for converting an HDF configuration file into a file that can be read by the software. @@ -31,24 +31,24 @@ The table below describes the keywords used in the HCS syntax. **Table 1** Keywords used in HCS syntax -| Keyword| Description| Remarks| +| Keyword| Description| Remarks| | -------- | -------- | -------- | -| root | Sets the root node.| - | -| include | References other HCS files.| - | -| delete | Deletes a node or an attribute.| Applicable only to the configuration tree referenced by **include**.| -| template | Defines a template node.| - | -| match_attr | Marks the node attribute for matching.| During configuration parsing, the attribute value can be used to locate the corresponding node.| +| root | Sets the root node.| - | +| include | References other HCS files.| - | +| delete | Deletes a node or an attribute.| Applicable only to the configuration tree referenced by **include**.| +| template | Defines a template node.| - | +| match_attr | Marks the node attribute for matching.| When parsing the configuration, the driver can use the attribute value as a parameter to call an API to locate the node that has this attribute. | ### Basic Structures The HCS has two structures: attribute and node. -**Attribute** +Attribute An attribute is the minimum, independent configuration unit. The syntax is as follows: - + ``` attribute_name = value; ``` @@ -57,7 +57,7 @@ An attribute is the minimum, independent configuration unit. The syntax is as fo - The **value** can be in any of the following formats: - - A binary, octal, decimal, or hexadecimal integer. For details, see the **Data Types** section. + - A binary, octal, decimal, or hexadecimal integer. For details, see [Data Types](#data-types). - String quoted by double quotation marks (""). - Node reference. @@ -67,7 +67,7 @@ An attribute is the minimum, independent configuration unit. The syntax is as fo A node is a set of attributes. The syntax is as follows: - + ``` node_name { module = "sample"; @@ -97,7 +97,7 @@ Attributes automatically use built-in data types, including integer, string, arr - Octal: prefixed with **0**, for example, **0664**. -- Decimal: signed or unsigned, without prefix, for example, **1024** or **+1024**. Negative integers can be read only via signed interfaces. +- Decimal: signed or unsigned, without prefix, for example, **1024** or **+1024**. Negative integers can be read only via APIs with signed numbers. - Hexadecimal: prefixed with **0x**, for example, **0xff00** and **0xFF**. @@ -109,13 +109,13 @@ A string is enclosed in double quotation marks (""). An array can hold either integers or strings, but not a mixture of them. The mixed use of **uint32_t** and **uint64_t** in an integer array will cause typecasting to **uint64**. The following is an example of an integer array and a string array: - + ``` attr_foo = [0x01, 0x02, 0x03, 0x04]; attr_bar = ["hello", "world"]; ``` -**Boolean** +Boolean Boolean data type is a form of data with only two possible values: **true** and **false**. @@ -126,7 +126,7 @@ Boolean data type is a form of data with only two possible values: **true** and The keyword **include** is used to import other HCS files. The syntax is as follows: - + ``` #include "foo.hcs" #include "../bar.hcs" @@ -143,14 +143,14 @@ The following two comment formats are supported: - Single-line comment - + ``` // comment ``` - Multi-line comment - + ``` /* comment @@ -165,13 +165,13 @@ The following two comment formats are supported: You can reference the content of a node to modify the content of another node. The syntax is as follows: - + ``` node :& source_node ``` In this statement, the content of **node** is referenced to modify the content of **source_node**. - + Example: ``` root { @@ -196,7 +196,7 @@ root { The configuration tree generated is as follows: - + ``` root { module = "sample"; @@ -222,7 +222,7 @@ In this example, the value of **bar.attr** is changed to **foo** by referencing You can replicate a node to define a node with similar content. The syntax is as follows: - + ``` node : source_node ``` @@ -230,7 +230,7 @@ You can replicate a node to define a node with similar content. The syntax is as This statement replicates the attributes of the **source_node** node to define **node**. Example: - + ``` root { module = "sample"; @@ -245,7 +245,7 @@ root { The configuration tree generated is as follows: - + ``` root { module = "sample"; @@ -269,7 +269,7 @@ You do not need to specify the path of the **foo** node if the **foo** node and You can use the keyword **delete** to delete unnecessary nodes or attributes from the base configuration tree imported by using the **include** keyword. The following example includes the configuration in **sample2.hcs** to **sample1.hcs** and deletes the **attribute2** attribute and the **foo_2** node. Example: - + ``` // sample2.hcs root { @@ -291,7 +291,7 @@ root { The configuration tree generated is as follows: - + ``` root { attr_1 = 0x1; @@ -306,7 +306,7 @@ root { You can associate an attribute and a node so that the node can be quickly located when the attribute is read during configuration parsing. The syntax is as follows: - + ``` attribute = &node; ``` @@ -314,7 +314,7 @@ You can associate an attribute and a node so that the node can be quickly locate In this statement, the value of **attribute** is a referenced to the node. During code parsing, you can quickly locate the node based on this **attribute**. Example: - + ``` node1 { attributes; @@ -324,9 +324,9 @@ node2 { } ``` -or +Or + - ``` node2 { node1 { @@ -344,7 +344,7 @@ The template is used to generate nodes with consistent syntax, thereby facilitat If a node is defined using the keyword **template**, its child nodes inherit from the node configuration through the double colon operator (::). The child nodes can modify or add but cannot delete attributes in **template**. The attributes not defined in the child nodes will use the attributes defined in **template** as the default values. Example: - + ``` root { module = "sample"; @@ -364,7 +364,7 @@ root { The configuration tree generated is as follows: - + ``` root { module = "sample"; @@ -382,7 +382,7 @@ root { In this example, the **bar** and **bar_1** nodes inherit from the **foo** node. The structure of the generated configuration tree is the same as that of the **foo** node, except that the attribute values are different. -## **Configuration Generation** +## Configuration Generation The HC-GEN tool checks the HCS configuration syntax and converts HCS source files into HCB files. @@ -391,7 +391,7 @@ The HC-GEN tool checks the HCS configuration syntax and converts HCS source file HC-GEN options: - + ``` Usage: hc-gen [Options] [File] options: @@ -410,28 +410,28 @@ options: Generate a .c or .h configuration file. - + ``` hc-gen -o [OutputCFileName] -t [SourceHcsFileName] ``` Generate an HCB file. - + ``` hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName] ``` Generate a macro definition file. - + ``` hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName] ``` Decompile an HCB file to an HCS file. - + ``` hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName] ``` diff --git a/en/device-dev/driver/driver-hdf-message-management.md b/en/device-dev/driver/driver-hdf-message-management.md index 65ae4d200de147bdc04d9755809a4f2368197426..0efb9694a6e4ba21776d7dbe576f0c5da8b36743 100644 --- a/en/device-dev/driver/driver-hdf-message-management.md +++ b/en/device-dev/driver/driver-hdf-message-management.md @@ -10,23 +10,23 @@ The HDF messaging mechanism implements the interaction between the user-mode app The messaging mechanism allows: -1. User-mode applications to send messages to drivers. +1. A user-mode application to send a message to a driver. -2. User-mode applications to receive events from drivers. +2. A user-mode application to receive events reported by a driver. **Table 1** APIs for the driver messaging mechanism | API| Description| | -------- | -------- | -| struct HdfIoService \*HdfIoServiceBind(const char \*serviceName); | Obtains a driver service. The **Dispatch()** method in the driver service obtained sends messages to the driver.| -| void HdfIoServiceRecycle(struct HdfIoService \*service); | Releases a driver service.| -| int HdfDeviceRegisterEventListener(struct HdfIoService \*target, struct HdfDevEventlistener \*listener); | Registers the method for receiving events reported by the driver.| -| int HdfDeviceSendEvent(struct HdfDeviceObject \*deviceObject, uint32_t id, struct HdfSBuf \*data); | Sends events. | +| struct HdfIoService \*HdfIoServiceBind(const char \*serviceName); | Obtains a driver service. After obtaining the driver service, the user-mode application uses **Dispatch()** in the driver service obtained to send messages to the driver.| +| void HdfIoServiceRecycle(struct HdfIoService \*service); | Releases a driver service.| +| int HdfDeviceRegisterEventListener(struct HdfIoService \*target, struct HdfDevEventlistener \*listener); | Registers the method for receiving events reported by the driver.| +| int HdfDeviceSendEvent(struct HdfDeviceObject \*deviceObject, uint32_t id, struct HdfSBuf \*data); | Sends events. | ## How to Develop -1. In the driver configuration, set **policy** to **2**. For more details, see [policy](../driver/driver-hdf-servicemanage.md). +1. In the driver configuration file, set **policy** to **2**. For more details, see [Driver Service Management](../driver/driver-hdf-servicemanage.md). ``` device_sample :: Device { @@ -35,23 +35,23 @@ The messaging mechanism allows: } ``` -2. Set the driver permission. By default, the **permission** field is set to **0666**, which allows the driver to create device nodes. You can set this field based on service requirements. +2. Set permissions for the device node of the driver. By default, the **permission** field is set to **0666**. You can set it based on service requirements. 3. Implement the **Dispatch()** method of **IDeviceIoService**. - ``` - // Dispatch messages sent from the user-mode application. - int32_t SampleDriverDispatch(struct HdfDeviceObject *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) + ```c + // Dispatch() is used to process messages sent from the user-mode application. + int32_t SampleDriverDispatch(struct HdfDeviceIoClient *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) { - HDF_LOGE("sample driver lite A dispatch"); - return 0; + HDF_LOGI("sample driver lite A dispatch"); + return HDF_SUCCESS; } int32_t SampleDriverBind(struct HdfDeviceObject *device) { - HDF_LOGE("test for lite os sample driver A Open!"); + HDF_LOGI("test for lite os sample driver A Open!"); if (device == NULL) { HDF_LOGE("test for lite os sample driver A Open failed!"); - return -1; + return HDF_FAILURE; } static struct ISampleDriverService sampleDriverA = { .ioService.Dispatch = SampleDriverDispatch, @@ -59,36 +59,36 @@ The messaging mechanism allows: .ServiceB = SampleDriverServiceB, }; device->service = (struct IDeviceIoService *)(&sampleDriverA); - return 0; + return HDF_SUCCESS; } ``` 4. Define the cmd type in the message processing function. - ``` + ```c #define SAMPLE_WRITE_READ 1 // Read and write operation 1 ``` -5. Bind the driver service and the user-mode application to enable messages to be sent to the driver. +5. Enable the user-mode application to obtain a service and send a message to the driver. - ``` + ```c int SendMsg(const char *testMsg) { if (testMsg == NULL) { HDF_LOGE("test msg is null"); - return -1; + return HDF_FAILURE; } struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); if (serv == NULL) { HDF_LOGE("fail to get service"); - return -1; + return HDF_FAILURE; } - struct HdfSBuf *data = HdfSBufObtainDefaultSize(); + struct HdfSBuf *data = HdfSbufObtainDefaultSize(); if (data == NULL) { HDF_LOGE("fail to obtain sbuf data"); - return -1; + return HDF_FAILURE; } - struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); + struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); if (reply == NULL) { HDF_LOGE("fail to obtain sbuf reply"); ret = HDF_DEV_ERR_NO_MEMORY; @@ -105,41 +105,41 @@ The messaging mechanism allows: goto out; } out: - HdfSBufRecycle(data); - HdfSBufRecycle(reply); + HdfSbufRecycle(data); + HdfSbbufRecycle(reply); HdfIoServiceRecycle(serv); return ret; } ``` 6. Enable the user-mode application to receive messages from the driver. - 1. Enable the driver to report events to the user-mode application. + 1. Implement the method for the user-mode application to process the events reported by the driver. - ``` + ```c static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) { OsalTimespec time; OsalGetTime(&time); - HDF_LOGE("%s received event at %llu.%llu", (char *)priv, time.sec, time.usec); + HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec); const char *string = HdfSbufReadString(data); if (string == NULL) { HDF_LOGE("fail to read string in event data"); - return -1; + return HDF_FAILURE; } - HDF_LOGE("%s: dev event received: %d %s", (char *)priv, id, string); - return 0; + HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s", (char *)priv, id, string); + return HDF_SUCCESS; } ``` - 2. Register the method for receiving the messages from the driver. + 2. Register the method for the user-mode application to receive messages from the driver. - ``` + ```c int RegisterListen() { struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); if (serv == NULL) { HDF_LOGE("fail to get service"); - return -1; + return HDF_FAILURE; } static struct HdfDevEventlistener listener = { .callBack = OnDevEventReceived, @@ -147,20 +147,20 @@ The messaging mechanism allows: }; if (HdfDeviceRegisterEventListener(serv, &listener) != 0) { HDF_LOGE("fail to register event listener"); - return -1; + return HDF_FAILURE; } ...... HdfDeviceUnregisterEventListener(serv, &listener); HdfIoServiceRecycle(serv); - return 0; + return HDF_SUCCESS; } ``` 3. Enable the driver to report events. - ``` - int32_t SampleDriverDispatch(struct HdfDeviceObject *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) + ```c + int32_t SampleDriverDispatch(HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) { - ... // Process the API call. - return HdfDeviceSendEvent(deviceObject, cmdCode, data); + // Process the API call. + return HdfDeviceSendEvent(client->device, cmdCode, data); } ``` diff --git a/en/device-dev/driver/driver-hdf-overview.md b/en/device-dev/driver/driver-hdf-overview.md index 451a190bc027610966bafeb7bef011fabc3ff495..46991d127a694dfb38987ac474bce3b8c98812d1 100644 --- a/en/device-dev/driver/driver-hdf-overview.md +++ b/en/device-dev/driver/driver-hdf-overview.md @@ -3,7 +3,7 @@ ## Introduction -The Hardware Driver Foundation (HDF) provides driver framework capabilities including driver loading, driver service management, and driver messaging mechanism. It strives to build a unified driver architecture platform to provide a more precise and efficient development environment, where you can perform one-time development for multi-device deployment. +The Hardware Driver Foundation (HDF) provides driver framework capabilities, such as driver loading, driver service management, driver messaging mechanism, and configuration management, for driver developers. It strives to provide a more precise and efficient driver development environment, where you can perform one-time development for multi-device deployment. ## Driver Loading @@ -12,18 +12,23 @@ The HDF supports the following loading modes: - On-demand loading - The driver is loaded by default during the operating system (OS) boot process or dynamically loaded after OS is started. + The HDF allows drivers to be loaded by default during the operating system (OS) boot process or dynamically loaded after the OS is started. - Sequential loading - The driver is loaded based on its priority during the OS boot process. + The HDF allows drivers to be loaded based on their priorities during the OS boot process. ## Driver Service Management -The HDF allows centralized management of driver services. You can obtain a driver service by using the API provided by the HDF. +The HDF supports centralized management of driver services. You can obtain a driver service by using the API provided by the HDF. -## Driver messaging mechanism +## Driver Messaging Mechanism The HDF provides a unified driver messaging mechanism, which allows messages to be exchanged between user-mode applications and kernel-mode drivers. + + +## Configuration Management + +HDF Configuration Source (HCS) provides the source code that describes the HDF configuration in key-value pairs. It decouples the configuration code from driver code, simplifying configuration management. diff --git a/en/device-dev/driver/driver-hdf-sample.md b/en/device-dev/driver/driver-hdf-sample.md index 29288f97467bd55616335e1f5505335526d85e89..8b3d31a230f24aeb0107e5e201c64b80bfeb9946 100644 --- a/en/device-dev/driver/driver-hdf-sample.md +++ b/en/device-dev/driver/driver-hdf-sample.md @@ -51,7 +51,7 @@ root { Write the driver code based on the HDF. For more details, see [Driver Development](../driver/driver-hdf-development.md). -``` +```c #include #include #include @@ -59,34 +59,34 @@ Write the driver code based on the HDF. For more details, see [Driver Developmen #include "hdf_base.h" #include "hdf_device_desc.h" -#define HDF_LOG_TAG "sample_driver" +#define HDF_LOG_TAG sample_driver #define SAMPLE_WRITE_READ 123 -int32_t HdfSampleDriverDispatch( - struct HdfDeviceObject *deviceObject, int id, struct HdfSBuf *data, struct HdfSBuf *reply) +static int32_t HdfSampleDriverDispatch( + struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply) { - HDF_LOGE("%s: received cmd %d", __func__, id); + HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id); if (id == SAMPLE_WRITE_READ) { const char *readData = HdfSbufReadString(data); if (readData != NULL) { - HDF_LOGE("%s: read data is: %s", __func__, readData); + HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData); } if (!HdfSbufWriteInt32(reply, INT32_MAX)) { - HDF_LOGE("%s: reply int32 fail", __func__); + HDF_LOGE("%{public}s: reply int32 fail", __func__); } - return HdfDeviceSendEvent(deviceObject, id, data); + return HdfDeviceSendEvent(client->device, id, data); } return HDF_FAILURE; } -void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) +static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) { // Release resources. return; } -int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) +static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) { if (deviceObject == NULL) { return HDF_FAILURE; @@ -98,17 +98,17 @@ int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) return HDF_SUCCESS; } -int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) +static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) { if (deviceObject == NULL) { - HDF_LOGE("%s::ptr is null!", __func__); + HDF_LOGE("%{public}s::ptr is null!", __func__); return HDF_FAILURE; } - HDF_LOGE("Sample driver Init success"); + HDF_LOGI("Sample driver Init success"); return HDF_SUCCESS; } -struct HdfDriverEntry g_sampleDriverEntry = { +static struct HdfDriverEntry g_sampleDriverEntry = { .moduleVersion = 1, .moduleName = "sample_driver", .Bind = HdfSampleDriverBind, @@ -122,10 +122,10 @@ HDF_INIT(g_sampleDriverEntry); ## Implementing Interaction Between the Application and the Driver -Write the code for interaction between the user-mode application and the driver. Place the code in the **drivers/adapter/uhdf** directory for compilation. For details about **build.gn**, see **drivers/framework/sample/platform/uart/dev/build.gn**. +Write the code for interaction between the user-mode application and the driver. Place the code in the **drivers/hdf_core/adapter/uhdf** directory for compilation. For details about **BUILD.gn**, see **drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn**. -``` +```c #include #include #include @@ -134,7 +134,7 @@ Write the code for interaction between the user-mode application and the driver. #include "hdf_sbuf.h" #include "hdf_io_service_if.h" -#define HDF_LOG_TAG "sample_test" +#define HDF_LOG_TAG sample_test #define SAMPLE_SERVICE_NAME "sample_service" #define SAMPLE_WRITE_READ 123 @@ -149,7 +149,7 @@ static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) g_replyFlag = 1; return HDF_FAILURE; } - HDF_LOGE("%s: dev event received: %u %s", (char *)priv, id, string); + HDF_LOGI("%{public}s: dev event received: %{public}u %{public}s", (char *)priv, id, string); g_replyFlag = 1; return HDF_SUCCESS; } @@ -157,13 +157,13 @@ static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) static int SendEvent(struct HdfIoService *serv, char *eventData) { int ret = 0; - struct HdfSBuf *data = HdfSBufObtainDefaultSize(); + struct HdfSBuf *data = HdfSbufObtainDefaultSize(); if (data == NULL) { HDF_LOGE("fail to obtain sbuf data"); return 1; } - struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); + struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); if (reply == NULL) { HDF_LOGE("fail to obtain sbuf reply"); ret = HDF_DEV_ERR_NO_MEMORY; @@ -188,10 +188,10 @@ static int SendEvent(struct HdfIoService *serv, char *eventData) ret = HDF_ERR_INVALID_OBJECT; goto out; } - HDF_LOGE("Get reply is: %d", replyData); + HDF_LOGI("Get reply is: %{public}d", replyData); out: - HdfSBufRecycle(data); - HdfSBufRecycle(reply); + HdfSbufRecycle(data); + HdfSbufRecycle(reply); return ret; } @@ -237,8 +237,8 @@ int main() > > deps = [ > -> "//drivers/adapter/uhdf/manager:hdf_core", +> "//drivers/hdf_core/adapter/uhdf/manager:hdf_core", > -> "//drivers/adapter/uhdf/posix:hdf_posix_osal", +> "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal", > > ] diff --git a/en/device-dev/driver/driver-hdf-servicemanage.md b/en/device-dev/driver/driver-hdf-servicemanage.md index 9cb526b98bb33b6cabc4af87fb17b45760a83bdb..dc617206e0bbfe31e26ff40adbed6fda2c6a0bc1 100644 --- a/en/device-dev/driver/driver-hdf-servicemanage.md +++ b/en/device-dev/driver/driver-hdf-servicemanage.md @@ -7,8 +7,8 @@ Driver services are objects of capabilities provided by HDF driver devices to ex The HDF uses the **policy** field in the configuration file to define policies for a driver to provide services externally. The values this field are as follows: - -``` + +```c typedef enum { /* The driver does not provide services. */ SERVICE_POLICY_NONE = 0, @@ -28,7 +28,7 @@ typedef enum { ## When to Use -The driver service management capability can be used if the driver provides capabilities using APIs. +You can use the driver service management capability of the HDF when the driver needs to provide capabilities via APIs. ## Available APIs @@ -37,7 +37,7 @@ The table below describes the APIs for driver service management. **Table 1** APIs for driver service management -| API| Description| +| API| Description| | -------- | -------- | | int32_t (\*Bind)(struct HdfDeviceObject \*deviceObject) | Binds a service API to the HDF. You need to implement the **Bind()** function.| | const struct HdfObject \*DevSvcManagerClntGetService(const char \*svcName)| Obtains a driver service.| @@ -48,9 +48,9 @@ The table below describes the APIs for driver service management. The development procedure is as follows: -1. Define the services to be published by the driver. - - ``` +1. Define the service to be published by the driver. + + ```c // Define the driver service structure. struct ISampleDriverService { struct IDeviceIoService ioService; // The first member must be of the IDeviceIoService type. @@ -62,91 +62,87 @@ The development procedure is as follows: int32_t SampleDriverServiceA(void) { // You need to implement the service logic. - return 0; + return HDF_SUCCESS; } int32_t SampleDriverServiceB(uint32_t inputCode) { // You need to implement the service logic. - return 0; + return HDF_SUCCESS; } ``` -2. Bind the driver service to the HDF and implement the **Bind()** function in the **HdfDriverEntry** structure. +2. Bind the driver service. - ``` + Implement the **Bind** pointer function, for example, **SampleDriverBind**, in **HdfDriverEntry** to bind the driver service to the HDF. + + ```c int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject) { // deviceObject is a pointer to the device object created by the HDF for each driver. The device object holds private device data and service APIs. if (deviceObject == NULL) { HDF_LOGE("Sample device object is null!"); - return -1; + return HDF_FAILURE; } static struct ISampleDriverService sampleDriverA = { .ServiceA = SampleDriverServiceA, .ServiceB = SampleDriverServiceB, }; deviceObject->service = &sampleDriverA.ioService; - return 0; + return HDF_SUCCESS; } ``` 3. Obtain the driver service. - The driver service can be obtained by using the API or subscription mechanism provided by the HDF. - - - Using the API + The driver service can be obtained by using either of the following methods: - Use the API provided by the HDF to obtain the driver service if the driver has been loaded. + - Using the API provided by the HDF - ``` - ​``` - const struct ISampleDriverService *sampleService = - (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver"); - if (sampleService == NULL) { - return -1; - } - sampleService->ServiceA(); - sampleService->ServiceB(5); - ​``` - ``` + If the service requester clearly knows the time when the driver is loaded, use the API provided by the HDF to obtain the driver service. The following is an example: + + ```c + const struct ISampleDriverService *sampleService = + (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver"); + if (sampleService == NULL) { + return HDF_FAILURE; + } + sampleService->ServiceA(); + sampleService->ServiceB(5); + ``` - Using the subscription mechanism - If the kernel mode is unaware of the time for loading drivers on the same host, use the subscription mechanism provided by the HDF to subscribe to the drivers. After the drivers are loaded, the HDF publishes the driver services to the subscriber. - - The implementation is as follows: - - ``` - // Callback invoked to return the driver services after the subscribed driver is loaded. - // object is the pointer to the private data of the subscriber, and service is the pointer to the subscribed service object. - int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service) - { - const struct ISampleDriverService *sampleService = - (const struct ISampleDriverService *)service; - if (sampleService == NULL) { - return -1; - } - sampleService->ServiceA(); - sampleService->ServiceB(5); - } - // Implement the subscription process. - int32_t TestDriverInit(struct HdfDeviceObject *deviceObject) - { - if (deviceObject == NULL) { - HDF_LOGE("Test driver init failed, deviceObject is null!"); - return -1; - } - struct SubscriberCallback callBack; - callBack.deviceObject = deviceObject; - callBack.OnServiceConnected = TestDriverSubCallBack; - int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack); - if (ret != 0) { - HDF_LOGE("Test driver subscribe sample driver failed!"); - } - return ret; - } - ``` - - - + If the service requester is unaware of the time when the driver (in the same host) is loaded, use the subscription mechanism provided by the HDF to subscribe to the service. After the driver is loaded, the HDF publishes the driver service to the subscriber. The implementation is as follows: + + + ```c + // Callback invoked to return the driver service after the subscribed driver is loaded. + // object is the pointer to the private data of the subscriber, and service is the pointer to the subscribed service object. + int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service) + { + const struct ISampleDriverService *sampleService = + (const struct ISampleDriverService *)service; + if (sampleService == NULL) { + return HDF_FAILURE; + } + sampleService->ServiceA(); + sampleService->ServiceB(5); + } + // Implement the subscription process. + int32_t TestDriverInit(struct HdfDeviceObject *deviceObject) + { + if (deviceObject == NULL) { + HDF_LOGE("Test driver init failed, deviceObject is null!"); + return HDF_FAILURE; + } + struct SubscriberCallback callBack; + callBack.deviceObject = deviceObject; + callBack.OnServiceConnected = TestDriverSubCallBack; + int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack); + if (ret != HDF_SUCCESS) { + HDF_LOGE("Test driver subscribe sample driver failed!"); + } + return ret; + } + ``` diff --git a/en/device-dev/driver/driver-peripherals-camera-des.md b/en/device-dev/driver/driver-peripherals-camera-des.md index d161e032955a7fed5b93c46acd41ca1f855fda90..61384c46f9b08193d116b67b3a06af49fb07130c 100644 --- a/en/device-dev/driver/driver-peripherals-camera-des.md +++ b/en/device-dev/driver/driver-peripherals-camera-des.md @@ -1,128 +1,137 @@ # Camera -## Overview -### Camera +## Overview +### Function -The OpenHarmony camera driver model implements the camera hardware device interface (HDI) and the camera pipeline model to manage camera devices. -The camera driver model consists of the following layers: +The OpenHarmony camera driver model provides the camera hardware device interface (HDI) and the camera pipeline model to manage camera devices. +The camera driver model is divided into three layers: + HDI implementation layer: implements standard ohos (OpenHarmony operating system) APIs for cameras. -+ Framework layer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. -+ Device adaptation layer: shields the differences between underlying chips and OSs for multi-platform adaptation. ++ Framework layer: interacts with the HDI implementation layer to set up data channels and operate camera devices. ++ Device adaptation layer: supports different platforms by shielding the differences in underlying chips and operating systems. -### Working Principles +### Working Principles -The camera module is used to initialize services and devices, set up data channels, and configure, create, deliver, and capture streams. The figure below illustrates camera driver model. +The camera module is used to initialize services and devices, set up data channels, and configure, create, deliver, and capture streams. The following figure shows the camera driver model. **Figure 1** HDF-based camera driver model ![](figures/camera-driver-model-architecture.png) -1. When the system starts, the camera_host process is created. The process enumerates underlying devices, creates a **DeviceManager** instance that manages the device tree, an object for each underlying device, and a **CameraHost** instance, and registers the **CameraHost** instance with the UHDF service. Through the UHDF service, the camera service can obtain the underlying **CameraDeviceHost** services to operate the hardware devices. Note that the **DeviceManager** instance can also be created by using the configuration table. +1. When the system starts, the camera_host process is created. The process enumerates underlying devices, creates a **DeviceManager** instance (to manage the device tree), an object for each underlying device, and a **CameraHost** instance, and registers the **CameraHost** instance with the user-mode HDF (UHDF) service. Through the UHDF service, the camera service can obtain the underlying **CameraDeviceHost** services to operate the hardware devices. The **DeviceManager** instance can also be created by using the configuration table. -2. The Camera Service obtains the **CameraHost** instance through the CameraDeviceHost service. The **CameraHost** instance can be used to obtain the bottom-layer camera capabilities, turn on the flashlight, call the **Open()** interface to start the camera and create a connection, create a **DeviceManager** instance (powering on the bottom-layer hardware modules), and create a **CameraDevice** instance (providing the device control interface for the upper layer). When the **CameraDevice** instance is created, each submodule of PipelineCore is instantiated. Among the submodules, StreamPipelineCore is responsible for creating pipelines, and MetaQueueManager is responsible for reporting metadata. +2. The Camera Service obtains the **CameraHost** instance through the CameraDeviceHost service. + + The **CameraHost** instance can be used to obtain the underlying camera capabilities, turn on the flashlight, call **Open()** to start a camera and set up a connection with the camera, create a **DeviceManager** instance (to power on the hardware modules), and create a **CameraDevice** instance (to provide the device control interface for the upper layer). + + When the **CameraDevice** instance is created, the PipelineCore modules will be instantiated. The StreamPipelineCore module creates pipelines, and the MetaQueueManager module reports metadata. 3. The Camera Service configures stream and creates a **Stream** class through the CameraDevice module. The StreamPipelineStrategy module creates the node connection mode of the corresponding stream by using the mode issued by the upper layer and querying the configuration table. The StreamPipelineBuilder module creates a node and returns the pipeline to the StreamPipelineDispatcher module through the connection. The StreamPipelineDispatcher module dispatches pipelines. -4. The Camera Service controls the stream operations through the **Stream** instance. The **AttachBufferQueue()** interface is used to deliver the buffer queue requested from the display module to the bottom layer. The CameraDeviceDriverModel manages the buffer. After the **Capture()** interface is called to deliver commands, the bottom layer transfers the buffer to the upper layer. The Image Signal Processor (ISP) node obtains a specified number of buffers from the buffer queue and delivers the buffers to the bottom-layer ISP hardware. After filling the buffers, the ISP hardware transfers the buffers to the CameraDeviceDriverModel. The CameraDeviceDriverModel fills the created pipeline with the received buffers by using a loop thread. Each node processes the pipeline data and transfers the data to the upper layer by using a callback. At the same time, the buffers are freed for reuse. +4. The Camera Service controls the stream operations through the **Stream** instance. -5. The Camera Service delivers the photographing command through the **Capture()** interface. The **ChangeToOfflineStream()** interface is used to query the position of the photographing buffer. If the ISP hardware has output an image and sent the image data to the IPP node, the common photographing streams can be converted into offline streams. Otherwise, the close process is executed. The **ChangeToOfflineStream()** interface transfers **StreamInfo** to enable the offline stream to obtain the stream information of the common stream, confirms the node connection mode of the offline stream based on the configuration table, and creates the node connection of the offline stream. If the node connection has been created, the interface releases the node required by the non-offline stream through **CloseCamera()**. It then waits for the buffer to return from the bottom-layer pipeline to the upper layer and then releases the pipeline resources. + **AttachBufferQueue()** delivers the buffer queue requested from the display module to the bottom layer. The CameraDeviceDriverModel manages the buffer. After **Capture()** is called to deliver commands, the bottom layer transfers the buffer to the upper layer. The Image Signal Processor (ISP) node obtains a specified number of buffers from the buffer queue and delivers the buffers to the bottom-layer ISP hardware. After filling the buffers, the ISP hardware transfers the buffers to the CameraDeviceDriverModel. The CameraDeviceDriverModel fills the created pipeline with the received buffers by using a loop thread. Each node processes the pipeline data and transfers the data to the upper layer in a callback. At the same time, the buffers are freed to the buffer queue for reuse. -6. The Camera Service sends the **CaptureSetting** parameter to the CameraDeviceDriverModel through the **UpdateSettings()** interface of the **CameraDevice** instance. The CameraDeviceDriverModel forwards the parameter to each node through the StreamPipelineDispatcher module. The **CaptureSetting** parameter carried in **StartStreamingCapture()** and **Capture()** is forwarded to the node to which the stream belongs through the StreamPipelineDispatcher module. +5. The Camera Service delivers the photographing command through **Capture()**. **ChangeToOfflineStream()** is used to query the position of the photographing buffer. If the ISP hardware has output an image and sent the image data to the IPP node, the common photographing stream can be converted into an offline stream. Otherwise, the close process is executed. **ChangeToOfflineStream()** passes **StreamInfo** to enable the offline stream to obtain the stream information of the common stream, determines the node connection mode of the offline stream based on the configuration table, and creates the node connection for the offline stream (if the node connection has been created, the node required by the non-offline stream will be closed by **CloseCamera**.) When the buffer is transferred from the pipeline to the upper layer, the pipeline resources are released. -7. The Camera Service controls underlying metadata reporting through the **EnableResult()** and **DisableResult()** interfaces. If the underlying metadata needs to be reported, the pipeline creates a buffer queue in the CameraDeviceDriverModel to collect and transfer metadata, queries the configuration table based on the StreamPipelineStrategy module, and creates and connects to the specified node through the StreamPipelineBuilder module. The MetaQueueManager module delivers the buffer to the bottom layer, and the bottom-layer node fills in data. The MetaQueueManager module then invokes the upper-layer callback to transfer the data to the upper layer. +6. The Camera Service sends the **CaptureSetting** parameter to the CameraDeviceDriverModel through **UpdateSettings()** of the **CameraDevice** instance. The CameraDeviceDriverModel forwards the parameter to each node through the StreamPipelineDispatcher module. The **CaptureSetting** parameter carried in **StartStreamingCapture()** and **Capture()** is forwarded to the node to which the stream belongs through the StreamPipelineDispatcher module. -8. The Camera Service calls the **Close()** interface of the **CameraDevice** class, and the **CameraDevice** instance calls the corresponding DeviceManager module to power off each hardware. If an offline stream exists in the subpipeline of the IPP node, the offline stream must be reserved until the execution is complete. +7. The Camera Service uses **EnableResult()** and **DisableResult()** to control the reporting of underlying metadata. If the underlying metadata needs to be reported, the pipeline creates a buffer queue in the CameraDeviceDriverModel to collect and transfer metadata, queries the configuration table based on the StreamPipelineStrategy module, and creates and connects to the specified node through the StreamPipelineBuilder module. The MetaQueueManager module delivers the buffer to the bottom layer, and the bottom-layer node fills in data. The MetaQueueManager module then invokes the upper-layer callback to transfer the data to the upper layer. + +8. The Camera Service calls **Close()** of the **CameraDevice** class, and the **CameraDevice** instance calls the corresponding DeviceManager module to power off each hardware. If an offline stream exists in the subpipeline of the IPP node, the offline stream must be reserved until the execution is complete. 9. To implement dynamic frame control, a CollectBuffer thread is started in the StreamOperator. The CollectBuffer thread obtains a buffer from the buffer queue of each stream. If the frame rate of a stream needs to be controlled (1/n of the sensor output frame rate), the CollectBuffer thread can control the buffer packaging of each frame as required, and determine whether to collect the buffer of the stream. For example, if the output frame rate of the sensor is 120 fps and the preview stream frame rate is 30 fps, the CollectBuffer thread collects the buffer of the preview stream every 4 fps. -## Development Guidelines +## Development Guidelines -### When to Use +### When to Use -The camera module encapsulates camera operations in camera preview, photographing, and video streams to facilitate camera hardware operations and improve development efficiency. +The camera module encapsulates camera operations in camera preview, photographing, and video streams to implement camera hardware operations and improve development efficiency. -### Available APIs +### Available APIs +The following table describes the C++ APIs generated from the Interface Definition Language (IDL) interface description. For details about the interface declaration, see the .idl file in **/drivers/interface/camera/v1_0/**. +The parameters passed in the HDI cannot exceed the capability range obtained by **GetCameraAbility**. Even if the parameters beyond the capability range can be passed in APIs such as **UpdateSettings**, **CommitStreams**, and **Capture** with no error returned, unexpected behavior may be caused. - icamera_device.h - | API | Description | - | ------------------------------------------------------------ | ---------------------------- | - | CamRetCode GetStreamOperator(
const OHOS::sptr &callback,
OHOS::sptr &streamOperator) | Obtains the stream controller. | - | CamRetCode UpdateSettings(const std::shared_ptr &settings) | Updates device control parameters. | - | CamRetCode SetResultMode(const ResultCallbackMode &mode) | Sets the result callback mode and function.| - | CamRetCode GetEnabledResults(std::vector &results) | Obtains the enabled ResultMeta. | - | CamRetCode EnableResult(const std::vector &results) | Enables specific ResultMeta. | - | CamRetCode DisableResult(const std::vector &results) | Disables specific ResultMeta. | - | void Close() | Closes the camera device. | + | API | Description | + | ---------------------------- | ------------------------------------------------------------ | + | int32_t GetStreamOperator(const sptr& callbackObj, sptr& streamOperator) | Obtains the stream controller. | + | int32_t UpdateSettings(const std::vector& settings) | Updates device control parameters. | + | int32_t SetResultMode(ResultCallbackMode mode) | Sets the result callback mode and function.| + | int32_t GetEnabledResults(std::vector& results) | Obtains the enabled ResultMeta. | + | int32_t EnableResult(const std::vector& results) | Enables specific ResultMeta. | + | int32_t DisableResult(const std::vector& results) | Disables specific ResultMeta. | + | int32_t Close() | Closes the camera device. | - icamera_device_callback.h - | API | Description | + | API | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | - | void OnError(ErrorType type, int32_t errorCode) | Called when an error occurs on the device to return error information. You need to implement this interface.| - | void OnResult(uint64_t timestamp, const std::shared_ptr &result) | Callback invoked to report metadata related to the camera device. | + | int32_t OnError(ErrorType type, int32_t errorCode) | Called when an error occurs on the camera device. The caller needs to implement this API. | + | int32_t OnResult(uint64_t timestamp, const std::vector& result) | Called to report metadata related to the camera device. | - icamera_host.h - | API | Description | - | ------------------------------------------------------------ | ------------------------------ | - | CamRetCode SetCallback(const OHOS::sptr &callback) | Sets the **ICameraHostCallback** API. | - | CamRetCode GetCameraIds(std::vector\ &cameraIds) | Obtains the IDs of available camera devices.| - | CamRetCode GetCameraAbility(const std::string &cameraId,
std::shared_ptr &ability) | Obtains the abilities of a camera device. | - | CamRetCode OpenCamera(const std::string &cameraId,
const OHOS::sptr &callback,
OHOS::sptr &device) | Opens a camera. | - | CamRetCode SetFlashlight(const std::string &cameraId, bool &isEnable) | Turns on or off the flash. | + | API | Description | + | ------------------------------------------------------------ | ------------------------------------------------------------ | + | int32_t SetCallback(const sptr& callbackObj) | Sets the **ICameraHostCallback** API. | + | int32_t GetCameraIds(std::vector& cameraIds) | Obtains the IDs of available camera devices. | + | int32_t GetCameraAbility(const std::string& cameraId, std::vector& cameraAbility) | Obtains the abilities of a camera device. | + | int32_t OpenCamera(const std::string& cameraId, const sptr& callbackObj, sptr& device) | Opens a camera. | + | int32_t SetFlashlight(const std::string& cameraId, bool isEnable) | Turns on or off the flash. | - icamera_host_callback.h - | API | Description | - | ------------------------------------------------------------ | ---------------------- | - | void OnCameraStatus(const std::string &cameraId, CameraStatus status) | Reports camera status changes.| - | void OnFlashlightStatus(const std::string &cameraId, FlashlightStatus status) | Callback invoked to report the flash status changes. | + | API | Description | + | ------------------------------------------------------------ | ------------------------------------------------------------ | + | int32_t OnCameraStatus(const std::string& cameraId, CameraStatus status) | Called to report camera status changes. | + | int32_t OnFlashlightStatus(const std::string& cameraId, FlashlightStatus status) | Called to report the flash status changes. | + | int32_t OnCameraEvent(const std::string& cameraId, CameraEvent event) | Called to report a camera event. | - ioffline_stream_operator.h - | API | Description | - | ------------------------------------------------------------ | -------------- | - | CamRetCode CancelCapture(int captureId) | Cancels a capture request. | - | CamRetCode ReleaseStreams(const std::vector &streamIds) | Releases streams. | - | CamRetCode Release() | Releases all offline streams.| + | API | Description | + | ---- | ---- | + | int32_t CancelCapture(int32_t captureId) | Cancels a capture request. | + | int32_t ReleaseStreams(const std::vector& streamIds) | Releases streams. | + | int32_t Release() | Releases all offline streams. | - istream_operator.h - | API | Description | - | ------------------------------------------------------------ | -------------------------------- | - | CamRetCode IsStreamsSupported(
OperationMode mode,
const std::shared_ptr\ &modeSetting,
const std::vector<std::shared_ptr<StreamInfo>> &info,
StreamSupportType &type) | Checks whether a stream can be added. | - | CamRetCode CreateStreams(const std::vector> &streamInfos) | Creates streams. | - | CamRetCode ReleaseStreams(const std::vector &streamIds) | Releases streams. | - | CamRetCode CommitStreams(OperationMode mode,
const std::shared_ptr &modeSetting) | Configure streams. | - | CamRetCode GetStreamAttributes(
std::vector> &attributes) | Obtain stream attributes. | - | CamRetCode AttachBufferQueue(int streamId, const OHOS::sptr\ &producer) | Attaches a producer handle to a stream. | - | CamRetCode DetachBufferQueue(int streamId) | Detaches a producer handle from a stream.| - | CamRetCode Capture(int captureId,
const std::shared_ptr &info, bool isStreaming) | Captures images. | - | CamRetCode CancelCapture(int captureId) | Cancels a capture. | - | CamRetCode ChangeToOfflineStream(const std::vector &streamIds,
OHOS::sptr &callback,
OHOS::sptr &offlineOperator) | Changes a stream into an offline stream. | + | API | Description | + | ------------------------------------------------------------ | ------------------------------------------------------------ | + | int32_t IsStreamsSupported(OperationMode mode,
const std::vector& modeSetting,
const std::vector& infos,
StreamSupportType& type) | Checks whether a stream can be added. | + | int32_t CreateStreams(const std::vector& streamInfos) | Creates streams. | + | int32_t ReleaseStreams(const std::vector& streamIds) | Releases streams. | + | int32_t CommitStreams(OperationMode mode, const std::vector& modeSetting) | Configure streams. | + | int32_t GetStreamAttributes(std::vector& attributes) | Obtain stream attributes. | + | int32_t AttachBufferQueue(int32_t streamId, const sptr& bufferProducer) | Attaches a producer handle to a stream. | + | int32_t DetachBufferQueue(int32_t streamId) | Detaches a producer handle from a stream. | + | int32_t Capture(int32_t captureId, const CaptureInfo& info, bool isStreaming) | Captures images. | + | int32_t CancelCapture(int32_t captureId) | Cancels a capture. | + | int32_t ChangeToOfflineStream(const std::vector& streamIds,
const sptr& callbackObj,
sptr& offlineOperator) | Changes a stream into an offline stream. | - istream_operator_callback.h - | API | Description | - | ------------------------------------------------------------ | ---------------------------------------- | - | void OnCaptureStarted(int32_t captureId, const std::vector &streamIds) | Called when a capture starts. | - | void OnCaptureEnded(int32_t captureId,
const std::vector> &infos) | Called when a capture ends. | - | void OnCaptureError(int32_t captureId,
const std::vector> &infos) | Called when an error occurs during the capture.| - | void OnFrameShutter(int32_t captureId,
const std::vector &streamIds, uint64_t timestamp) | Called when a frame is captured. | + | API | Description | + | ------------------------------------------------------------ | ----------------------------------------------- | + | int32_t OnCaptureStarted(int32_t captureId, const std::vector& streamIds) | Called when a capture starts. | + | int32_t OnCaptureEnded(int32_t captureId, const std::vector& infos) | Called when a capture ends. | + | int32_t OnCaptureError(int32_t captureId, const std::vector& infos) | Called when an error occurs during the capture. | + | int32_t OnFrameShutter(int32_t captureId, const std::vector& streamIds, uint64_t timestamp) | Called when a frame is captured. | -### How to Develop -To camera driver development procedure is as follows: +### How to Develop +The camera driver development procedure is as follows: -1. Register a **CameraHost**. +1. Register a **CameraHost** instance. - Define the **HdfDriverEntry** structure to define the method for initializing a **CameraHost**. - ``` + Define the **HdfDriverEntry** structure to define the method for initializing **CameraHost**. For details about the code, see **drivers/peripheral/camera/interfaces/hdi_ipc/camera_host_driver.cpp**. + ```c++ struct HdfDriverEntry g_cameraHostDriverEntry = { .moduleVersion = 1, .moduleName = "camera_service", @@ -133,35 +142,48 @@ To camera driver development procedure is as follows: HDF_INIT(g_cameraHostDriverEntry); // Register the HdfDriverEntry structure with the HDF. ``` -2. Initialize the **CameraHost**. +2. Initialize the **CameraHost** service. - **HdfCameraHostDriverBind** defined in the **HdfDriverEntry** structure provides the registration of **CameraServiceDispatch()** and **CameraHostStubInstance()**. **CameraServiceDispatch()** is used to remotely call a method of the **CameraHost**, such as **OpenCamera()** and **SetFlashlight()**. **CameraHostStubInstance()** is used to initialize the camera device, which is called during system startup. + The **HdfCameraHostDriverBind()** method defined in the **HdfDriverEntry** structure registers **CameraServiceDispatch()** and **CameraHostStubInstance()**. **CameraServiceDispatch()** is used to remotely call the **CameraHost** methods, such as **OpenCamera()** and **SetFlashlight()**. **CameraHostStubInstance()** is called during the system startup to initialize the camera. - ``` - int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) + ```c++ + static int HdfCameraHostDriverBind(struct HdfDeviceObject *deviceObject) { - HDF_LOGI("HdfCameraHostDriverBind enter!"); - if (deviceObject == nullptr) { - HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !"); + HDF_LOGI("HdfCameraHostDriverBind enter"); + + auto *hdfCameraHostHost = new (std::nothrow) HdfCameraHostHost; + if (hdfCameraHostHost == nullptr) { + HDF_LOGE("%{public}s: failed to create HdfCameraHostHost object", __func__); + return HDF_FAILURE; + } + + hdfCameraHostHost->ioService.Dispatch = CameraHostDriverDispatch; // Provide a method to remotely call a CameraHost method. + hdfCameraHostHost->ioService.Open = NULL; + hdfCameraHostHost->ioService.Release = NULL; + + auto serviceImpl = ICameraHost::Get(true); + if (serviceImpl == nullptr) { + HDF_LOGE("%{public}s: failed to get of implement service", __func__); + delete hdfCameraHostHost; return HDF_FAILURE; } - HdfCameraService *hdfCameraService = reinterpret_cast(OsalMemAlloc(sizeof(HdfCameraService))); - if (hdfCameraService == nullptr) { - HDF_LOGE("HdfCameraHostDriverBind OsalMemAlloc HdfCameraService failed!"); + + hdfCameraHostHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl, + ICameraHost::GetDescriptor()); // Initialize the camera. + if (hdfCameraHostHost->stub == nullptr) { + HDF_LOGE("%{public}s: failed to get stub object", __func__); + delete hdfCameraHostHost; return HDF_FAILURE; } - hdfCameraService->ioservice.Dispatch = CameraServiceDispatch; // Used to call methods of the CameraHost. - hdfCameraService->ioservice.Open = nullptr; - hdfCameraService->ioservice.Release = nullptr; - hdfCameraService->instance = CameraHostStubInstance(); // Initialize the camera device. - deviceObject->service = &hdfCameraService->ioservice; + + deviceObject->service = &hdfCameraHostHost->ioService; return HDF_SUCCESS; } ``` The following functions are the implementation of the methods of the **CameraHost**: - ``` + ```c++ int32_t CameraHostStub::CameraHostServiceStubOnRemoteRequest(int cmdId, MessageParcel &data, MessageParcel &reply, MessageOption &option) { @@ -192,11 +214,11 @@ To camera driver development procedure is as follows: **CameraHostStubInstance()** finally calls **CameraHostImpl::Init()** to obtain the physical camera and initialize the DeviceManager and PipelineCore modules. -3. Obtain the **CameraHost**. +3. Obtain the **CameraHost** service. - Call the **Get()** interface to obtain the **CameraHost** from the **CameraService**. The **Get()** interface is as follows: + Use **Get()** to obtain the **CameraHost** from the **CameraService**. The **Get()** method is as follows: - ``` + ```c++ sptr ICameraHost::Get(const char *serviceName) { do { @@ -218,51 +240,80 @@ To camera driver development procedure is as follows: } ``` -4. Implement the **OpenCamera\(\)** interface. +4. Open a camera device. - The **CameraHostProxy** class provides five interfaces: **SetCallback()**, **GetCameraIds()**, **GetCameraAbility()**, **OpenCamera()**, and **SetFlashlight()**. The following describes **OpenCamera()**. - The **OpenCamera()** interface calls the remote **CameraHostStubOpenCamera()** interface through the CMD_CAMERA_HOST_OPEN_CAMERA to obtain an **ICameraDevice** object. + The **CameraHostProxy** class provides **SetCallback()**, **GetCameraIds()**, **GetCameraAbility()**, **OpenCamera()**, and **SetFlashlight()**. - ``` - CamRetCode CameraHostProxy::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &pDevice) + Use **OpenCamera()** to call the remote **CameraHostStubOpenCamera()** through the **CMD_CAMERA_HOST_OPEN_CAMERA** to obtain an **ICameraDevice** object. + + ```c++ + int32_t CameraHostProxy::OpenCamera(const std::string& cameraId, const sptr& callbackObj, + sptr& device) { - int32_t ret = Remote()->SendRequest(CMD_CAMERA_HOST_REMOTE_OPEN_CAMERA, data, reply, option); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%{public}s: SendRequest failed, error code is %{public}d", __func__, ret); - return INVALID_ARGUMENT; + MessageParcel cameraHostData; + MessageParcel cameraHostReply; + MessageOption cameraHostOption(MessageOption::TF_SYNC); + + if (!cameraHostData.WriteInterfaceToken(ICameraHost::GetDescriptor())) { + HDF_LOGE("%{public}s: failed to write interface descriptor!", __func__); + return HDF_ERR_INVALID_PARAM; } - CamRetCode retCode = static_cast(reply.ReadInt32()); - bool flag = reply.ReadBool(); - if (flag) { - sptr remoteCameraDevice = reply.ReadRemoteObject(); - if (remoteCameraDevice == nullptr) { - HDF_LOGE("%{public}s: CameraHostProxy remoteCameraDevice is null", __func__); - } - pDevice = OHOS::iface_cast(remoteCameraDevice); + + if (!cameraHostData.WriteCString(cameraId.c_str())) { + HDF_LOGE("%{public}s: write cameraId failed!", __func__); + return HDF_ERR_INVALID_PARAM; + } + + if (!cameraHostData.WriteRemoteObject(OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(callbackObj, + ICameraDeviceCallback::GetDescriptor()))) { + HDF_LOGE("%{public}s: write callbackObj failed!", __func__); + return HDF_ERR_INVALID_PARAM; + } + + int32_t cameraHostRet = Remote()->SendRequest(CMD_CAMERA_HOST_OPEN_CAMERA, cameraHostData, cameraHostReply, cameraHostOption); + if (cameraHostRet != HDF_SUCCESS) { + HDF_LOGE("%{public}s failed, error code is %{public}d", __func__, cameraHostRet); + return cameraHostRet; } - return retCode; + + device = hdi_facecast(cameraHostReply.ReadRemoteObject()); + + return cameraHostRet; } ``` - **Remote()->SendRequest** calls **CameraHostServiceStubOnRemoteRequest()**, enters the **CameraHostStubOpenCamera()** interface based on **cmdId**, and finally calls **CameraHostImpl::OpenCamera()** to obtain a **CameraDevice** and power on the camera hardware. + **Remote()->SendRequest** calls **CameraHostServiceStubOnRemoteRequest()**, locates **CameraHostStubOpenCamera()** based on **cmdId**, and finally calls **CameraHostImpl::OpenCamera()** to obtain a **CameraDevice** and power on the camera hardware. - ``` - CamRetCode CameraHostImpl::OpenCamera(const std::string &cameraId, const OHOS::sptr &callback, OHOS::sptr &device) + ```c++ + int32_t CameraHostImpl::OpenCamera(const std::string& cameraId, const sptr& callbackObj, + sptr& device) { - std::shared_ptr cameraDevice = std::static_pointer_cast(itr->second); + CAMERA_LOGD("OpenCamera entry"); + DFX_LOCAL_HITRACE_BEGIN; + if (CameraIdInvalid(cameraId) != RC_OK || callbackObj == nullptr) { + CAMERA_LOGW("open camera id is empty or callback is null."); + return INVALID_ARGUMENT; + } + + auto itr = cameraDeviceMap_.find(cameraId); + if (itr == cameraDeviceMap_.end()) { + CAMERA_LOGE("camera device not found."); + return INSUFFICIENT_RESOURCES; + } + CAMERA_LOGD("OpenCamera cameraId find success."); + + std::shared_ptr cameraDevice = itr->second; if (cameraDevice == nullptr) { CAMERA_LOGE("camera device is null."); return INSUFFICIENT_RESOURCES; } - CamRetCode ret = cameraDevice->SetCallback(callback); - if (ret != NO_ERROR) { - CAMERA_LOGW("set camera device callback failed."); - return ret; - } + + CamRetCode ret = cameraDevice->SetCallback(callbackObj); + CHECK_IF_NOT_EQUAL_RETURN_VALUE(ret, HDI::Camera::V1_0::NO_ERROR, ret); + CameraHostConfig *config = CameraHostConfig::GetInstance(); - if (config == nullptr) { - return INVALID_ARGUMENT; - } + CHECK_IF_PTR_NULL_RETURN_VALUE(config, INVALID_ARGUMENT); + std::vector phyCameraIds; RetCode rc = config->GetPhysicCameraIds(cameraId, phyCameraIds); if (rc != RC_OK) { @@ -274,214 +325,333 @@ To camera driver development procedure is as follows: CameraPowerDown(phyCameraIds); return DEVICE_ERROR; } - + auto sptrDevice = deviceBackup_.find(cameraId); if (sptrDevice == deviceBackup_.end()) { + #ifdef CAMERA_BUILT_ON_OHOS_LITE + deviceBackup_[cameraId] = cameraDevice; + #else deviceBackup_[cameraId] = cameraDevice.get(); + #endif } device = deviceBackup_[cameraId]; cameraDevice->SetStatus(true); - return NO_ERROR; + CAMERA_LOGD("open camera success."); + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; } ``` -5. Implement the **GetStreamOperator\(\)** interface. +5. Obtain streams. - **CameraDeviceImpl** defines interfaces such as **GetStreamOperator()**, **UpdateSettings()**, **SetResultMode()**, and **GetEnabledResult()**. The following is an example of implementing the **GetStreamOperator()** interface: + **CameraDeviceImpl** defines **GetStreamOperator()**, **UpdateSettings()**, **SetResultMode()**, and **GetEnabledResult()**. Use **GetStreamOperator()** to obtain steams. - ``` - CamRetCode CameraDeviceImpl::GetStreamOperator(const OHOS::sptr &callback, - OHOS::sptr &streamOperator) + ```c++ + int32_t CameraDeviceImpl::GetStreamOperator(const sptr& callbackObj, + sptr& streamOperator) { - if (callback == nullptr) { + HDI_DEVICE_PLACE_A_WATCHDOG; + DFX_LOCAL_HITRACE_BEGIN; + if (callbackObj == nullptr) { CAMERA_LOGW("input callback is null."); return INVALID_ARGUMENT; } - spCameraDeviceCallback_ = callback; + + spCameraDeciceCallback_ = callbackObj; if (spStreamOperator_ == nullptr) { - // Here, an spStreamOperator object is created and passed to the caller for stream operations. - spStreamOperator_ = new(std::nothrow) StreamOperatorImpl(spCameraDeviceCallback_, shared_from_this()); + #ifdef CAMERA_BUILT_ON_OHOS_LITE + // Create a spStreamOperator_ object and pass it to the caller for subsequent stream operations. + spStreamOperator_ = std::make_shared(spCameraDeciceCallback_, shared_from_this()); + #else + spStreamOperator_ = new(std::nothrow) StreamOperator(spCameraDeciceCallback_, shared_from_this()); + #endif if (spStreamOperator_ == nullptr) { CAMERA_LOGW("create stream operator failed."); return DEVICE_ERROR; } + spStreamOperator_->Init(); ismOperator_ = spStreamOperator_; } streamOperator = ismOperator_; - - spStreamOperator_->SetRequestCallback([this](){ - spCameraDeviceCallback_->OnError(REQUEST_TIMEOUT, 0); + #ifndef CAMERA_BUILT_ON_OHOS_LITE + CAMERA_LOGI("CameraDeviceImpl %{public}s: line: %{public}d", __FUNCTION__, __LINE__); + pipelineCore_->GetStreamPipelineCore()->SetCallback( + [this](const std::shared_ptr &metadata) { + OnMetadataChanged(metadata); }); + #endif + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; } ``` -6. Create a stream. +6. Create streams. - Fill in the **StreamInfo** structure before creating a stream by calling **CreateStreams()**. + Fill in the **StreamInfo** structure before creating streams by calling **CreateStreams()**. - ``` + ```c++ using StreamInfo = struct _StreamInfo { int streamId_; - int width_; // Stream width - int height_; // Stream height - int format_; // Stream format, for example, PIXEL_FMT_YCRCB_420_SP + int width_; // Stream width + int height_; // Stream height + int format_; // Stream format, for example, PIXEL_FMT_YCRCB_420_SP int dataSpace_; - StreamIntent intent_; // StreamIntent, for example, PREVIEW + StreamIntent intent_; // StreamIntent, for example, PREVIEW bool tunneledMode_; - OHOS::sptr bufferQueue_; // The stream buffer queue can be created by using the streamCustomer->CreateProducer() interface. + ufferProducerSequenceable bufferQueue_; // Use streamCustomer->CreateProducer() to create a buffer queue for streams. int minFrameDuration_; EncodeType encodeType_; }; ``` - The **CreateStreams()** interface in the **StreamOperatorImpl** class is used to create a **StreamBase** instance, which can then be used to initialize operations such as **CreateBufferPool()** by using the **init()** interface. + **CreateStreams()** is a method in the **StreamOperator** class (**StreamOperatorImpl** is the base class of **StreamOperator**). Use **CreateStreams()** to create a **StreamBase** object, which initializes operations such as **CreateBufferPool** through its **Init()** method. - ``` - RetCode StreamOperatorImpl::CreateStream(const std::shared_ptr& streamInfo) + ```c++ + int32_t StreamOperator::CreateStreams(const std::vector& streamInfos) { - static std::map typeMap = { - {PREVIEW, "PREVIEW"}, - {VIDEO, "VIDEO"}, - {STILL_CAPTURE, "STILL_CAPTURE"}, - {POST_VIEW, "POST_VIEW"}, {ANALYZE, "ANALYZE"}, - {CUSTOM, "CUSTOM"} - }; - - auto itr = typeMap.find(streamInfo->intent_); - if (itr == typeMap.end()) { - CAMERA_LOGE("do not support stream type. [type = %{public}d]", streamInfo->intent_); - return RC_ERROR; + PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_); + DFX_LOCAL_HITRACE_BEGIN; + for (const auto& it : streamInfos) { + CHECK_IF_NOT_EQUAL_RETURN_VALUE(CheckStreamInfo(it), true, INVALID_ARGUMENT); + CAMERA_LOGI("streamId:%{public}d and format:%{public}d and width:%{public}d and height:%{public}d", + it.streamId_, it.format_, it.width_, it.height_); + if (streamMap_.count(it.streamId_) > 0) { + CAMERA_LOGE("stream [id = %{public}d] has already been created.", it.streamId_); + return INVALID_ARGUMENT; + } + std::shared_ptr stream = StreamFactory::Instance().CreateShared( // Create a stream instance. + IStream::g_availableStreamType[it.intent_], it.streamId_, it.intent_, pipelineCore_, messenger_); + if (stream == nullptr) { + CAMERA_LOGE("create stream [id = %{public}d] failed.", it.streamId_); + return INSUFFICIENT_RESOURCES; + } + StreamConfiguration scg; + StreamInfoToStreamConfiguration(scg, it); + RetCode rc = stream->ConfigStream(scg); + if (rc != RC_OK) { + CAMERA_LOGE("configure stream %{public}d failed", it.streamId_); + return INVALID_ARGUMENT; + } + if (!scg.tunnelMode && (it.bufferQueue_)->producer_ != nullptr) { + CAMERA_LOGE("stream [id:%{public}d] is not tunnel mode, can't bind a buffer producer", it.streamId_); + return INVALID_ARGUMENT; + } + if ((it.bufferQueue_)->producer_ != nullptr) { + auto tunnel = std::make_shared(); + CHECK_IF_PTR_NULL_RETURN_VALUE(tunnel, INSUFFICIENT_RESOURCES); + rc = tunnel->AttachBufferQueue((it.bufferQueue_)->producer_); + CHECK_IF_NOT_EQUAL_RETURN_VALUE(rc, RC_OK, INVALID_ARGUMENT); + if (stream->AttachStreamTunnel(tunnel) != RC_OK) { + CAMERA_LOGE("attach buffer queue to stream [id = %{public}d] failed", it.streamId_); + return INVALID_ARGUMENT; + } + } + { + std::lock_guard l(streamLock_); + streamMap_[stream->GetStreamId()] = stream; + } + CAMERA_LOGI("create stream success [id:%{public}d] [type:%{public}s]", stream->GetStreamId(), + IStream::g_availableStreamType[it.intent_].c_str()); } - std::shared_ptr stream = StreamFactory::Instance().CreateShared(itr->second); // Create a StreamBase instance. - RetCode rc = stream->Init(streamInfo); - return RC_OK; - } + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; + } ``` -7. Configure the stream. +7. Configure streams. - Use the **CommitStreams()** interface to configure the stream, including PipelineCore initialization and creation. It must be called after the stream is created. + Use **CommitStreams()** to configure streams, including initializing and creating **PipelineCore**. **CommitStreams()** must be called after streams are created. - ``` - CamRetCode StreamOperatorImpl::CommitStreams(OperationMode mode, const std::shared_ptr& modeSetting) + ```c++ + int32_t StreamOperator::CommitStreams(OperationMode mode, const std::vector& modeSetting) { - auto cameraDevice = cameraDevice_.lock(); - if (cameraDevice == nullptr) { - CAMERA_LOGE("camera device closed."); - return CAMERA_CLOSED; - } - std::shared_ptr PipelineCore = - std::static_pointer_cast(cameraDevice)->GetPipelineCore(); - if (PipelineCore == nullptr) { - CAMERA_LOGE("Failed to obtain PipelineCore."); - return CAMERA_CLOSED; + CAMERA_LOGV("enter"); + CHECK_IF_PTR_NULL_RETURN_VALUE(streamPipeline_, DEVICE_ERROR); + PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_); + if (modeSetting.empty()) { + CAMERA_LOGE("input vector is empty"); + return INVALID_ARGUMENT; } + DFX_LOCAL_HITRACE_BEGIN; - streamPipeCore_ = PipelineCore->GetStreamPipelineCore(); - if (streamPipeCore_ == nullptr) { - CAMERA_LOGE("Failed to obtain the stream PipelineCore."); - return DEVICE_ERROR; + std::vector configs = {}; + { + std::lock_guard l(streamLock_); + std::transform(streamMap_.begin(), streamMap_.end(), std::back_inserter(configs), + [](auto &iter) { return iter.second->GetStreamAttribute(); }); } - - RetCode rc = streamPipeCore_->Init(); // Initialize the PipelineCore. + + std::shared_ptr setting; + MetadataUtils::ConvertVecToMetadata(modeSetting, setting); + DynamicStreamSwitchMode method = streamPipeline_->CheckStreamsSupported(mode, setting, configs); + if (method == DYNAMIC_STREAM_SWITCH_NOT_SUPPORT) { + return INVALID_ARGUMENT; + } + if (method == DYNAMIC_STREAM_SWITCH_NEED_INNER_RESTART) { + std::lock_guard l(streamLock_); + for (auto it : streamMap_) { + it.second->StopStream(); + } + } + { + std::lock_guard l(streamLock_); + for (auto it : streamMap_) { + if (it.second->CommitStream() != RC_OK) { + CAMERA_LOGE("commit stream [id = %{public}d] failed.", it.first); + return DEVICE_ERROR; + } + } + } + RetCode rc = streamPipeline_->PreConfig(setting); // Configure the device stream. if (rc != RC_OK) { - CAMERA_LOGE("Failed to initialize the stream PipelineCore."); + CAMERA_LOGE("prepare mode settings failed"); return DEVICE_ERROR; } - rc = streamPipeCore_->CreatePipeline(mode); // Create a pipeline. + rc = streamPipeline_->CreatePipeline(mode); // Create a pipeline. if (rc != RC_OK) { - CAMERA_LOGE("Failed to create pipeline."); + CAMERA_LOGE("create pipeline failed."); return INVALID_ARGUMENT; } - return NO_ERROR; + + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; } ``` 8. Capture images. - Fill in the **CaptureInfo** structure before calling the **Capture()** method. + Fill in the **CaptureInfo** structure before calling **Capture()**. - ``` + ```c++ using CaptureInfo = struct _CaptureInfo { - std::vector streamIds_; // IDs of streams to be captured - std::shared_ptr captureSetting_; // Camera ability can be obtained through the GetCameraAbility() interface of CameraHost. - bool enableShutterCallback_; + int[] streamIds_; // IDs of the streams to capture. + unsigned char[] captureSetting_; // Use the camera ability obtained by GetCameraAbility() of CameraHost to fill in the settings. + bool enableShutterCallback_; }; ``` - Use the **Capture()** interface in **StreamOperatorImpl** to call the **CreateCapture()** interface to capture streams. + Use the **Capture()** method in **StreamOperator** to capture data streams. - ``` - CamRetCode StreamOperatorImpl::Capture(int captureId, const std::shared_ptr& captureInfo, bool isStreaming) + ```c++ + int32_t StreamOperator::Capture(int32_t captureId, const CaptureInfo& info, bool isStreaming) { - if (!ValidCaptureInfo(captureId, captureInfo)) { - CAMERA_LOGE("capture streamIds is empty. [captureId = %d]", captureId); - return INVALID_ARGUMENT; - } - std::shared_ptr cameraCapture = nullptr; - RetCode rc = CreateCapture(captureId, captureInfo, isStreaming, cameraCapture); - if (rc != RC_OK) { - CAMERA_LOGE("create capture is failed."); - return DEVICE_ERROR; + CHECK_IF_EQUAL_RETURN_VALUE(captureId < 0, true, INVALID_ARGUMENT); + PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_); + DFX_LOCAL_HITRACE_BEGIN; + + for (auto id : info.streamIds_) { + std::lock_guard l(streamLock_); + auto it = streamMap_.find(id); + if (it == streamMap_.end()) { + return INVALID_ARGUMENT; + } } - + { - std::unique_lock lock(captureMutex_); - camerCaptureMap_.insert(std::make_pair(captureId, cameraCapture)); + std::lock_guard l(requestLock_); + auto itr = requestMap_.find(captureId); + if (itr != requestMap_.end()) { + return INVALID_ARGUMENT; + } } - - rc = StartThread(); - if (rc != RC_OK) { - CAMERA_LOGE("preview start failed."); - return DEVICE_ERROR; + + std::shared_ptr captureSetting; + MetadataUtils::ConvertVecToMetadata(info.captureSetting_, captureSetting); + CaptureSetting setting = captureSetting; + auto request = + std::make_shared(captureId, info.streamIds_.size(), setting, + info.enableShutterCallback_, isStreaming); + for (auto id : info.streamIds_) { + RetCode rc = streamMap_[id]->AddRequest(request); + if (rc != RC_OK) { + return DEVICE_ERROR; + } } - return NO_ERROR; + + { + std::lock_guard l(requestLock_); + requestMap_[captureId] = request; + } + return HDI::Camera::V1_0::NO_ERROR; } ``` 9. Cancel the capture and release the offline stream. - Use the **CancelCapture()** interface in the **StreamOperatorImpl** class to cancel the stream capture based on **captureId**. + Use **CancelCapture()** in the **StreamOperatorImpl** class to cancel the stream capture based on **captureId**. - ``` - CamRetCode StreamOperatorImpl::CancelCapture(int captureId) + ```c++ + int32_t StreamOperator::CancelCapture(int32_t captureId) { - auto itr = camerCaptureMap_.find(captureId); // Search for the CameraCapture object in the Map based on the captureId. - RetCode rc = itr->second->Cancel(); // Call the Cancel() interface in CameraCapture to cancel the stream capture. - std::unique_lock lock(captureMutex_); - camerCaptureMap_.erase(itr); // Erase the CameraCapture object. - return NO_ERROR; + CHECK_IF_EQUAL_RETURN_VALUE(captureId < 0, true, INVALID_ARGUMENT); + PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_); + DFX_LOCAL_HITRACE_BEGIN; + + std::lock_guard l(requestLock_); + auto itr = requestMap_.find(captureId); // Search for the CameraCapture object in the Map based on the captureId. + if (itr == requestMap_.end()) { + CAMERA_LOGE("can't cancel capture [id = %{public}d], this capture doesn't exist", captureId); + return INVALID_ARGUMENT; + } + + RetCode rc = itr->second->Cancel(); // Call Cancel() in CameraCapture to cancel the stream capture. + if (rc != RC_OK) { + return DEVICE_ERROR; + } + requestMap_.erase(itr); // Erase the CameraCapture object. + + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; } ``` - Use the **ReleaseStreams()** interface in the **StreamOperatorImpl** class t release the streams created by using **CreateStream()** and **CommitStreams()** and destroy the pipeline. + Use **ReleaseStreams()** in the **StreamOperatorImpl** class to release the streams created by using **CreateStream()** and **CommitStreams()** and destroy the pipeline. - ``` - CamRetCode StreamOperatorImpl::ReleaseStreams(const std::vector& streamIds) + ```c++ + int32_t StreamOperator::ReleaseStreams(const std::vector& streamIds) { - RetCode rc = DestroyStreamPipeline(streamIds); // Destroy the pipeline based on streamIds. - rc = DestroyHostStreamMgr(streamIds); - rc = DestroyStreams(streamIds); // Destroy the stream specified by streamIds. - return NO_ERROR; + PLACE_A_NOKILL_WATCHDOG(requestTimeoutCB_); + DFX_LOCAL_HITRACE_BEGIN; + for (auto id : streamIds) { + std::lock_guard l(streamLock_); + auto it = streamMap_.find(id); + if (it == streamMap_.end()) { + continue; + } + if (it->second->IsRunning()) { + it->second->StopStream(); + } + it->second->DumpStatsInfo(); + streamMap_.erase(it); + } + + for (auto id : streamIds) { + CHECK_IF_EQUAL_RETURN_VALUE(id < 0, true, INVALID_ARGUMENT); + } + + DFX_LOCAL_HITRACE_END; + return HDI::Camera::V1_0::NO_ERROR; } ``` 10. Close the camera device. - - Use the **Close()** interface in the **CameraDeviceImpl** class to close the camera device. This interface calls **PowerDown()** in the **DeviceManager** to power off the device. -### Development Example + Use **Close()** in the **CameraDeviceImpl** class to close the camera device. The **PowerDown()** in **DeviceManager** is called to power off the device. -There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. After system startup, the executable file **ohos_camera_demo** is generated in the **/vendor/bin** directory. This demo can implement basic camera capabilities such as preview and photographing. The following uses the demo as an example to describe how to use the HDI to write the **PreviewOn()** and **CaptureON()** instances. For details, see [ohos_camera_demo](https://gitee.com/openharmony/drivers_peripheral/tree/master/camera/hal/init). +### Example -1. Construct a CameraDemo object in the **main** function. This object contains methods for initializing the camera and starting, stopping, and releasing streams. The **mainDemo->InitSensors()** function is used to initialize the **CameraHost**, and the **mainDemo->InitCameraDevice()** function is used to initialize the **CameraDevice**. +There is a [ohos_camera_demo](https://gitee.com/openharmony/drivers_peripheral/tree/master/camera/hal/init) in the **/drivers/peripheral/camera/hal/init** directory. After the system is started, the executable file **ohos_camera_demo** is generated in the **/vendor/bin** directory. This demo implements basic camera capabilities such as preview and photographing. - ``` +The following uses the demo to describe how to use the HDI to implement **PreviewOn()** and **CaptureON()**. + +1. Construct a **CameraDemo** object in the **main** function. This object contains methods for initializing the camera and starting, stopping, and releasing streams. The **mainDemo->InitSensors()** function is used to initialize the **CameraHost**, and the **mainDemo->InitCameraDevice()** function is used to initialize the **CameraDevice**. + + ```c++ int main(int argc, char** argv) { RetCode rc = RC_OK; auto mainDemo = std::make_shared(); - rc = mainDemo->InitSensors(); // Initialize the CameraHost. + rc = mainDemo->InitSensors(); // Initialize the CameraHost. if (rc == RC_ERROR) { CAMERA_LOGE("main test: mainDemo->InitSensors() error\n"); return -1; @@ -493,13 +663,13 @@ There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. return -1; } - rc = PreviewOn(0, mainDemo); // Configure and enable streams. + rc = PreviewOn(0, mainDemo); // Configure and enable streams. if (rc != RC_OK) { CAMERA_LOGE("main test: PreviewOn() error demo exit"); return -1; } - ManuList(mainDemo, argc, argv); // Print the menu to the console. + ManuList(mainDemo, argc, argv); // Print the menu to the console. return RC_OK; } @@ -507,190 +677,309 @@ There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. The function used to initialize the **CameraHost** is implemented as follows, where the HDI **ICameraHost::Get()** is called to obtain the **demoCameraHost** and set the callback: - ``` - RetCode CameraDemo::InitSensors() + ```c++ + RetCode OhosCameraDemo::InitSensors() { - demoCameraHost_ = ICameraHost::Get(DEMO_SERVICE_NAME); + int rc = 0; + + CAMERA_LOGD("demo test: InitSensors enter"); + + if (demoCameraHost_ != nullptr) { + return RC_OK; + } + #ifdef CAMERA_BUILT_ON_OHOS_LITE + demoCameraHost_ = OHOS::Camera::CameraHost::CreateCameraHost(); + #else + constexpr const char *DEMO_SERVICE_NAME = "camera_service"; + demoCameraHost_ = ICameraHost::Get(DEMO_SERVICE_NAME, false); + #endif if (demoCameraHost_ == nullptr) { CAMERA_LOGE("demo test: ICameraHost::Get error"); return RC_ERROR; } - - hostCallback_ = new CameraHostCallback(); + + #ifdef CAMERA_BUILT_ON_OHOS_LITE + hostCallback_ = std::make_shared(); + #else + hostCallback_ = new DemoCameraHostCallback(); + #endif rc = demoCameraHost_->SetCallback(hostCallback_); + if (rc != HDI::Camera::V1_0::NO_ERROR) { + CAMERA_LOGE("demo test: demoCameraHost_->SetCallback(hostCallback_) error"); + return RC_ERROR; + } + + CAMERA_LOGD("demo test: InitSensors exit"); + return RC_OK; } ``` - The implementation of the function for initializing the **CameraDevice** is as follows, where the **GetCameraIds(cameraIds_)**, **GetCameraAbility(cameraId, ability_)**, and **OpenCamera(cameraIds_.front(), callback, demoCameraDevice_)** interfaces are called to obtain the **demoCameraHost**. + The function for initializing the **CameraDevice** is implemented as follows. The **GetCameraIds(cameraIds_)**, **GetCameraAbility(cameraId, ability_)**, and **OpenCamera(cameraIds\_.front(), callback, demoCameraDevice_)** methods are used to obtain the **demoCameraHost**. - ``` - RetCode CameraDemo::InitCameraDevice() + ```c++ + RetCode OhosCameraDemo::InitCameraDevice() { + int rc = 0; + + CAMERA_LOGD("demo test: InitCameraDevice enter"); + + if (demoCameraHost_ == nullptr) { + CAMERA_LOGE("demo test: InitCameraDevice demoCameraHost_ == nullptr"); + return RC_ERROR; + } + (void)demoCameraHost_->GetCameraIds(cameraIds_); + if (cameraIds_.empty()) { + return RC_ERROR; + } const std::string cameraId = cameraIds_.front(); - demoCameraHost_->GetCameraAbility(cameraId, ability_); - - sptr callback = new CameraDeviceCallback(); + demoCameraHost_->GetCameraAbility(cameraId, cameraAbility_); + + MetadataUtils::ConvertVecToMetadata(cameraAbility_, ability_); + + GetFaceDetectMode(ability_); + GetFocalLength(ability_); + GetAvailableFocusModes(ability_); + GetAvailableExposureModes(ability_); + GetExposureCompensationRange(ability_); + GetExposureCompensationSteps(ability_); + GetAvailableMeterModes(ability_); + GetAvailableFlashModes(ability_); + GetMirrorSupported(ability_); + GetStreamBasicConfigurations(ability_); + GetFpsRange(ability_); + GetCameraPosition(ability_); + GetCameraType(ability_); + GetCameraConnectionType(ability_); + GetFaceDetectMaxNum(ability_); + + #ifdef CAMERA_BUILT_ON_OHOS_LITE + std::shared_ptr callback = std::make_shared(); + #else + sptr callback = new DemoCameraDeviceCallback(); + #endif rc = demoCameraHost_->OpenCamera(cameraIds_.front(), callback, demoCameraDevice_); + if (rc != HDI::Camera::V1_0::NO_ERROR || demoCameraDevice_ == nullptr) { + CAMERA_LOGE("demo test: InitCameraDevice OpenCamera failed"); + return RC_ERROR; + } + + CAMERA_LOGD("demo test: InitCameraDevice exit"); + return RC_OK; } ``` -2. Implement the **PreviewOn()** interface to configure streams, enable preview streams, and start stream capture. After this interface is called, the camera preview channel starts running. Two streams are enabled: preview stream and capture or video stream. Only the preview stream will be captured. - - ``` - static RetCode PreviewOn(int mode, const std::shared_ptr& mainDemo) - { - rc = mainDemo->StartPreviewStream(); // Configure the preview stream. - if (mode == 0) { - rc = mainDemo->StartCaptureStream(); // Configure the capture stream. - } else { - rc = mainDemo->StartVideoStream(); // Configure the video stream. - } - - rc = mainDemo->CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW); // Capture the preview stream. - return RC_OK; - } - ``` +2. Implement **PreviewOn()** to configure streams, enable preview streams, and start stream capture. - The **StartCaptureStream()**, **StartVideoStream()**, and **StartPreviewStream()** interfaces call the **CreateStream()** interface with different input parameters. + After **PreviewOn()** is called, the camera preview channel starts running. Two streams are enabled: preview stream and capture or video stream. Only the preview stream will be captured. - ``` - RetCode CameraDemo::StartVideoStream() + ```c++ + static RetCode PreviewOn(int mode, const std::shared_ptr& mainDemo) { RetCode rc = RC_OK; - if (isVideoOn_ == 0) { - isVideoOn_ = 1; - rc = CreateStream(STREAM_ID_VIDEO, streamCustomerVideo_, VIDEO); // To enable the preview stream or capture stream, change the input parameters. + CAMERA_LOGD("main test: PreviewOn enter"); + + rc = mainDemo->StartPreviewStream(); // Configure the preview stream. + if (rc != RC_OK) { + CAMERA_LOGE("main test: PreviewOn StartPreviewStream error"); + return RC_ERROR; } + + if (mode == 0) { + rc = mainDemo->StartCaptureStream(); // Configure the capture stream. + if (rc != RC_OK) { + CAMERA_LOGE("main test: PreviewOn StartCaptureStream error"); + return RC_ERROR; + } + } else { + rc = mainDemo->StartVideoStream(); // Configure the video stream. + if (rc != RC_OK) { + CAMERA_LOGE("main test: PreviewOn StartVideoStream error"); + return RC_ERROR; + } + } + + rc = mainDemo->CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW); + if (rc != RC_OK) { + CAMERA_LOGE("main test: PreviewOn mainDemo->CaptureON() preview error"); + return RC_ERROR; + } + + CAMERA_LOGD("main test: PreviewOn exit"); return RC_OK; - } + } ``` - The **CreateStream()** interface calls the HDI to configure and create a stream. Specifically, the interface first calls the HDI to obtain a **StreamOperation** object and then creates a **StreamInfo** object. Call **CreateStreams()** and **CommitStreams()** to create and configure a stream. + The **StartCaptureStream()**, **StartVideoStream()**, and **StartPreviewStream()** methods call **CreateStream()** with different input parameters. - ``` - RetCode CameraDemo::CreateStreams(const int streamIdSecond, StreamIntent intent) + Use **CreateStream()** to call an HDI API to configure and create streams. Specifically, **CreateStream()** calls the HDI to obtain a **StreamOperation** object and then creates a **StreamInfo** object. Call **CreateStreams()** and **CommitStreams()** to create and configure streams. + + ```c++ + RetCode OhosCameraDemo::CreateStream(const int streamId, std::shared_ptr &streamCustomer, + StreamIntent intent) { - std::vector> streamInfos; - std::vector>().swap(streamInfos); + int rc = 0; + CAMERA_LOGD("demo test: CreateStream enter"); + GetStreamOpt(); // Obtain a StreamOperator object. - std::shared_ptr previewStreamInfo = std::make_shared(); - SetStreamInfo(previewStreamInfo, streamCustomerPreview_, STREAM_ID_PREVIEW, PREVIEW); // Fill in the StreamInfo. - if (previewStreamInfo->bufferQueue_ == nullptr) { - CAMERA_LOGE("demo test: CreateStream CreateProducer(); is nullptr\n"); + if (streamOperator_ == nullptr) { + CAMERA_LOGE("demo test: CreateStream GetStreamOpt() is nullptr\n"); return RC_ERROR; } - streamInfos.push_back(previewStreamInfo); - - std::shared_ptr secondStreamInfo = std::make_shared(); - if (streamIdSecond == STREAM_ID_CAPTURE) { - SetStreamInfo(secondStreamInfo, streamCustomerCapture_, STREAM_ID_CAPTURE, intent); - } else { - SetStreamInfo(secondStreamInfo, streamCustomerVideo_, STREAM_ID_VIDEO, intent); - } - - if (secondStreamInfo->bufferQueue_ == nullptr) { - CAMERA_LOGE("demo test: CreateStreams CreateProducer() secondStreamInfo is nullptr\n"); + + StreamInfo streamInfo = {0}; + + SetStreamInfo(streamInfo, streamCustomer, streamId, intent); // Fills in the StreamInfo stream. + if (streamInfo.bufferQueue_->producer_ == nullptr) { + CAMERA_LOGE("demo test: CreateStream CreateProducer(); is nullptr\n"); return RC_ERROR; } - streamInfos.push_back(secondStreamInfo); - + + std::vector streamInfos; + streamInfos.push_back(streamInfo); + rc = streamOperator_->CreateStreams(streamInfos); // Create a stream. - if (rc != Camera::NO_ERROR) { + if (rc != HDI::Camera::V1_0::NO_ERROR) { CAMERA_LOGE("demo test: CreateStream CreateStreams error\n"); return RC_ERROR; } - rc = streamOperator_->CommitStreams(Camera::NORMAL, ability_); - if (rc != Camera::NO_ERROR) { + rc = streamOperator_->CommitStreams(NORMAL, cameraAbility_); + if (rc != HDI::Camera::V1_0::NO_ERROR) { CAMERA_LOGE("demo test: CreateStream CommitStreams error\n"); - std::vector streamIds = {STREAM_ID_PREVIEW, streamIdSecond}; + std::vector streamIds; + streamIds.push_back(streamId); streamOperator_->ReleaseStreams(streamIds); return RC_ERROR; } + + CAMERA_LOGD("demo test: CreateStream exit"); + return RC_OK; } ``` - The **CaptureON()** interface calls the **Capture()** interface of **StreamOperator** to obtain camera data, rotate the buffer, and start a thread to receive data of the corresponding type. + Use **CaptureON()** to call the **Capture()** method of **StreamOperator** to obtain camera data, flip the buffer, and start a thread to receive data of the corresponding type. - ``` - RetCode CameraDemo::CaptureON(const int streamId, const int captureId, CaptureMode mode) + ```c++ + RetCode OhosCameraDemo::CaptureON(const int streamId, + const int captureId, CaptureMode mode) { - std::shared_ptr captureInfo = std::make_shared(); // Create and fill in CaptureInfo. - captureInfo->streamIds_ = {streamId}; - captureInfo->captureSetting_ = ability_; - captureInfo->enableShutterCallback_ = false; - - int rc = streamOperator_->Capture(captureId, captureInfo, true);// The stream capture starts, and buffer recycling starts. + CAMERA_LOGI("demo test: CaptureON enter streamId == %{public}d and captureId == %{public}d and mode == %{public}d", + streamId, captureId, mode); + std::lock_guard l(metaDatalock_); + if (mode == CAPTURE_SNAPSHOT) { + constexpr double latitude = 27.987500; // dummy data: Qomolangma latitde + constexpr double longitude = 86.927500; // dummy data: Qomolangma longituude + constexpr double altitude = 8848.86; // dummy data: Qomolangma altitude + constexpr size_t entryCapacity = 100; + constexpr size_t dataCapacity = 2000; + captureSetting_ = std::make_shared(entryCapacity, dataCapacity); + captureQuality_ = OHOS_CAMERA_JPEG_LEVEL_HIGH; + captureOrientation_ = OHOS_CAMERA_JPEG_ROTATION_270; + mirrorSwitch_ = OHOS_CAMERA_MIRROR_ON; + gps_.push_back(latitude); + gps_.push_back(longitude); + gps_.push_back(altitude); + captureSetting_->addEntry(OHOS_JPEG_QUALITY, static_cast(&captureQuality_), + sizeof(captureQuality_)); + captureSetting_->addEntry(OHOS_JPEG_ORIENTATION, static_cast(&captureOrientation_), + sizeof(captureOrientation_)); + captureSetting_->addEntry(OHOS_CONTROL_CAPTURE_MIRROR, static_cast(&mirrorSwitch_), + sizeof(mirrorSwitch_)); + captureSetting_->addEntry(OHOS_JPEG_GPS_COORDINATES, gps_.data(), gps_.size()); + } + + std::vector setting; + MetadataUtils::ConvertMetadataToVec(captureSetting_, setting); + captureInfo_.streamIds_ = {streamId}; + if (mode == CAPTURE_SNAPSHOT) { + captureInfo_.captureSetting_ = setting; + } else { + captureInfo_.captureSetting_ = cameraAbility_; + } + captureInfo_.enableShutterCallback_ = false; + + int rc = streamOperator_->Capture(captureId, captureInfo_, true); // The capture starts, and buffer starts to flip. + if (rc != HDI::Camera::V1_0::NO_ERROR) { + CAMERA_LOGE("demo test: CaptureStart Capture error\n"); + streamOperator_->ReleaseStreams(captureInfo_.streamIds_); + return RC_ERROR; + } + if (mode == CAPTURE_PREVIEW) { - streamCustomerPreview_->ReceiveFrameOn(nullptr); // Create a preview thread to receive the passed buffers. + streamCustomerPreview_->ReceiveFrameOn(nullptr); // Create a preview thread to receive the passed buffer. } else if (mode == CAPTURE_SNAPSHOT) { - streamCustomerCapture_->ReceiveFrameOn([this](void* addr, const uint32_t size) { // Create a capture thread to receive the passed buffers through the StoreImage callback. + streamCustomerCapture_->ReceiveFrameOn([this](void* addr, const uint32_t size) { // Create a capture thread to receive the passed buffer through the StoreImage callback. StoreImage(addr, size); }); } else if (mode == CAPTURE_VIDEO) { OpenVideoFile(); - streamCustomerVideo_->ReceiveFrameOn([this](void* addr, const uint32_t size) {// Create a video thread to receive the passed buffer by calling the StoreVideo callback. + + streamCustomerVideo_->ReceiveFrameOn([this](void* addr, const uint32_t size) { // Create a video thread to receive the passed buffer through the StoreImage callback. StoreVideo(addr, size); }); } + CAMERA_LOGD("demo test: CaptureON exit"); + return RC_OK; } ``` -3. Implement the **ManuList()** function to obtain characters from the console through the **fgets()** interface. Different characters correspond to different capabilities provided by the demo, and the functionality menu is printed. +3. Implement **ManuList()** to obtain characters from the console through **fgets()**. Different characters correspond to different capabilities provided by the demo, and the functionality menu is printed. - ``` - static void ManuList(const std::shared_ptr& mainDemo, + ```c++ + static void ManuList(const std::shared_ptr& mainDemo, const int argc, char** argv) { int idx, c; - int awb = 1; - constexpr char shortOptions[] = "h:cwvaqof:"; - c = getopt_long(argc, argv, shortOptions, longOptions, &idx); - while(1) { + bool isAwb = true; + const char *shortOptions = "h:cwvaeqof:"; + c = getopt_long(argc, argv, shortOptions, LONG_OPTIONS, &idx); + while (1) { switch (c) { case 'h': - c = PutMenuAndGetChr(); // Print the menu. + c = PutMenuAndGetChr(); // Print the menu. break; - - case 'f': - FlashLightTest(mainDemo); // Test the flashlight capability. + case 'f': + FlashLightTest(mainDemo); // Verify the flashlight capability. c = PutMenuAndGetChr(); break; case 'o': - OfflineTest(mainDemo); // Test the offline capability. + OfflineTest(mainDemo); // Verify the offline capability. c = PutMenuAndGetChr(); break; case 'c': - CaptureTest(mainDemo); // Test the capture capability. + CaptureTest(mainDemo); // Verify the capture capability. c = PutMenuAndGetChr(); break; - case 'w': // Test the AWB capability. - if (awb) { + case 'w': // Verify the AWB capability. + if (isAwb) { mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_INCANDESCENT); } else { mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_OFF); } - awb = !awb; + isAwb = !isAwb; c = PutMenuAndGetChr(); break; - case 'a': // Test the AE capability. + case 'a': // Verify the AE capability. mainDemo->SetAeExpo(); c = PutMenuAndGetChr(); break; - case 'v': // Test the video capability. + case'e': // Verify the metadata operations. + mainDemo->SetMetadata(); + c = PutMenuAndGetChr(); + break; + case'v': // Verify the video function. VideoTest(mainDemo); c = PutMenuAndGetChr(); break; - case 'q': // Exit the demo. + case 'q': // Exit the demo. PreviewOff(mainDemo); mainDemo->QuitDemo(); - exit(EXIT_SUCCESS); - + return; default: CAMERA_LOGE("main test: command error please retry input command"); c = PutMenuAndGetChr(); @@ -700,9 +989,9 @@ There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. } ``` - The **PutMenuAndGetChr()** interface prints the menu of the demo and calls **fgets()** to wait for commands from the console. + Use **PutMenuAndGetChr()** to print the menu of the demo and call **fgets()** to wait for commands from the console. - ``` + ```c++ static int PutMenuAndGetChr(void) { constexpr uint32_t inputCount = 50; @@ -724,7 +1013,7 @@ There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. The console outputs the menu details as follows: - ``` + ```c++ "Options:\n" "-h | --help Print this message\n" "-o | --offline stream offline test\n" @@ -732,8 +1021,28 @@ There is a camera demo in the **/drivers/peripheral/camera/hal/init** directory. "-w | --set WB Set white balance Cloudy\n" "-v | --video capture Video of 10s\n" "-a | --Set AE Set Auto exposure\n" + "-e | --Set Metadeta Set Metadata\n" "-f | --Set Flashlight Set flashlight ON 5s OFF\n" "-q | --quit stop preview and quit this app\n"); ``` - +4. Compile and build the **ohos_camera_demo**. + + Add **init:ohos_camera_demo** to **deps** in the **drivers/peripheral/camera/hal/BUILD.gn** file. + + The sample code is as follows: + + ``` + deps = [ + "buffer_manager:camera_buffer_manager", + "device_manager:camera_device_manager", + "hdi_impl:camera_host_service_1.0", + "pipeline_core:camera_pipeline_core", + "utils:camera_utils", + "init:ohos_camera_demo", + ] + ``` + + The following uses RK3568 development board as an example. + 1. Run the **./build.sh --product-name rk3568 --ccache** command to generate the executable binary file **ohos_camera_demo** in **out/rk3568/packages/phone/vendor/bin/**. + 2. Import the executable file **ohos_camera_demo** to the development board, modify the permission, and run the file. diff --git a/en/device-dev/driver/driver-peripherals-codec-des.md b/en/device-dev/driver/driver-peripherals-codec-des.md index fcc4ccf2c47a05a71e06bce9f5e695586024a12c..735c38fa84ae0ae500b7fac604398cc3562b14eb 100644 --- a/en/device-dev/driver/driver-peripherals-codec-des.md +++ b/en/device-dev/driver/driver-peripherals-codec-des.md @@ -84,7 +84,7 @@ The codec module implements hardware encoding and decoding of video data. It con For more information, see [codec](https://gitee.com/openharmony/drivers_peripheral/tree/master/codec). -### Development Procedure +### How to Develop The codec HDI driver development procedure is as follows: #### Registering and Initializing the Driver diff --git a/en/device-dev/driver/driver-peripherals-external-des.md b/en/device-dev/driver/driver-peripherals-external-des.md index efaf1d14e916222840d56537b48e2e19b83fb844..d2b681dcad9172b622cb261d81c9efdf3a0b86cc 100644 --- a/en/device-dev/driver/driver-peripherals-external-des.md +++ b/en/device-dev/driver/driver-peripherals-external-des.md @@ -5,22 +5,22 @@ ### WLAN -The Wireless Local Area Network (WLAN) Driver module in OpenHarmony is developed based on the Hardware Driver Foundation (HDF). It provides cross-OS porting, self-adaptation to component differences, and module assembly and building. +The Wireless Local Area Network (WLAN) driver module is developed based on OpenHarmony Hardware Driver Foundation (HDF). It supports modular assembly and building, automatic adaptation to device differences, and cross-OS porting. ### Working Principles -You can adapt your driver code based on the unified interfaces provided by the WLAN module. The WLAN module provides: +You can modify your driver code based on the unified APIs provided by the WLAN module. The WLAN module provides: -- A unified underlying interface to implement capabilities, such as setting up or closing a WLAN hotspot, scanning hotspots, and connecting to or disconnecting from a hotspot. -- A unified interface to the Hardware Device Interface (HDI) layer to implement capabilities, such as setting or obtaining the device Media Access Control (MAC) address and setting the transmit power. +- APIs for the underlying layer to implement capabilities, such as opening or closing a WLAN hotspot, scanning hotspots, and connecting to or disconnecting from a hotspot. +- APIs for the Hardware Device Interface (HDI) layer to implement capabilities, such as setting or obtaining the device Media Access Control (MAC) address and setting the transmit power. -The figure below shows the WLAN architecture. The WLAN Driver module implements startup loading, parses configuration files, and provides bus abstraction APIs. The WLAN Chip Driver module provides the MAC Sublayer Management Entity (MLME). +The following figure shows the WLAN architecture. The WLAN driver module implements startup loading, parses configuration files, and provides bus abstraction APIs. The WLAN chip driver module provides the MAC Sublayer Management Entity (MLME). **Figure 1** WLAN architecture ![image](figures/WLAN_architecture.png "WLAN architecture") - The figure below shows the WLAN driver architecture. + The following figure shows the WLAN driver architecture. **Figure 2** WLAN driver architecture @@ -32,11 +32,11 @@ The WLAN driver consists of the following modules: 2. WLAN Configuration Core: parses WLAN configuration files. -3. Access point (AP): provides a WLAN access interface for devices. +3. Access point (AP): allows devices to connect to the WLAN. -4. Station (STA): a terminal that accesses the WLAN system. +4. Station (STA): a device that has access to the WLAN system and allows transmission and reception of data. -5. mac80211: defines MAC-layer interfaces for underlying drivers. +5. mac80211: defines MAC-layer APIs for underlying drivers. 6. Bus: provides a unified bus abstract interface for the upper layer. It shields the differences between different kernels by calling the Secure Digital Input Output (SDIO) interfaces provided by the platform layer and encapsulating the adapted USB and PCIe interfaces. It also encapsulates different types of bus operations in a unified manner to shield differences between different chipsets. The complete bus driving capabilities provided by the bus module help simplify and streamline the development of different chip vendors. @@ -58,13 +58,13 @@ The relationships between the main modules are as follows: 4. The protocol stack works with the NetDevice, NetBuf, and FlowCtl modules to exchange data flows. -## Development Guidelines +## How to Develop ### Available APIs The WLAN module provides the following types of APIs: -1. Hardware Device Interface (HDI) and Hardware Abstraction Layer (HAL) APIs for upper-layer services +1. HDI and Hardware Abstraction Layer (HAL) APIs for upper-layer services 2. APIs for vendors @@ -75,7 +75,7 @@ The WLAN module provides the following types of APIs: ![image](figures/WLAN_driver_APIs.png "WLAN Driver APIs") -- The WLAN module provides HAL APIs for upper-layer services (applicable to small and mini systems). **Table 2** and **Table 3** describe some APIs. +- The WLAN module provides HAL APIs for upper-layer services (applicable to small and mini systems). **Table 1** and **Table 2** describe some APIs. **Table 1** wifi_hal.h @@ -95,7 +95,7 @@ The WLAN module provides the following types of APIs: | int32_t (\*getDeviceMacAddress)(const struct IWiFiBaseFeature \*, unsigned char \*, uint8_t)| Obtains the device MAC address.| | int32_t (\*setTxPower)(const struct IWiFiBaseFeature \*, int32_t)| Sets the transmit power.| -- The WLAN Driver module also provides APIs that you need to fill in the implementation. **Table 4** describes some APIs. +- The WLAN Driver module also provides APIs that you need to fill in the implementation. **Table 3** describes some APIs. **Table 3** net_device.h @@ -110,7 +110,7 @@ The WLAN module provides the following types of APIs: - The WLAN Driver module provides APIs that you can directly use to create or release a **WifiModule**, connect to or disconnect from a WLAN hotspot, request or release a **NetBuf**, and convert between the **pbuf** structure of Lightweight IP (lwIP) and a **NetBuf**. - Tables 5 to 7 describe the APIs. + The following tables describe the APIs. **Table 4** wifi_module.h @@ -119,7 +119,7 @@ The WLAN module provides the following types of APIs: | struct WifiModule \*WifiModuleCreate(const struct HdfConfigWifiModuleConfig \*config)| Creates a **WifiModule**.| | void WifiModuleDelete(struct WifiModule \*module)| Deletes a **WifiModule** and releases its data.| | int32_t DelFeature(struct WifiModule \*module, uint16_t featureType)| Deletes a feature from a **WifiModule**.| - | int32_t AddFeature(struct WifiModule \*module, uint16_t featureType, struct WifiFeature \*featureData)| Adds a feature to a **WifiModule**.| + | int32_t AddFeature(struct WifiModule \*module, uint16_t featureType,
struct WifiFeature \*featureData)| Adds a feature to a **WifiModule**.| **Table 5** wifi_mac80211_ops.h @@ -136,11 +136,11 @@ The WLAN module provides the following types of APIs: | -------- | -------- | | static inline void NetBufQueueInit(struct NetBufQueue \*q)| Initializes a **NetBuf** queue.| | struct NetBuf \*NetBufAlloc(uint32_t size)| Allocates a **NetBuf**.| - | void NetBufFree(struct NetBuf \*nb) | Releases a **NetBuf**.| + | void NetBufFree(struct NetBuf \*nb)| Releases a **NetBuf**.| | struct NetBuf \*Pbuf2NetBuf(const struct NetDevice \*netdev, struct pbuf \*lwipBuf)| Converts the **pbuf** structure of lwIP to a **NetBuf**.| | struct pbuf \*NetBuf2Pbuf(const struct NetBuf \*nb)| Converts a **NetBuf** to the **pbuf** structure of lwIP.| -### How to Develop +### Development Procedure #### WLAN Framework Adaptation The WLAN driver framework developed based on the HDF and Platform framework provides a unified driver model regardless of the OS and system on a chip (SoC). When developing your WLAN driver, you need to configure data based on the WLAN driver framework. @@ -186,19 +186,19 @@ The following uses the Hi3881 WLAN chip as an example to describe how to initial } } reset { - resetType = 0; /* Reset type. The value 0 indicates that reset is dynamically determined, and 1 indicates reset through GPIO. */ - gpioId = 2; /* GPIO pin number. */ - activeLevel=1; /* Active level. The value 0 indicates low level, and 1 indicates high level. */ - resetHoldTime = 30; /* Hold time (ms) after a reset. */ + resetType = 0; /* Reset type. The value 0 indicates that reset is dynamically determined, and 1 indicates reset through GPIO. */ + gpioId = 2; /* GPIO pin number. */ + activeLevel=1; /* Active level. The value 0 indicates low level, and 1 indicates high level. */ + resetHoldTime = 30; /* Hold time (ms) after a reset. */ } - bootUpTimeout = 30; /* Boot timeout duration (ms). */ + bootUpTimeout = 30; /* Boot timeout duration (ms). */ bus { - busEnable = 1; /* Whether to initialize the bus. The value 1 means to initialize the bus; the value 0 means the opposite. */ - busType = 0; /* Bus type. The value 0 indicates SDIO. */ - busId = 2; /* Bus number. */ - funcNum = [1]; /* SDIO function number. */ - timeout = 1000; /* Timeout duration for data read/write. */ - blockSize = 512; /* Size of the data block to read or write. */ + busEnable = 1; /* Whether to initialize the bus. The value 1 means to initialize the bus; the value 0 means the opposite. */ + busType = 0; /* Bus type. The value 0 indicates SDIO. */ + busId = 2; /* Bus number. */ + funcNum = [1]; /* SDIO function number. */ + timeout = 1000; /* Timeout duration for data read/write. */ + blockSize = 512; /* Size of the data block to read or write. */ } } } @@ -546,11 +546,7 @@ The following uses the Hi3881 WLAN chip as an example to describe how to initial } ``` -4. Invoke the event reporting APIs. - - The WLAN framework provides the event reporting APIs. For details, see **hdf_wifi_event.c**. - - For example, call **HdfWiFiEventNewSta AP** to report information about the newly associated STA. +4. Invoke the event reporting APIs.
The WLAN framework provides the event reporting APIs. For details, see hdf_wifi_event.c.
For example, call **HdfWiFiEventNewSta AP** to report information about the newly associated STA. ```c hi_u32 oal_cfg80211_new_sta(oal_net_device_stru *net_device, const hi_u8 *mac_addr, hi_u8 addr_len, @@ -567,12 +563,10 @@ The following uses the Hi3881 WLAN chip as an example to describe how to initial hi_unref_param(en_gfp); hi_unref_param(addr_len); #endif - + return HI_SUCCESS; } ``` - - **Verification** Develop test cases in the WLAN module unit test to verify the basic features of the WLAN module. The following uses Hi3516D V300 standard system as an example. @@ -650,7 +644,7 @@ Develop test cases in the WLAN module unit test to verify the basic features of exit 0 ``` - - Create a **udhcpd.conf** file (used to start the **udhcpd**) and copy the following content to the file. In the following, **opt dns** *x.x.x.x* *x.x.x.x* indicates two DNS servers configured. You can configure DNS servers as required. + - Create a **udhcpd.conf** file (used to start the **udhcpd**) and copy the following content to the file.
In the following, **opt dns** *x.x.x.x* *x.x.x.x* indicates two DNS servers configured. You can configure DNS servers as required. ```text start 192.168.12.2 @@ -704,54 +698,44 @@ Develop test cases in the WLAN module unit test to verify the basic features of busybox udhcpd /vendor/etc/udhcpd.conf ``` - 4. On the mobile phone, select the network named **test** in the available Wi-Fi list and enter the password. - - The network name and password are configured in the **hostapd.conf** file. You can see that network name in the connected Wi-Fi list if the connection is successful. + 4. On the mobile phone, select the network named **test** in the available Wi-Fi list and enter the password.
The network name and password are configured in the **hostapd.conf** file. You can see that network name in the connected Wi-Fi list if the connection is successful. 5. Ping the test terminal from the development board. - + ```shell busybox ping xxx.xxx.xxx.xxx ``` - In the command, xxx.xxx.xxx.xxx indicates the IP address of the test terminal. If the test terminal can be pinged, the WLAN driver provides basic features normally. - - + In the command, *xxx.xxx.xxx.xxx* indicates the IP address of the test terminal. If the test terminal can be pinged, the WLAN driver provides basic features normally. - Verify basic STA features. - 1. Start the STA on the development board, and enable the hotspot on the test terminal. - - The hotspot name and password are configured in the **hostapd.conf** file. The hotspot name is **test**, and the password is **12345678**. - + 1. Start the STA on the development board, and enable the hotspot on the test terminal.
The hotspot name and password are configured in the **hostapd.conf** file. The hotspot name is **test**, and the password is **12345678**. + 2. Run the following command in the **cmd** window: - + ```shell hdc shell wpa_supplicant -i wlan0 -d -c wpa_supplicant.conf ``` - - - + 3. Run the following commands in another **cmd** window: - + ```shell hdc shell mount -o rw,remount / mount -o rw,remount /vendor busybox udhcpc -i wlan0 -s system/lib/dhcpc.sh ``` - The IP addresses of the board and test terminal are displayed if the command is successful. - + 4. Ping the test terminal from the development board. - + ```shell busybox ping xxx.xxx.xxx.xxx ``` - - In the command, *xxx.xxx.xxx.xxx* indicates the IP address of the test terminal. If the test terminal can be pinged, the WLAN driver provides basic features normally. - + + In the command, xxx.xxx.xxx.xxx indicates the IP address of the test terminal. If the test terminal can be pinged, the WLAN driver provides basic features normally. #### **API Invocation** The WLAN driver module provides two types of capability interfaces for the upper layer: HDI interface and HAL interface. @@ -963,19 +947,17 @@ The WLAN driver module provides two types of capability interfaces for the upper - Code paths: - - Adaptation of WLAN FlowCtl component on LiteOS: **//drivers/hdf_core/adapter/khdf/liteos/model/network/wifi** - - Adaptation of HDF network model on LiteOS: **//drivers/hdf_core/adapter/khdf/liteos/model/network** - - - Adaptation of WLAN FlowCtl component on Linux, build of the HDF WLAN model, and build of the vendor's WLAN driver: **//drivers/hdf_core/adapter/khdf/linux/model/network/wifi** - - Core code for implementing the WLAN module: **//drivers/hdf_core/framework/model/network/wifi** - - External APIs of the WLAN module: **//drivers/hdf_core/framework/include/wifi** - - HDF network model APIs: **//drivers/hdf_core/framework/include/net** - - WLAN HDI server implementation: **//drivers/peripheral/wlan** - - - - - - - + Adaptation of WLAN FlowCtl component on LiteOS: **//drivers/hdf_core/adapter/khdf/liteos/model/network/wifi** + + Adaptation of HDF network model on LiteOS: **//drivers/hdf_core/adapter/khdf/liteos/model/network** + + Adaptation of WLAN FlowCtl component on Linux, build of the HDF WLAN model, and build of the vendor's WLAN driver: **//drivers/hdf_core/adapter/khdf/linux/model/network/wifi** + + Core code for implementing the WLAN module: **//drivers/hdf_core/framework/model/network/wifi** + + External APIs of the WLAN module: **//drivers/hdf_core/framework/include/wifi** + + HDF network model APIs: **//drivers/hdf_core/framework/include/net** + + WLAN HDI server implementation: **//drivers/peripheral/wlan** diff --git a/en/device-dev/driver/driver-peripherals-lcd-des.md b/en/device-dev/driver/driver-peripherals-lcd-des.md index d5ba25a559c9c255a829ac083e869c0b28db9b00..ce3cfd04cf2a23ef6ef8bd250ffa287691c7faab 100644 --- a/en/device-dev/driver/driver-peripherals-lcd-des.md +++ b/en/device-dev/driver/driver-peripherals-lcd-des.md @@ -1,19 +1,25 @@ # LCD - ## Overview +### Function + The Liquid Crystal Display (LCD) driver performs operations such as powering on the LCD and initializing the internal registers of the driver integrated circuits (ICs). -The display driver model developed based on the Hardware Driver Foundation [(HDF)](../driver/driver-hdf-overview.md) provides a basic framework for LCD driver development, improves driver development efficiency, and facilitates porting of the driver across OSs and chip platforms. The figure below shows the HDF-based display driver model. +The display driver model based on the [Hardware Driver Foundation (HDF)](../driver/driver-hdf-overview.md) provides the following functions: + +- Provides a basic framework for LCD driver development to improve development efficiency. + +- Facilitates driver porting across OSs and chip platforms. + +The following figure shows the HDF-based display driver model. **Figure 1** HDF-based display driver model ![image](figures/architecture-of-the-display-driver-model.png "Display Driver Model") - -The display driver model consists of the display common driver layer, SoC adapter layer, and LCD panel driver layer. The HDF-based display driver model shields the differences between kernel forms through platform and OSAL APIs so the LCD driver can be easily ported between different OSs and chip platforms. The display driver model connects to the display common Hardware Abstraction Layer (HAL), supports the implementation of the Hardware Device Interface (HDI), and provides various driver capability interfaces for graphics services through the Display-HDI. +The display driver model consists of the display common driver layer, SoC adapter layer, and LCD panel driver layer. The HDF-based display driver model shields the differences between kernel forms through platform and OSAL APIs so that the LCD driver can be easily ported across OSs and chip platforms. The display driver model connects to the display common Hardware Abstraction Layer (HAL), supports the implementation of the Hardware Device Interface (HDI), and provides various driver capability interfaces for graphics services through the Display-HDI. - Display common driver layer: connects to the display common HAL through the IOService data channel provided by the HDF to receive and process various upper-layer calls in a centralized manner. @@ -23,8 +29,7 @@ The display driver model consists of the display common driver layer, SoC adapte The display driver model leverages the capabilities and APIs provided by the platform to simplify the display driver development and improve the efficiency. - -## Available APIs +### Basic Concepts The LCD interfaces include the Mobile Industry Processor Interface (MIPI) Display Serial Interface (DSI), Transistor-Transistor Logic (TTL) interface, and Low Voltage Differential Signaling (LVDS) interface. The MIPI DSI and TTL interfaces are commonly used. Here is a brief introduction to them. @@ -34,7 +39,7 @@ The LCD interfaces include the Mobile Industry Processor Interface (MIPI) Displa ![](figures/mipi-dsi.png "MIPI DSI") - The DSI is defined by the MIPI Alliance for the displays used in mobile devices. The MIPI DSI is used to transmit image data, in compliance with the MIPI protocol. Generally, control information is sent to the peer IC in the form of MIPI packets over the MIPI DSI, without the need of additional peripheral interfaces. + The DSI is defined by the MIPI Alliance for the displays used in mobile devices. The MIPI DSI is used to transmit image data, in compliance with the MIPI protocol. Generally, control information is sent to the peer IC in the form of MIPI packets over the MIPI DSI, without the need of additional peripheral interfaces. - TTL interface @@ -42,14 +47,31 @@ The LCD interfaces include the Mobile Industry Processor Interface (MIPI) Displa ![](figures/ttl-interface.png "ttl-interface") - TTL level signals are generated by TTL devices, which are a major type of digital integrated circuits. TTL devices are manufactured using the bipolar process and feature high speed, low power consumption, and diversified types. + ​ TTL level signals are generated by TTL devices, which are a major type of digital integrated circuits. TTL devices are manufactured using the bipolar process and feature high speed, low power consumption, and diversified types. + + The TTL interface is used to transmit data in parallel mode under control signals. It transmits data signals, clock signals, and control signals (such as line synchronization signals, frame synchronization signals, and data validity signals). For the LCD with the TTL, additional peripheral interfaces, such as the Serial Peripheral Interface (SPI) and Inter-Integrated Circuit (I2C), are required for the read and write of the internal registers. - The TTL interface is used to transmit data in parallel mode under control signals. It transmits data signals, clock signals, and control signals (such as line synchronization signals, frame synchronization signals, and data validity signals). For the LCD with the TTL, additional peripheral interfaces, such as the Serial Peripheral Interface (SPI) and Inter-Integrated Circuit (I2C), are required for the read and write of the internal registers. +### Constraints +In addition to the IC model, you must consider the LCD peripheral circuit design, LCD interface unit of the baseband chip, backlight IC control, and upper-layer software programs. All these factors affect the development of the LCD driver. -## How to Develop +## Development Guidelines -The HDF-based display driver model provides a unified driver model for LCDs regardless of the OS (LiteOS or Linux OS) and chip platform (Hi35xx, Hi38xx, or V3S). The development procedure is as follows: +### When to Use + +Before applying your device with OpenHarmony system, you need to perform LCD driver adaption. The HDF-based display driver model provides a unified driver model for LCD adaptation regardless of the OS (LiteOS or Linux OS) and chip platform (Hi35xx, Hi38xx, or V3S). + +### Available APIs + +Table 1 APIs required for LCD driver adaptation + +| API | Description | +| :------------------------------------------------------ | ------------------- | +| display :: host | Sets device information. | +| static int32_t LcdResetOn(void) | Sets the status of the reset pin.| +| int32_t SampleEntryInit(struct HdfDeviceObject *object) | Initializes the entry function of the device driver. | + +### How to Develop 1. Add the device configuration related to the LCD driver. @@ -58,305 +80,228 @@ The HDF-based display driver model provides a unified driver model for LCDs rega 3. Add the LCD panel driver and register the panel driver functions in the driver entry function **Init**. The functions provide capabilities for: - Powering on/off the LCD device + Based on the LCD hardware connection, use the GPIO APIs provided by the platform to perform operations on the LCD pins, such as the reset pin and IOVCC pin. For details about the power-on sequence, see the SPEC provided by the LCD supplier. + - Sending the initialization sequence + Based on the LCD hardware interfaces, use the I2C, SPI, and MIPI interfaces provided by the platform to download the LCD initialization sequence. For details, see the SPEC provided by the LCD supplier. -4. Implement other HDF APIs as required, for example, **Release()**. - -5. Use the HDF to create other device nodes for implementing service logic or debugging as required. - - -## Development Example - -Add the device configuration. - - -``` -/* Configuration of the devices related to the display driver */ -display :: host { - hostName = "display_host"; - /* Configuration of the HDF display driver */ - device_hdf_disp :: device { - device0 :: deviceNode { - policy = 2; - priority = 200; - permission = 0660; - moduleName = "HDF_DISP"; - serviceName = "hdf_disp"; - } - } - /* Configuration of the driver device at the SoC adapter layer */ - device_hi35xx_disp :: device { - device0 :: deviceNode { - policy = 0; - priority = 199; - moduleName = "HI351XX_DISP"; - } - } - /* Configuration of the LCD driver */ - device_lcd :: device { - device0 :: deviceNode { - policy = 0; - priority = 100; - preload = 0; - moduleName = "LCD_Sample"; - } - device1 :: deviceNode { - policy = 0; - priority = 100; - preload = 2; - moduleName = "LCD_SampleXX"; - } - } -} -``` - -Adapt the driver to the chip at the SoC adapter layer. The following example shows how to adapt the MIPI device to the Hi35xx series chips at the SoC adapter layer: - - -``` -static int32_t MipiDsiInit(struct PanelInfo *info) -{ - int32_t ret; - struct DevHandle *mipiHandle = NULL; - struct MipiCfg cfg; - - mipiHandle = MipiDsiOpen(0); - if (mipiHandle == NULL) { - HDF_LOGE("%s: MipiDsiOpen failure", __func__); - return HDF_FAILURE; - } - cfg.lane = info->mipi.lane; - cfg.mode = info->mipi.mode; - cfg.format = info->mipi.format; - cfg.burstMode = info->mipi.burstMode; - cfg.timing.xPixels = info->width; - cfg.timing.hsaPixels = info->hsw; - cfg.timing.hbpPixels = info->hbp; - cfg.timing.hlinePixels = info->width + info->hbp + info->hfp + info->hsw; - cfg.timing.vsaLines = info->vsw; - cfg.timing.vbpLines = info->vbp; - cfg.timing.vfpLines = info->vfp; - cfg.timing.ylines = info->height; - /* 0 : no care */ - cfg.timing.edpiCmdSize = 0; - cfg.pixelClk = CalcPixelClk(info); - cfg.phyDataRate = CalcDataRate(info); - /* Configure the MIPI device. */ - ret = MipiDsiSetCfg(mipiHandle, &cfg); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:MipiDsiSetCfg failure", __func__); - } - MipiDsiClose(mipiHandle); - HDF_LOGI("%s:pixelClk = %d, phyDataRate = %d\n", __func__, - cfg.pixelClk, cfg.phyDataRate); - return ret; -} -``` - -Develop an LCD driver. The sample code is as follows: - - -``` -#define RESET_GPIO 5 -#define MIPI_DSI0 0 -#define BLK_PWM1 1 -#define PWM_MAX_PERIOD 100000 -/* Set the backlight. */ -#define MIN_LEVEL 0 -#define MAX_LEVEL 255 -#define DEFAULT_LEVEL 100 - -#define WIDTH 480 -#define HEIGHT 960 -#define HORIZONTAL_BACK_PORCH 20 -#define HORIZONTAL_FRONT_PORCH 20 -#define HORIZONTAL_SYNC_WIDTH 10 -#define VERTICAL_BACK_PORCH 14 -#define VERTICAL_FRONT_PORCH 16 -#define VERTICAL_SYNC_WIDTH 2 -#define FRAME_RATE 60 - -/* PanelInfo structure */ -struct PanelInfo { - uint32_t width; - uint32_t height; - uint32_t hbp; - uint32_t hfp; - uint32_t hsw; - uint32_t vbp; - uint32_t vfp; - uint32_t vsw; - uint32_t frameRate; - enum LcdIntfType intfType; - enum IntfSync intfSync; - struct MipiDsiDesc mipi; - struct BlkDesc blk; - struct PwmCfg pwm; -}; - -/* Initialization sequence of the LCD panel */ -static uint8_t g_payLoad0[] = { 0xF0, 0x5A, 0x5A }; -static uint8_t g_payLoad1[] = { 0xF1, 0xA5, 0xA5 }; -static uint8_t g_payLoad2[] = { 0xB3, 0x03, 0x03, 0x03, 0x07, 0x05, 0x0D, 0x0F, 0x11, 0x13, 0x09, 0x0B }; -static uint8_t g_payLoad3[] = { 0xB4, 0x03, 0x03, 0x03, 0x06, 0x04, 0x0C, 0x0E, 0x10, 0x12, 0x08, 0x0A }; -static uint8_t g_payLoad4[] = { 0xB0, 0x54, 0x32, 0x23, 0x45, 0x44, 0x44, 0x44, 0x44, 0x60, 0x00, 0x60, 0x1C }; -static uint8_t g_payLoad5[] = { 0xB1, 0x32, 0x84, 0x02, 0x87, 0x12, 0x00, 0x50, 0x1C }; -static uint8_t g_payLoad6[] = { 0xB2, 0x73, 0x09, 0x08 }; -static uint8_t g_payLoad7[] = { 0xB6, 0x5C, 0x5C, 0x05 }; -static uint8_t g_payLoad8[] = { 0xB8, 0x23, 0x41, 0x32, 0x30, 0x03 }; -static uint8_t g_payLoad9[] = { 0xBC, 0xD2, 0x0E, 0x63, 0x63, 0x5A, 0x32, 0x22, 0x14, 0x22, 0x03 }; -static uint8_t g_payLoad10[] = { 0xb7, 0x41 }; -static uint8_t g_payLoad11[] = { 0xC1, 0x0c, 0x10, 0x04, 0x0c, 0x10, 0x04 }; -static uint8_t g_payLoad12[] = { 0xC2, 0x10, 0xE0 }; -static uint8_t g_payLoad13[] = { 0xC3, 0x22, 0x11 }; -static uint8_t g_payLoad14[] = { 0xD0, 0x07, 0xFF }; -static uint8_t g_payLoad15[] = { 0xD2, 0x63, 0x0B, 0x08, 0x88 }; -static uint8_t g_payLoad16[] = { 0xC6, 0x08, 0x15, 0xFF, 0x10, 0x16, 0x80, 0x60 }; -static uint8_t g_payLoad17[] = { 0xc7, 0x04 }; -static uint8_t g_payLoad18[] = { - 0xC8, 0x7C, 0x50, 0x3B, 0x2C, 0x25, 0x16, 0x1C, 0x08, 0x27, 0x2B, 0x2F, 0x52, 0x43, 0x4C, 0x40, - 0x3D, 0x30, 0x1E, 0x06, 0x7C, 0x50, 0x3B, 0x2C, 0x25, 0x16, 0x1C, 0x08, 0x27, 0x2B, 0x2F, 0x52, - 0x43, 0x4C, 0x40, 0x3D, 0x30, 0x1E, 0x06 -}; -static uint8_t g_payLoad19[] = { 0x11 }; -static uint8_t g_payLoad20[] = { 0x29 }; - -struct DsiCmdDesc g_OnCmd[] = { - { 0x29, 0, sizeof(g_payLoad0), g_payLoad0 }, - { 0x29, 0, sizeof(g_payLoad1), g_payLoad1 }, - { 0x29, 0, sizeof(g_payLoad2), g_payLoad2 }, - { 0x29, 0, sizeof(g_payLoad3), g_payLoad3 }, - { 0x29, 0, sizeof(g_payLoad4), g_payLoad4 }, - { 0x29, 0, sizeof(g_payLoad5), g_payLoad5 }, - { 0x29, 0, sizeof(g_payLoad6), g_payLoad6 }, - { 0x29, 0, sizeof(g_payLoad7), g_payLoad7 }, - { 0x29, 0, sizeof(g_payLoad8), g_payLoad8 }, - { 0x29, 0, sizeof(g_payLoad9), g_payLoad9 }, - { 0x23, 0, sizeof(g_payLoad10), g_payLoad10 }, - { 0x29, 0, sizeof(g_payLoad11), g_payLoad11 }, - { 0x29, 0, sizeof(g_payLoad12), g_payLoad12 }, - { 0x29, 0, sizeof(g_payLoad13), g_payLoad13 }, - { 0x29, 0, sizeof(g_payLoad14), g_payLoad14 }, - { 0x29, 0, sizeof(g_payLoad15), g_payLoad15 }, - { 0x29, 0, sizeof(g_payLoad16), g_payLoad16 }, - { 0x23, 0, sizeof(g_payLoad17), g_payLoad17 }, - { 0x29, 1, sizeof(g_payLoad18), g_payLoad18 }, - { 0x05, 120, sizeof(g_payLoad19), g_payLoad19 }, - { 0x05, 120, sizeof(g_payLoad20), g_payLoad20 }, -}; -static DevHandle g_mipiHandle = NULL; -static DevHandle g_pwmHandle = NULL; - -/* Set the status of the reset pin. */ -static int32_t LcdResetOn(void) -{ - int32_t ret; - ret = GpioSetDir(RESET_GPIO, GPIO_DIR_OUT); - if (ret != HDF_SUCCESS) { - HDF_LOGE("GpioSetDir failure, ret:%d", ret); - return HDF_FAILURE; - } - ret = GpioWrite(RESET_GPIO, GPIO_VAL_HIGH); - if (ret != HDF_SUCCESS) { - HDF_LOGE("GpioWrite failure, ret:%d", ret); - return HDF_FAILURE; - } - /* Set the delay to 20 ms. */ - OsalMSleep(20); - return HDF_SUCCESS; -} - -static int32_t SampleInit(void) -{ - /* Open the MIPI DSI device handle. */ - g_mipiHandle = MipiDsiOpen(MIPI_DSI0); - if (g_mipiHandle == NULL) { - HDF_LOGE("%s: MipiDsiOpen failure", __func__); - return HDF_FAILURE; - } - return HDF_SUCCESS; -} - -static int32_t SampleOn(void) -{ - int32_t ret; - /* Power on the LCD. */ - ret = LcdResetOn(); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: LcdResetOn failure", __func__); - return HDF_FAILURE; - } - if (g_mipiHandle == NULL) { - HDF_LOGE("%s: g_mipiHandle is null", __func__); - return HDF_FAILURE; - } - /* Send the initialization sequence via MIPI. */ - int32_t count = sizeof(g_OnCmd) / sizeof(g_OnCmd[0]); - int32_t i; - for (i = 0; i < count; i++) { - ret = MipiDsiTx(g_mipiHandle, &(g_OnCmd[i])); - if (ret != HDF_SUCCESS) { - HDF_LOGE("MipiDsiTx failure"); - return HDF_FAILURE; - } - } - /* Set MIPI to the high speed (HS) mode. */ - MipiDsiSetHsMode(g_mipiHandle); - return HDF_SUCCESS; -} - -/* PanelInfo structure variables */ -static struct PanelInfo g_panelInfo = { - .width = WIDTH, /* width */ - .height = HEIGHT, /* height */ - .hbp = HORIZONTAL_BACK_PORCH, /* horizontal back porch */ - .hfp = HORIZONTAL_FRONT_PORCH, /* horizontal front porch */ - .hsw = HORIZONTAL_SYNC_WIDTH, /* horizontal sync width */ - .vbp = VERTICAL_BACK_PORCH, /* vertical back porch */ - .vfp = VERTICAL_FRONT_PORCH, /* vertical front porch */ - .vsw = VERTICAL_SYNC_WIDTH, /* vertical sync width */ - .frameRate = FRAME_RATE, /* frame rate */ - .intfType = MIPI_DSI, /* panel interface type */ - .intfSync = OUTPUT_USER, /* output timing type */ - /* MIPI configuration */ - .mipi = { DSI_2_LANES, DSI_VIDEO_MODE, VIDEO_BURST_MODE, FORMAT_RGB_24_BIT }, - /* backlight config info */ - .blk = { BLK_PWM, MIN_LEVEL, MAX_LEVEL, DEFAULT_LEVEL }, - .pwm = { BLK_PWM1, PWM_MAX_PERIOD }, -}; - -/* Basic APIs that need to be adapted for the chip driver */ -static struct PanelData g_panelData = { - .info = &g_panelInfo, - .init = SampleInit, - .on = SampleOn, - .off = SampleOff, - .setBacklight = SampleSetBacklight, -}; - -/* Entry function of the chip driver */ -int32_t SampleEntryInit(struct HdfDeviceObject *object) -{ - HDF_LOGI("%s: enter", __func__); - if (object == NULL) { - HDF_LOGE("%s: param is null!", __func__); - return HDF_FAILURE; - } - /* Register the device driver APIs with the platform driver. */ - if (PanelDataRegister(&g_panelData) != HDF_SUCCESS) { - HDF_LOGE("%s: PanelDataRegister error!", __func__); - return HDF_FAILURE; - } - return HDF_SUCCESS; -} - -struct HdfDriverEntry g_sampleDevEntry = { - .moduleVersion = 1, - .moduleName = "LCD_SAMPLE", - .Init = SampleEntryInit, -}; - -HDF_INIT(g_sampleDevEntry); -``` +4. (Optional) Implement other HDF APIs, such as **Release()**, as required. + +5. (Optional) Create other device nodes for implementing service logic or debugging based on the HDF as required. + +### Example + +The following uses the Hi35xx series chips as an example to describe how to perform LCD driver adaptation. + +1. Add device information in the **vendor/bearpi/bearpi_hm_micro/hdf_config/device_info/device_info.hcs** file. + + ```c++ + /* Configuration of the devices related to the display driver */ + display :: host { + hostName = "display_host"; + /* Configuration of the HDF display driver */ + device_hdf_disp :: device { + device0 :: deviceNode { + policy = 2; + priority = 200; + permission = 0660; + moduleName = "HDF_DISP"; + serviceName = "hdf_disp"; + } + } + /* Configuration of the driver device at the SoC adapter layer */ + device_hi35xx_disp :: device { + device0 :: deviceNode { + policy = 0; + priority = 199; + moduleName = "HI351XX_DISP"; + } + } + /* Configuration of the LCD driver */ + device_lcd :: device { + device0 :: deviceNode { + policy = 0; + priority = 100; + preload = 0; + moduleName = "LCD_Sample"; + } + device1 :: deviceNode { + policy = 0; + priority = 100; + preload = 2; + moduleName = "LCD_SampleXX"; + } + } + } + ``` + +2. Configure the chip platform driver information in the **drivers/hdf_core/framework/model/display/driver/adapter_soc/hi35xx_disp.c file**. + + ```c++ + /* Configuration of the display driver to adapt to the MIPI and chip platform */ + static int32_t MipiDsiInit(struct PanelInfo *info) + { + int32_t ret; + struct DevHandle *mipiHandle = NULL; + struct MipiCfg cfg; + + mipiHandle = MipiDsiOpen(0); + if (mipiHandle == NULL) { + HDF_LOGE("%s: MipiDsiOpen failure", __func__); + return HDF_FAILURE; + } + cfg.lane = info->mipi.lane; + cfg.mode = info->mipi.mode; + cfg.format = info->mipi.format; + cfg.burstMode = info->mipi.burstMode; + cfg.timing.xPixels = info->width; + cfg.timing.hsaPixels = info->hsw; + cfg.timing.hbpPixels = info->hbp; + cfg.timing.hlinePixels = info->width + info->hbp + info->hfp + info->hsw; + cfg.timing.vsaLines = info->vsw; + cfg.timing.vbpLines = info->vbp; + cfg.timing.vfpLines = info->vfp; + cfg.timing.ylines = info->height; + /* 0 : no care */ + cfg.timing.edpiCmdSize = 0; + cfg.pixelClk = CalcPixelClk(info); + cfg.phyDataRate = CalcDataRate(info); + /* Configure the MIPI device. */ + ret = MipiDsiSetCfg(mipiHandle, &cfg); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s:MipiDsiSetCfg failure", __func__); + } + MipiDsiClose(mipiHandle); + HDF_LOGI("%s:pixelClk = %d, phyDataRate = %d\n", __func__, + cfg.pixelClk, cfg.phyDataRate); + return ret; + } + ``` + +3. Add a device in **drivers/hdf_core/framework/model/display/driver/panel/mipi_icn9700.c**. + + - Define driver-related interface information. + + ```c++ + #define RESET_GPIO 5 + #define MIPI_DSI0 0 + #define BLK_PWM1 1 + #define PWM_MAX_PERIOD 100000 + /* Set the backlight. */ + #define MIN_LEVEL 0 + #define MAX_LEVEL 255 + #define DEFAULT_LEVEL 100 + #define WIDTH 480 + #define HEIGHT 960 + #define HORIZONTAL_BACK_PORCH 20 + #define HORIZONTAL_FRONT_PORCH 20 + #define HORIZONTAL_SYNC_WIDTH 10 + #define VERTICAL_BACK_PORCH 14 + #define VERTICAL_FRONT_PORCH 16 + #define VERTICAL_SYNC_WIDTH 2 + #define FRAME_RATE 60 + ``` + + - Define the **PanelInfo** structure. + + ```c++ + struct PanelInfo { + uint32_t width; + uint32_t height; + uint32_t hbp; + uint32_t hfp; + uint32_t hsw; + uint32_t vbp; + uint32_t vfp; + uint32_t vsw; + uint32_t frameRate; + enum LcdIntfType intfType; + enum IntfSync intfSync; + struct MipiDsiDesc mipi; + struct BlkDesc blk; + struct PwmCfg pwm; + }; + ``` + + - Initialize the LCD. + + ```c++ + static uint8_t g_payLoad0[] = { 0xF0, 0x5A, 0x5A }; + static uint8_t g_payLoad1[] = { 0xF1, 0xA5, 0xA5 }; + static uint8_t g_payLoad2[] = { 0xB3, 0x03, 0x03, 0x03, 0x07, 0x05, 0x0D, 0x0F, 0x11, 0x13, 0x09, 0x0B }; + static uint8_t g_payLoad3[] = { 0xB4, 0x03, 0x03, 0x03, 0x06, 0x04, 0x0C, 0x0E, 0x10, 0x12, 0x08, 0x0A }; + static uint8_t g_payLoad4[] = { 0xB0, 0x54, 0x32, 0x23, 0x45, 0x44, 0x44, 0x44, 0x44, 0x60, 0x00, 0x60, 0x1C }; + static uint8_t g_payLoad5[] = { 0xB1, 0x32, 0x84, 0x02, 0x87, 0x12, 0x00, 0x50, 0x1C }; + static uint8_t g_payLoad6[] = { 0xB2, 0x73, 0x09, 0x08 }; + static uint8_t g_payLoad7[] = { 0xB6, 0x5C, 0x5C, 0x05 }; + static uint8_t g_payLoad8[] = { 0xB8, 0x23, 0x41, 0x32, 0x30, 0x03 }; + static uint8_t g_payLoad9[] = { 0xBC, 0xD2, 0x0E, 0x63, 0x63, 0x5A, 0x32, 0x22, 0x14, 0x22, 0x03 }; + static uint8_t g_payLoad10[] = { 0xb7, 0x41 }; + static uint8_t g_payLoad11[] = { 0xC1, 0x0c, 0x10, 0x04, 0x0c, 0x10, 0x04 }; + static uint8_t g_payLoad12[] = { 0xC2, 0x10, 0xE0 }; + static uint8_t g_payLoad13[] = { 0xC3, 0x22, 0x11 }; + static uint8_t g_payLoad14[] = { 0xD0, 0x07, 0xFF }; + static uint8_t g_payLoad15[] = { 0xD2, 0x63, 0x0B, 0x08, 0x88 }; + static uint8_t g_payLoad16[] = { 0xC6, 0x08, 0x15, 0xFF, 0x10, 0x16, 0x80, 0x60 }; + static uint8_t g_payLoad17[] = { 0xc7, 0x04 }; + static uint8_t g_payLoad18[] = { + 0xC8, 0x7C, 0x50, 0x3B, 0x2C, 0x25, 0x16, 0x1C, 0x08, 0x27, 0x2B, 0x2F, 0x52, 0x43, 0x4C, 0x40, + 0x3D, 0x30, 0x1E, 0x06, 0x7C, 0x50, 0x3B, 0x2C, 0x25, 0x16, 0x1C, 0x08, 0x27, 0x2B, 0x2F, 0x52, + 0x43, 0x4C, 0x40, 0x3D, 0x30, 0x1E, 0x06 + }; + static uint8_t g_payLoad19[] = { 0x11 }; + static uint8_t g_payLoad20[] = { 0x29 }; + static DevHandle g_mipiHandle = NULL; + static DevHandle g_pwmHandle = NULL; + ``` + + - Set the status of the reset pin. + + ```c++ + static int32_t LcdResetOn(void) + { + int32_t ret; + ret = GpioSetDir(RESET_GPIO, GPIO_DIR_OUT); + if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioSetDir failure, ret:%d", ret); + return HDF_FAILURE; + } + ret = GpioWrite(RESET_GPIO, GPIO_VAL_HIGH); + if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioWrite failure, ret:%d", ret); + return HDF_FAILURE; + } + /* Set the delay to 20 ms. */ + OsalMSleep(20); + return HDF_SUCCESS; + } + ``` + + - Initialize the entry function of the device driver. + + ```c++ + int32_t SampleEntryInit(struct HdfDeviceObject *object) + { + HDF_LOGI("%s: enter", __func__); + if (object == NULL) { + HDF_LOGE("%s: param is null!", __func__); + return HDF_FAILURE; + } + /* Register the device driver APIs with the platform driver. */ + if (PanelDataRegister(&g_panelData) != HDF_SUCCESS) { + HDF_LOGE("%s: PanelDataRegister error!", __func__); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + struct HdfDriverEntry g_sampleDevEntry = { + .moduleVersion = 1, + .moduleName = "LCD_SAMPLE", + .Init = SampleEntryInit, + }; + + HDF_INIT(g_sampleDevEntry); + ``` diff --git a/en/device-dev/driver/driver-peripherals-light-des.md b/en/device-dev/driver/driver-peripherals-light-des.md index 2abac6f9b91ce912813ca0e2660880a00299d661..9b680a1bced3964d7197ad7c99004105988a40e1 100644 --- a/en/device-dev/driver/driver-peripherals-light-des.md +++ b/en/device-dev/driver/driver-peripherals-light-des.md @@ -5,7 +5,7 @@ ### Light -The light driver model provides APIs for the upper-layer light hardware service layer to control lights, including obtaining the light type, setting the lighting mode and blinking effect, and turning on or off a light. This model implements functionalities such as cross-OS migration and differentiated configurations based on the Hardware Driver Foundation (HDF) to achieve the goal of "one-time development for cross-system deployment" of the light driver. +The light driver model provides APIs for the upper-layer light hardware service layer to control lights, including obtaining the light type, setting the lighting mode and blinking effect, and turning on or off a light. This model implements cross-OS porting and differentiated configurations based on the Hardware Driver Foundation (HDF) to achieve the goal of "one-time development for cross-system deployment" of the light driver. The figure below shows the light driver model. @@ -21,14 +21,14 @@ The figure below shows how the light driver works. ![How light driver works](figures/light_working.png) -The following uses the Hi3516D V300 development board powered by the standard system as an example to describe how the light driver works. +The following describes how the light module driver loads and starts on a Hi3516D V300 board that runs the standard system. -1. The light driver reads the light device management configuration from **Light Host** in the **device_info.hcs** file. -2. The light driver reads the light data configuration from the **light_config.hcs** file. -3. The light driver parses the light device management configuration and associates with the corresponding device driver. -4. The light proxy delivers an instruction to the light stub. -5. The light stub delivers an instruction to the light controller. -6. The light abstract driver interface is started. +1. The Device Manager reads the Light device management configuration from the **device_info.hcs** file. +2. The Device Manager reads the light data configuration from the **light_config.hcs** file. +3. The HCS Parser parses the light device management configuration, loads the Light Host, and controls the Host to load the driver. +4. The Light Proxy obtains the light HDI service instance and calls the Light Stub over Inter-Process Communication (IPC). +5. The Light Stub processes IPC-related service logic and calls the Light Controller after parameter deserialization. +6. The Light Controller implements the HDI APIs and calls the Light Abstract Driver APIs to operate the light devices. ## Development Guidelines @@ -38,33 +38,32 @@ Light control is widely used in daily life. For example, a light is blinking whe ### Available APIs -The light driver model provides APIs to obtain information about all the lights in the system and dynamically set the blinking mode and duration. The light hardware service calls the **GetLightInfo** method to obtain basic information about the light and calls the **TurnOnLight** method to make the light blinking. The table below describes the APIs of the light driver model. +The light driver model provides APIs for obtaining information about all the lights in the system and dynamically setting the blinking mode and duration. The light hardware service calls **GetLightInfo()** to obtain the basic light information, calls **TurnOnLight()** to set the blinking effect, and calls **TurnOffLight()** to turn off lights. The following table describes the APIs of the light driver model. **Table 1** APIs of the light driver model -| API | Description | +| API | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| int32_t (*GetLightInfo)(struct LightInfo **lightInfo, uint32_t *count) | Obtains information about all lights in the system.
- **lightInfo** indicates the double pointer to the basic light information.
- **count** indicates the pointer to the number of lights. | -| int32_t (*TurnOnLight)(uint32_t lightId, struct LightEffect *effect) | Turns on available lights in the list based on the specified light type.
**lightId** indicates the light type, and **effect** indicates the pointer to the blinking effect. | -| int32_t (*TurnOffLight)(uint32_t lightId) | Turns off available lights in the light list based on the specified light type.
**lightId** indicates the light type. | +| int32_t (*GetLightInfo)([out] struct LightInfo **lightInfo, [out] uint32_t *count) | Obtains information about all types of lights in the system.
**lightInfo** indicates the double pointer to the light information obtained.
**count** indicates the pointer to the number of lights.| +| int32_t (*TurnOnLight)([in] uint32_t lightId, [in] struct LightEffect *effect) | Turns on available lights in the list based on the specified light type.
**lightId** indicates the light type, and **effect** indicates the pointer to the light effect.| +| int32_t (*TurnOffLight)([in] uint32_t lightId) | Turns off available lights in the list based on the specified light type. | -### How to Develop -1. Based on the HDF and the driver entry, complete the light abstract driver development (using the **Bind**, **Init**, **Release**, and **Dispatch** functions), resource configuration, and HCS parsing. Configure the light driver device information. +### Development Procedure +1. Based on the HDF and the driver entry, complete the light abstract driver development (using the **Bind**, **Init**, **Release**, and **Dispatch** functions), resource configuration, and HCS parsing. - Call **HDF_INIT** to register the driver entry with the HDF. Generally, the HDF calls the **Bind** function and then the **Init** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - The light driver model uses HDF configuration source (HCS). For details about HCS fields, see [Configuration Management](../driver/driver-hdf-manage.md). - The light driver entry is defined as follows: + - The light driver model uses HDF configuration source (HCS). For details about HCS fields, see [Configuration Management](driver-hdf-manage.md). The light driver entry is defined as follows: ```c /* Register the light entry data structure object. */ struct HdfDriverEntry g_lightDriverEntry = { - .moduleVersion = 1, // Light module version. - .moduleName = "HDF_LIGHT", // Light module name, which must be the same as the value of moduleName in the device_info.hcs file. - .Bind = BindLightDriver, // BInd the light driver. - .Init = InitLightDriver, // Initialize the light driver. - .Release = ReleaseLightDriver, // Release the light resources. + .moduleVersion = 1, // Version of the light module. + .moduleName = "HDF_LIGHT", // Light module name, which must be the same as the value of moduleName in the device_info.hcs file. + .Bind = BindLightDriver, // Bind() of the light driver. + .Init = InitLightDriver, // Init() of the light driver. + .Release = ReleaseLightDriver, // Release() of the light driver. }; - /* Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls Bind() and then Init() to load the driver. If Init() fails to be called, the HDF will call Release() to release resources and exit. */ + /* Call HDF_INIT to register the driver entry with the HDF. */ HDF_INIT(g_lightDriverEntry); ``` @@ -122,7 +121,7 @@ The light driver model provides APIs to obtain information about all the lights } /* Initialize work items. */ if (HdfWorkInit(&drvData->work, LightWorkEntry, (void*)drvData) != HDF_SUCCESS) { - HDF_LOGE("%s: init workQueue fail!", __func__); + HDF_LOGE("%s: init work fail!", __func__); return HDF_FAILURE; } /* Parse the HCS. */ @@ -160,20 +159,24 @@ The light driver model provides APIs to obtain information about all the lights } ``` - - The light device management module publishes light device APIs in the system. During the system startup process, the HDF loads the device management driver based on **Light Host** in the HCS. + - The light device management module is responsible for publishing light device APIs in the system. During the system startup process, the HDF loads the device management driver based on **Light Host** in the HCS. - ``` - /* Light device HCS */ - device_light :: device { - device0 :: deviceNode { - policy = 2; // Policy for the driver to publish services. (0: The driver does not provide services. 1: The driver publishes services for the kernel space. 2: The driver publishes services for both the kernel space and user space.) - priority = 100; // Light driver startup priority. The value ranges from 0 to 200. A larger value indicates a lower priority. The value 100 is recommended. If the priorities are the same, the device loading sequence cannot be ensured. - preload = 0; // Whether to load the driver on demand. The value 0 means to load the driver on demand; the value 2 means the opposite. - permission = 0664; // Permission for the driver to create a device node. - moduleName = "HDF_LIGHT"; // Light driver name. The value of this field must be the same as that of moduleName in the HdfDriverEntry structure. - serviceName = "hdf_light"; // Unique name of the service published by the driver. - deviceMatchAttr = "hdf_light_driver"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. + ```c + /* HCS of the light device. */ + light :: host { + hostName = "light_host"; + device_light :: device { + device0 :: deviceNode { + policy = 2; // Policy for the driver to publish services. If the value is 0, the driver does not publish services. If the value is 1, the driver publishes services to the kernel space. If the value is 2, the driver publishes services to both the kernel space and user space. + priority = 100; // Priority (0–200) for starting the light driver. A larger value indicates a lower priority. The recommended value is 100. If the priorities are the same, the device loading sequence is not ensured. + preload = 0; // The value 0 means to load the driver by default during the startup of the system. The value 2 means the opposite. + permission = 0664; // Permission for the device node created. + moduleName = "HDF_LIGHT"; // Light driver name. The value must be the same as the value of moduleName in the driver entry structure. + serviceName = "hdf_light"; // Service published by the light driver. The service name must be unique. + deviceMatchAttr = "hdf_light_driver"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. + } } + } ``` 2. Parse the device attribute information and registers, and register them with the light device management module. @@ -193,34 +196,35 @@ The light driver model provides APIs to obtain information about all the lights } for (i = 0; i < drvData->lightNum; ++i) { - ..... - /* Types are used as subscripts to create space. */ - drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo)); - ..... - /* Fill in the light device information. */ - ret = parser->GetUint32(node, "busRNum", (uint32_t *)&drvData->info[temp]->busRNum, 0); - if (ret != HDF_SUCCESS) { - /* If busNum fails to be obtained, the color of the light corresponding to busNum cannot be set. */ - drvData->info[temp]->busRNum = LIGHT_INVALID_GPIO; - } - ret = parser->GetUint32(node, "busGNum", (uint32_t *)&drvData->info[temp]->busGNum, 0); - if (ret != HDF_SUCCESS) { - drvData->info[temp]->busGNum = LIGHT_INVALID_GPIO; - } - ret = parser->GetUint32(node, "busBNum", (uint32_t *)&drvData->info[temp]->busBNum, 0); - if (ret != HDF_SUCCESS) { - drvData->info[temp]->busBNum = LIGHT_INVALID_GPIO; + ..... + /* Types are used as subscripts to create space. */ + drvData->info[temp] = (struct LightDeviceInfo *)OsalMemCalloc(sizeof(struct LightDeviceInfo)); + ..... + /* Fill in the light device information. */ + ret = parser->GetUint32(node, "busRNum", (uint32_t *)&drvData->info[temp]->busRNum, 0); + if (ret != HDF_SUCCESS) { + /* If busNum fails to be obtained, the color of the light corresponding to busNum cannot be set. */ + drvData->info[temp]->busRNum = LIGHT_INVALID_GPIO; + } + ret = parser->GetUint32(node, "busGNum", (uint32_t *)&drvData->info[temp]->busGNum, 0); + if (ret != HDF_SUCCESS) { + drvData->info[temp]->busGNum = LIGHT_INVALID_GPIO; + } + ret = parser->GetUint32(node, "busBNum", (uint32_t *)&drvData->info[temp]->busBNum, 0); + if (ret != HDF_SUCCESS) { + drvData->info[temp]->busBNum = LIGHT_INVALID_GPIO; + } } ..... return HDF_SUCCESS; } ``` -3. Call related APIs to obtain the light type, turn on and off lights, and create and delete the timer based on the blinking mode. +3. Implement the APIs for obtaining the light type, setting the blinking mode, turning on and off lights, and creating and destroying a timer based on the blinking mode. ```c - /* Call GetAllLightInfo() to obtain the light type. Call TurnOnLight() to enable the blinking mode. - Call TurnOffLight() to stop blinking. */ + /* Call GetAllLightInfo() to obtain the light types, call TurnOnLight() to turn on lights, + and call TurnOffLight() to turn off lights. */ static int32_t GetAllLightInfo(struct HdfSBuf *data, struct HdfSBuf *reply) { ..... @@ -255,7 +259,7 @@ The light driver model provides APIs to obtain information about all the lights } else { lightBrightness = drvData->info[lightId]->lightBrightness; } - /* If bits 0 to 7 are not 0, output the GPIO pin corresponding to blue based on the status of lightOn. */ + /* If bits 0 to 7 are not 0, output the GPIO pins corresponding to blue based on the status of lightOn. */ if ((lightBrightness & LIGHT_MAKE_B_BIT) != 0) { ret = WriteGpio(drvData->info[lightId]->busBNum, lightOn); if (ret != HDF_SUCCESS) { @@ -263,7 +267,7 @@ The light driver model provides APIs to obtain information about all the lights return HDF_FAILURE; } } - /* If bits 8 to 15 are not 0, output the GPIO pin corresponding to green based on the status of lightOn. */ + /* If bits 8 to 15 are not 0, output the GPIO pins corresponding to green based on the status of lightOn. */ if ((lightBrightness & LIGHT_MAKE_G_BIT) != 0) { ret = WriteGpio(drvData->info[lightId]->busGNum, lightOn); if (ret != HDF_SUCCESS) { @@ -271,7 +275,7 @@ The light driver model provides APIs to obtain information about all the lights return HDF_FAILURE; } } - /* If bits 16 to 23 are not 0, output the GPIO pin corresponding to red based on the status of lightOn. */ + /* If bits 16 to 23 are not 0, output the GPIO pins corresponding to red based on the status of lightOn. */ if ((lightBrightness & LIGHT_MAKE_R_BIT) != 0) { ret = WriteGpio(drvData->info[lightId]->busRNum, lightOn); if (ret != HDF_SUCCESS) { @@ -286,7 +290,7 @@ The light driver model provides APIs to obtain information about all the lights static int32_t TurnOnLight(uint32_t lightId, struct HdfSBuf *data, struct HdfSBuf *reply) { ..... - /* Receive the lightBrightness value passed in. Bits 24 to 31 are extension bits, bits 16 to 23 indicate red, bits 8 to 15 indicate green, and bits 0 to 7 indicate blue. If lightBrightness is not 0, enable the light in the specified color. + /* Receive the lightBrightness value passed in. Bits 24 to 31 are extension bits, bits 16 to 23 indicate red, bits 8 to 15 indicate green, and bits 0 to 7 indicate blue. If lightBrightness is not 0, turn on the light in the specified color. Set the light brightness to a value ranging from 0 to 255 if supported. */ drvData->info[lightId]->lightBrightness = buf->lightBrightness; /* The light is steady on. */ @@ -304,13 +308,13 @@ The light driver model provides APIs to obtain information about all the lights /* Create a timer. */ if (OsalTimerCreate(&drvData->timer, drvData->info[lightId]->onTime, LightTimerEntry, (uintptr_t)lightId) != HDF_SUCCESS) { - HDF_LOGE("%s: create light timer fail!", __func__); - return HDF_FAILURE; + HDF_LOGE("%s: create light timer fail!", __func__); + return HDF_FAILURE; } /* Start the periodic timer. */ if (OsalTimerStartLoop(&drvData->timer) != HDF_SUCCESS) { - HDF_LOGE("%s: start light timer fail!", __func__); - return HDF_FAILURE; + HDF_LOGE("%s: start light timer fail!", __func__); + return HDF_FAILURE; } } return HDF_SUCCESS; @@ -362,7 +366,7 @@ void HdfLightTest::TearDownTestCase() } } -/* Obtain the light type. */ +/* Obtain the test light type. */ HWTEST_F(HdfLightTest, GetLightList001, TestSize.Level1) { struct LightInfo *info = nullptr; diff --git a/en/device-dev/driver/driver-peripherals-motion-des.md b/en/device-dev/driver/driver-peripherals-motion-des.md index e3c3de926067feead05a095109ed2e8cb1e5ed95..eb147972aff5358f8e503e7375a094ea56323840 100644 --- a/en/device-dev/driver/driver-peripherals-motion-des.md +++ b/en/device-dev/driver/driver-peripherals-motion-des.md @@ -4,11 +4,11 @@ ### Function -The motion module provides motion recognition and control capabilities. Currently, OpenHarmony supports recognition of pick-up, flip, shake, and rotation. +The motion module provides motion recognition and control capabilities. OpenHarmony supports recognition of pick-up, flip, shake, and rotation. The motion driver is developed based on the hardware driver foundation (HDF). It shields hardware differences and provides APIs for the Multimodal Sensor Data Platform (MSDP) to implement capabilities such as enabling or disabling motion recognition, and subscribing to or unsubscribing from motion recognition data. -The figure below shows the motion driver architecture. The framework layer provides MSDP services, and interacts with the Motion Stub through the Motion Proxy in the User Hardware Driver Foundation (UHDF). The Motion Stub calls the Motion HDI Impl APIs to provide motion recognition capabilities for upper-layer services. +The following figure shows the motion driver architecture. The framework layer provides MSDP services, and interacts with the Motion Stub through the Motion Proxy in the User Hardware Driver Foundation (UHDF). The Motion Stub calls the Motion HDI Impl APIs to provide motion recognition capabilities for upper-layer services. **Figure 1** Motion driver architecture @@ -23,8 +23,8 @@ The figure below illustrates how a motion driver works. ![](figures/motion_driver_work.png) 1. MSDP: The MSDP service obtains a Motion HDI service instance from the Motion Proxy and calls the Motion HDI API. -2. IDL: The IService Manager allocates a Motion HDI instance requested by the MSDP service, and the Motion Proxy forwards the instance to the MSDP service. After the MSDP service calls the HDI API provided by the Motion Proxy, Motion Stub is called through Inter-Process Communication (IPC) to invoke the Motion Service API. The code is automatically generated by a tool and does not need to be developed by the component vendor. -3. HDI Service: HDI Service consists of Motion Interface Driver, Motion Service, and Motion Impl. Motion Interface Driver provides the motion driver code. A **HdfDriverEntry** structure is defined to implement the **Init**, **Bind**, and **Release** functions. The **HDF_INIT** macro is used to load the driver in the functions. Motion Service provides the motion recognition service interface class. The specific implementation is described in Motion Impl. The code of HDI Service must be developed by the component vendor. +2. IDL: The IService Manager allocates a Motion HDI instance requested by the MSDP service, and the Motion Proxy forwards the instance to the MSDP service. After the MSDP service calls the HDI API provided by the Motion Proxy, Motion Stub is called through Inter-Process Communication (IPC) to invoke the Motion Service API. The code is automatically generated by a tool and does not need to be developed by the component vendor. +3. HDI Service: The HDI service consists of Motion Interface Driver, Motion Service, and Motion Impl. Motion Interface Driver provides the motion driver code. A **HdfDriverEntry** structure is defined to implement the **Init**, **Bind**, and **Release** functions. The **HDF_INIT** macro is used to load the driver in the functions. Motion Service provides the motion recognition service interface class. The specific implementation is described in Motion Impl. The code of HDI Service must be developed by the component vendor. ## Development Guidelines @@ -53,15 +53,15 @@ The development procedure is as follows: The motion recognition directory structure is as follows: ```undefined -/drivers/peripheral/motion # Developed by the vendor. -├── hdi_service # Driver capabilities provided by the motion recognition module for the MSDP service layer. -├── test # Test code for the motion recognition module. -│ └── unittest\hdi # HDI unit test code for the motion recognition module. +/drivers/peripheral/motion # Developed by the vendor. +├── hdi_service # Driver capabilities provided by the motion recognition module for the MSDP service layer. +├── test # Test code for the motion recognition module. +│ └── unittest\hdi # HDI unit test code for the motion recognition module. ``` The following describes how to develop a user-mode motion driver based on the HDF. For details, see [motion_interface_driver.cpp](https://gitee.com/openharmony/drivers_peripheral/blob/master/motion/hdi_service/motion_interface_driver.cpp). -To develop the user-mode driver for motion recognition, implement the **Bind**, **Init**, **Release**, and **Dispatch** functions. The **Bind** function provides service capabilities. The **Init** function initializes the driver before the driver is loaded. The **Release** function releases resources when the **Init** function fails. +You need to implement the **Bind()**, **Init()**, **Release()**, and **Dispatch()** functions. The **Bind()** function binds the service capability with the driver; **Init()** implements the initialization required before the driver is loaded; **Release()** reclaims resources when **Init()** fails; **Dispatch()** implements the service, which is bound in **Bind()**. ```c++ // Custom HdfMotionInterfaceHost object @@ -153,7 +153,6 @@ struct HdfDriverEntry g_motioninterfaceDriverEntry = { HDF_INIT(g_userAuthInterfaceDriverEntry); ``` - ### Verification The procedure is as follows: diff --git a/en/device-dev/driver/driver-peripherals-sensor-des.md b/en/device-dev/driver/driver-peripherals-sensor-des.md index 0d1d3b5707af5d4523c44c1ed20b6b3d5fe503e7..2f04c658f03f217fe292dc885cbf06c3a6b50597 100644 --- a/en/device-dev/driver/driver-peripherals-sensor-des.md +++ b/en/device-dev/driver/driver-peripherals-sensor-des.md @@ -1,41 +1,41 @@ # Sensor - + ## Overview -### Introduction +### Function -The sensor driver model masks the sensor hardware differences and provides interfaces for the upper-layer sensor service to implement basic sensor capabilities, including querying the sensor list, enabling or disabling a sensor, subscribing to or unsubscribing from sensor data changes, and setting sensor options. The model is developed on the Hardware Driver Foundation (HDF), Operating System Abstraction Layer (OSAL), and platform driver interfaces (such as the I2C, SPI, and UART buses). It provides functionalities such as cross-OS migration and differentiated device configurations. The figure below shows the architecture of the sensor driver model. +The sensor driver model shields the hardware difference and provides interfaces for the upper-layer sensor service to implement basic sensor capabilities, including querying the sensor list, enabling or disabling a sensor, subscribing to or unsubscribing from sensor data changes, and setting sensor attributes. Developed based on the Hardware Driver Foundation (HDF), the sensor driver model leverages the capabilities of the OS adaptation layer (OSAL) and platform driver interfaces (such as I2C, SPI, and UART buses) to shield the difference between OSs and platform bus resources, achieving "one-time development and multi-system deployment" of the sensor driver. The figure below shows the architecture of the sensor driver model. **Figure 1** Sensor driver model - + ![Sensor driver model](figures/sensor_driver_model.png) ### Basic Concepts -Currently, sensors are classified into medical sensors and traditional sensors by sensor ID. +Sensors are classified into the following types by sensor ID: -- The IDs of medical sensors range from 128 to 160. +- Medical sensors: The sensor IDs range from 128 to 160. -- The IDs of traditional sensors are out of the range of 128 to 160. +- Traditional sensors: The sensor IDs are out of the range of 128 to 160. ### Working Principles -Based on the loading and running process (shown below) of the sensor driver model, the relationships between key modules in the model and associated modules are clearly defined. +The following figure shows how a sensor driver works. -**Figure 2** How sensor driver works +**Figure 2** How a sensor driver works ![How sensor driver works](figures/sensor_working.png) The following uses the acceleration sensor driver on the Hi3516D V300 development board of the standard system as an example to describe the driver loading and running process. -1. The sensor host reads the sensor management configuration from the Sensor Host node of the device_info HCS (sensor device information HCS). -2. The sensor host parses the sensor management configuration from the HCB database and associates the corresponding sensor driver. +1. The sensor host reads the sensor device management configuration from **Sensor Host** in the **device_info.hcs** file. +2. The sensor host parses the sensor management configuration from the HCB database and associates the configuration with the sensor driver. 3. The sensor host loads and initializes the sensor manager driver. -4. The sensor manager driver publishes the sensor hardware device interfaces (HDIs). -5. The sensor host reads the acceleration sensor driver configuration from the Sensor Host node of the device_info HCS. +4. The sensor manager driver publishes the sensor APIs for the hardware driver interface (HDI). +5. The sensor host reads the acceleration sensor driver configuration information from **Sensor Host** in the **device_info.hcs** configuration file. 6. The sensor host loads the acceleration sensor abstract driver and calls the initialization interface to allocate the sensor driver resources and create the data processing queue. -7. The sensor host reads the chipset driver configuration and private configuration of the acceleration sensor from the accel_xxx_config HCS (sensor private configuration HCS). +7. The sensor host reads the chipset driver configuration and private configuration of the acceleration sensor from the **accel_xxx_config.hcs** file. 8. The acceleration sensor chipset driver calls the common configuration parsing interface to parse the sensor attributes and registers. 9. The chipset driver detects sensors, allocates configuration resources to the acceleration sensor, and registers the acceleration sensor chipset interfaces. 10. Upon successful sensor detection, the chipset driver instructs the abstract driver to register the acceleration sensor to the sensor manager driver. @@ -54,34 +54,38 @@ The following uses the acceleration sensor driver on the Hi3516D V300 developmen The sensor driver model offers the following APIs: -- Sensor HDIs, for easier sensor service development -- Sensor driver model capability interfaces - - Interfaces for registering, loading, and deregistering sensor drivers, and detecting sensors - - Driver normalization interface, register configuration parsing interface, bus access abstract interface, and platform abstract interface for the same type of sensors -- Interfaces to be implemented by developers: Based on the HDF Configuration Source (HCS) and differentiated configuration for sensors of the same type, developers need to implement serialized configuration of sensor device parameters and some sensor device operation interfaces to simplify sensor driver development. +- Sensor HDI APIs, for easier sensor service development + +- APIs for implementing sensor driver model capabilities + - APIs for loading, registering, and deregitering sensor drivers, and detecting sensors based on the HDF + - Unified driver API, register configuration parsing API, bus access abstract API, and platform abstract API for the same type of sensors + +- APIs to be implemented by developers + + Based on the HDF Configuration Source (HCS) and differentiated configuration for sensors of the same type, you need to implement serialized configuration of sensor device parameters and some sensor device operation interfaces to simplify sensor driver development. The sensor driver model provides APIs for the hardware service to make sensor service development easier. See the table below. **Table 1** APIs of the sensor driver model -| API| Description| +| API| Description| | ----- | -------- | | int32_t GetAllSensors(struct SensorInformation **sensorInfo, int32_t *count) | Obtains information about all registered sensors in the system. The sensor information includes the sensor name, sensor vendor, firmware version, hardware version, sensor type ID, sensor ID, maximum range, accuracy, and power consumption.| -| int32_t Enable(int32_t sensorId) | Enables a sensor. The subscriber can obtain sensor data only after the sensor is enabled.| -| int32_t Disable(int32_t sensorId) | Disables a sensor.| -| int32_t SetBatch(int32_t sensorId, int64_t samplingInterval, int64_t reportInterval) | Sets the sampling interval and data reporting interval for a sensor.| -| int32_t SetMode(int32_t sensorId, int32_t mode) | Sets the data reporting mode for a sensor.| -| int32_t SetOption(int32_t sensorId, uint32_t option) | Sets options for a sensor, including its range and accuracy.| -| int32_t Register(int32_t groupId, RecordDataCallback cb) | Registers a sensor data callback based on the group ID.| -| int32_t Unregister(int32_t groupId, RecordDataCallback cb) | Deregisters a sensor data callback based on the group ID.| +| int32_t Enable(int32_t sensorId) | Enables a sensor. The subscriber can obtain sensor data only after the sensor is enabled.| +| int32_t Disable(int32_t sensorId) | Disables a sensor.| +| int32_t SetBatch(int32_t sensorId, int64_t samplingInterval, int64_t reportInterval) | Sets the sampling interval and data reporting interval for a sensor.| +| int32_t SetMode(int32_t sensorId, int32_t mode) | Sets the data reporting mode for a sensor.| +| int32_t SetOption(int32_t sensorId, uint32_t option) | Sets options for a sensor, including its range and accuracy.| +| int32_t Register(int32_t groupId, RecordDataCallback cb) | Registers a sensor data callback based on the group ID.| +| int32_t Unregister(int32_t groupId, RecordDataCallback cb) | Deregisters a sensor data callback based on the group ID.| The sensor driver model provides driver development APIs that do not require further implementation. See the table below. - **Table 2** Sensor driver development APIs that do not need to be implemented by driver developers + **Table 2** Sensor driver development APIs -| API| Description| +| API| Description| | ----- | -------- | | int32_t AddSensorDevice(const struct SensorDeviceInfo *deviceInfo) | Adds a sensor of the current type to the sensor management module.| | int32_t DeleteSensorDevice(const struct SensorBasicInfo *sensorBaseInfo) | Deletes a sensor from the sensor management module.| @@ -97,12 +101,11 @@ The sensor driver model provides driver development APIs that do not require fur - The sensor driver model also provides certain driver development APIs that need to be implemented by driver developers. See the table below. -**Table 3** APIs that need to be implemented by driver developers +**Table 3** APIs to be implemented by driver developers -| API| Description| +| API| Description| | ----- | -------- | | int32_t init(void) | Initializes the sensor device configuration after a sensor is detected.| | int32_t Enable(void) | Enables the current sensor by delivering the register configuration in the enabling operation group based on the device information HCS configuration.| @@ -121,14 +124,14 @@ The sensor driver model also provides certain driver development APIs that need ```c /* Register the entry structure object of the acceleration sensor. */ struct HdfDriverEntry g_sensorAccelDevEntry = { - .moduleVersion = 1, // Version of the acceleration sensor module. - .moduleName = "HDF_SENSOR_ACCEL", // Name of the acceleration sensor module. The value must be the same as that of moduleName in the device_info.hcs file. - .Bind = BindAccelDriver, // Function for binding an acceleration sensor. - .Init = InitAccelDriver, // Function for initializing an acceleration sensor. - .Release = ReleaseAccelDriver, // Function for releasing acceleration sensor resources. + .moduleVersion = 1, // Version of the acceleration sensor module. + .moduleName = "HDF_SENSOR_ACCEL", // Name of the acceleration sensor module. The value must be the same as that of moduleName in the device_info.hcs file. + .Bind = BindAccelDriver, // Function for binding an acceleration sensor. + .Init = InitAccelDriver, // Function for initializing an acceleration sensor. + .Release = ReleaseAccelDriver // Function for releasing acceleration sensor resources. }; - /* Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls the Bind function first and then the Init function. If the Init function fails to be called, the HDF will call Release to release the driver resource and exit the sensor driver model. */ + /* Call HDF_INIT to register the driver entry with the HDF. When loading the driver, the HDF calls Bind() and then Init() to load the driver. If Init() fails to be called, the HDF calls Release() to release resources and exit. */ HDF_INIT(g_sensorAccelDevEntry); ``` @@ -228,7 +231,7 @@ The sensor driver model also provides certain driver development APIs that need return drvData->accelCfg; ... } - /* The entry function of the acceleration sensor driver is used to initialize the sensor private data structure object, allocate space for the sensor HCS data configuration object, call the entry function for initializing the sensor HCS data configuration, detect whether the sensor device is in position, create a sensor data reporting timer, register the sensor normalization APIs, and register the sensor device. */ + /* The entry function of the acceleration sensor driver is used to initialize the sensor private data structure object, allocate space for the sensor HCS data configuration object, call the entry function for initializing the sensor HCS data configuration, detect whether the sensor device is in position, create a timer for sensor data reporting, register the sensor normalization APIs, and register the sensor device. */ int32_t AccelInitDriver(struct HdfDeviceObject *device) { ... @@ -271,16 +274,16 @@ The sensor driver model also provides certain driver development APIs that need The acceleration sensor model uses the HCS as the configuration source code. For details about the HCS configuration fields, see [Driver Configuration Management](driver-hdf-manage.md). - ``` + ```hcs /* Device information HCS configuration of the acceleration sensor. */ device_sensor_accel :: device { device0 :: deviceNode { - policy = 1; // Policy for publishing the driver service. - priority = 110; // Driver startup priority (0–200). A larger value indicates a lower priority. The default value 100 is recommended. The sequence for loading devices with the same priority is random. - preload = 0; // Field for specifying whether to load the driver. The value 0 means to load the driver, and 2 means the opposite. - permission = 0664; // Permission for the driver to create a device node. - moduleName = "HDF_SENSOR_ACCEL"; // Driver name. The value must be the same as that of moduleName in the driver entry structure. - serviceName = "sensor_accel"; // Name of the service provided by the driver. The name must be unique. + policy = 1; // Policy for the driver to publish services. + priority = 100; // Priority (0–200) for starting the driver. A larger value indicates a lower priority. The recommended value is 100. If the priorities are the same, the device loading sequence is not ensured. + preload = 0; // The value 0 means to load the driver by default during the startup of the system. The value 2 means the opposite. + permission = 0664; // Permission for the device node created. + moduleName = "HDF_SENSOR_ACCEL"; // Driver name. It must be the same as moduleName in the driver entry structure. + serviceName = "sensor_accel"; // Name of the service published by the driver. The name must be unique. deviceMatchAttr = "hdf_sensor_accel_driver"; // Keyword matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. } } @@ -466,7 +469,7 @@ The sensor driver model also provides certain driver development APIs that need /* Release the resources allocated during driver initialization. */ void Bmi160ReleaseDriver(struct HdfDeviceObject *device) { - ...... + ... if (drvData->sensorCfg != NULL) { AccelReleaseCfgData(drvData->sensorCfg); drvData->sensorCfg = NULL; @@ -511,18 +514,12 @@ The sensor driver model also provides certain driver development APIs that need } ``` ->![](../public_sys-resources/icon-note.gif) **NOTE** -> ->- The sensor driver model provides certain APIs to implement sensor driver capabilities, including the driver device management, abstract bus and platform operation, common configuration, and configuration parsing capabilities. For details about them, see Table 2. ->- You need to implement the following functions: certain sensor operation interfaces (listed in Table 3) and sensor chipset HCS configuration. ->- You also need to verify basic driver functions. +### Verification -### How to Verify +After the driver is developed, develop test cases in the sensor unit test to verify the basic functions of the driver. Use your test platform to set up the test environment. -After the driver is developed, you can develop self-test cases in the sensor unit test to verify the basic functions of the driver. Use the developer self-test platform as the test environment. - -``` -static int32_t g_sensorDataFlag = 0; // Indicates whether to report sensor data. +```c++ +static int32_t g_sensorDataFlag = 0; // Whether to report sensor data. static const struct SensorInterface *g_sensorDev = nullptr; // Retain the obtained sensor interface instance address. /* Register the data reporting function. */ @@ -558,9 +555,9 @@ void HdfSensorTest::TearDownTestCase() /* Verify the sensor driver. */ HWTEST_F(HdfSensorTest,TestAccelDriver_001, TestSize.Level0) { - int32_t sensorInterval = 1000000000; // Data sampling interval, in nanoseconds. - int32_t pollTime = 5; // Data sampling duration, in seconds. - int32_t accelSensorId = 1; // Acceleration sensor ID, which is 1. + int32_t sensorInterval = 1000000000; // Data sampling interval, in nanoseconds. + int32_t pollTime = 5; // Data sampling time, in seconds. + int32_t accelSensorId = 1; // Acceleration sensor ID, which specifies the sensor type. int32_t count = 0; int ret; struct SensorInformation *sensorInfo = nullptr; diff --git a/en/device-dev/driver/driver-peripherals-touch-des.md b/en/device-dev/driver/driver-peripherals-touch-des.md index 5878f9a7cfc39a83ac63af557232a22c8ca1b8fc..292e94bffd5112e952d26e4b4fa8e7ee788a0460 100644 --- a/en/device-dev/driver/driver-peripherals-touch-des.md +++ b/en/device-dev/driver/driver-peripherals-touch-des.md @@ -1,397 +1,508 @@ -# Touchscreen +# Touchscreen -## Overview -- **Functions of the Touchscreen driver** +## Overview - The touchscreen driver is used to power on its integrated circuit \(IC\), configure and initialize hardware pins, register interrupts, configure Inter-Integrated Circuit \(I2C\) or SPI APIs, set input-related configurations, and download and update firmware. +### Function Introduction +The touchscreen driver powers on its integrated circuit (IC), initializes hardware pins, registers interrupts, configures the communication (I2C or SPI) interface, sets input configurations, and downloads and updates firmware. -- **Layers of the Touchscreen driver** +The touchscreen driver is developed based on the OpenHarmony input driver model, which applies basic APIs of the operating system abstraction layer (OSAL) and platform interface layer on the OpenHarmony Hardware Driver Foundation [(HDF)](../driver/driver-hdf-development.md). Common APIs include the bus communication APIs and OS native APIs (such as memory, lock, thread, and timer APIs). The OSAL and platform APIs shield the differences of underlying hardware. This allows the use of the touchscreen driver across platforms and OSs. In this regard, you can develop the touchscreen driver only once and deploy it on multiple devices. - This section describes how to develop the touchscreen driver based on the input driver model. [Figure 1](#fig6251184817261) shows an overall architecture of the touchscreen driver. +### Working Principles - The input driver is developed based on the hardware driver foundation \(HDF\), platform APIs, and operating system abstraction layer \(OSAL\) APIs. It provides hardware driver capabilities through the input Hardware Device Interfaces \(HDIs\) for upper-layer input services to control the touchscreen. +The input driver model is developed based on the HDF and APIs of the platform and OSAL. It provides hardware driver capabilities through the input Hardware Driver Interface (HDI) for upper-layer input services to control the touchscreen. The following figure shows the architecture of the input driver model. +**Figure 1** Input driver model -**Figure 1** Architecture of the input driver model -![](figures/architecture-of-the-input-driver-model.png "architecture-of-the-input-driver-model") +![image](figures/architecture-of-the-input-driver-model.png) -- **Input driver model** +The input driver model consists of the following: - The input driver model mainly consists of the device manager, common drivers, and chip drivers. The platform data channel provides capabilities for sending data generated by the touchscreen from the kernel to the user space. The driver model adapts to different touchscreen devices and hardware platforms via the configuration file, improving the efficiency of the touchscreen development. The description for each part of the input driver model is as follows: +- Input Device Manager: provides APIs for input device drivers to register and deregister input devices and manages the input device list in a unified manner. +- Common input drivers: provide common APIs that are applicable to different input devices (such as the common driver APIs for touchscreens). The APIs can be used to initialize board-specific hardware, handle hardware interrupts, and register input devices with the Input Device Manager. +- Input chip drivers: provide differentiated APIs for the drivers form different vendors. You can use these APIs to develop your drivers with minimum modification. +- Event Hub: provides a unified channel for different input devices to report input events. +- HDF input config: parses and manages the board-specific and private configuration of input devices. - - Input device manager: provides input device drivers with the APIs for registering or unregistering input devices and manages the input device list. +The input driver model provides configuration files to help you quickly develop your drivers. - - Input common driver: provides common abstract drivers \(such as the touchscreen common driver\) of various input devices for initializing the board-level hardware, processing hardware interrupts, and registering input devices with the input device manager. - - Input chip driver: provides different chip drivers of each vendor. You can minimize the workload for the input chip driver development by calling differentiated APIs reserved by the input platform driver. +## How to Develop - - Event hub: provides a unified data reporting channel, which enables input devices to report input events. +### When to Use - - HDF input config: parses and manages the board-level configuration as well as the private configuration of input devices. +The input module provides APIs for powering on the touchscreen driver IC, configuring and initializing hardware pins, registering interrupts, configuring the communication (I2C or SPI) interface, setting input configurations, and downloading and updating firmware. +### Available APIs -- **Advantages of developing drivers based on the HDF** +#### Hardware Interfaces - The touchscreen driver is developed based on the [HDF](driver-hdf-development.md) and is implemented via calls to the OSAL and platform APIs, including bus APIs and OS native APIs \(such as memory, lock, thread, and timer\). The OSAL and platform APIs hide the differences of underlying hardware, so that the touchscreen driver can be migrated across platforms and OSs. In this regard, you can develop the touchscreen driver only once but deploy it on multiple devices. +The hardware interfaces for touchscreens can be classified into the following types based on the pin attributes: +- Power interfaces -## Available APIs +- I/O control interfaces -Based on the attributes of the pins, interfaces on the touchscreens can be classified into the following types: +- Communication interfaces -- Power interfaces -- I/O control interfaces -- Communications interfaces +**Figure 2** Common touchscreen pins -**Figure 2** Common pins of the touchscreen ![](figures/common-pins-of-the-touchscreen.png "common-pins-of-the-touchscreen") -The interfaces shown in the figure are described as follows: +The interfaces shown in the preceding figure are described as follows: -- **Power interfaces** - - LDO\_1P8: 1.8 V digital circuits - - LDO\_3P3: 3.3 V analog circuits +1. **Power interfaces** - Generally, the touchscreen driver IC is separated from the LCD driver IC. In this case, the touchscreen driver IC requires both 1.8 V and 3.3 V power supplies. Nowadays, the touchscreen driver IC and LCD driver IC can be integrated. Therefore, the touchscreen, requires only the 1.8 V power supply, and the 3.3 V power required internally is supplied by the LCD VSP power \(typical value: 5.5 V\) in the driver IC. + - **LDO_1P8**: 1.8 V digital circuit + - **LDO_3P3**: 3.3 V analog circuit -- **I/O control interfaces** - - RESET: reset pin, which is used to reset the driver IC on the host when suspending or resuming the system. - - INT: interrupt pin, which needs to be set to the input direction and pull-up status during driver initialization. After detecting an external touch signal, the driver triggers the interrupt by operating the interrupt pin. The driver reads the touch reporting data in the ISR function. + If the touchscreen driver and ICD driver have its own IC, the touchscreen driver IC requires 1.8 V and 3.3 V power supplies. If the touchscreen driver and LCD driver have an integrated IC, you only need to care about the 1.8 V power supply for the touchscreen. The 3.3 V power supply required can be provided by the LCD VSP power (typically 5.5 V) in the driver IC. -- **Communications interfaces** - - I2C: Since only a small amount of touch data is reported by the touchscreen, I2C is used to transmit the reported data. For details about the I2C protocol and interfaces, see [I2C](driver-platform-i2c-des.md#section5361140416). - - SPI: In addition to touch reporting data coordinates, some vendors need to obtain basic capacitance data. Therefore, Serial Peripheral Interface \(SPI\) is used to transmit such huge amount of data. For details about the SPI protocol and interfaces, see [SPI](driver-platform-spi-des.md#overview). +2. **I/O control interfaces** + - **RESET**: pin used to reset the driver IC on the host when the kernel is put into hibernation or waken up. + - **INT**: interrupt pin, which must be set to the input pull-up state during driver initialization. After detecting an external touch signal, the driver triggers an interrupt by operating the interrupt pin. Then, the driver reads the touch reporting data in an interrupt handler. -## How to Develop +3. **Communication interfaces** -Regardless of the OS and system on a chip \(SoC\), the input driver is developed based on the HDF, platform, and OSAL APIs to provide a unified driver model for touchscreen devices. + - I2C: I2C is used if a small amount of data is reported by the touchscreen. For details about the I2C protocol and related operation APIs, see [I2C](../driver/driver-platform-i2c-des.md). + - SPI: SPI is used if a large amount of data is reported by the touchscreen. For details about the SPI protocol and related operation APIs, see [SPI](../driver/driver-platform-spi-des.md). -The following uses the touchscreen driver as an example to describe the loading process of the input driver model: +#### Software Interfaces -1. Complete the device description configuration, such as the loading priority, board-level hardware information, and private data, by referring to the existing template. +The HDI driver APIs provided for the input service can be classified into the input manager module, input reporter module, and input controller module. The following tables describe the available APIs. -2. Load the input device management driver. The input management driver is loaded automatically by the HDF to create and initialize the device manager. +- input_manager.h -3. Load the platform driver. The platform driver is loaded automatically by the HDF to parse the board-level configuration, initialize the hardware, and provide the API for registering the touchscreen. + | API | Description | + | ------------------------------------------------------------------------------------- | -------------------| + | int32_t (*OpenInputDevice)(uint32_t devIndex); | Opens an input device. | + | int32_t (*CloseInputDevice)(uint32_t devIndex); | Closes an input device. | + | int32_t (*GetInputDevice)(uint32_t devIndex, DeviceInfo **devInfo); | Obtains information about an input device.| + | int32_t (*GetInputDeviceList)(uint32_t *devNum, DeviceInfo **devList, uint32_t size); | Obtains the input device list.| -4. Load the touchscreen driver. The touchscreen driver is loaded automatically by the HDF to instantiate the touchscreen device, parse the private data, and implement differentiated APIs provided by the platform. +- input_reporter.h -5. Register the instantiated touchscreen device with the platform driver. Then bind this device to the platform driver, and complete touchscreen initialization such as interrupt registration and power-on and power-off. + | API | Description | + | ----------------------------------------------------------------------------------- | ------------------ | + | int32_t (*RegisterReportCallback)(uint32_t devIndex, InputReportEventCb *callback); | Registers a callback for an input device.| + | int32_t (*UnregisterReportCallback)(uint32_t devIndex); | Unregisters the callback for an input device.| + | void (*ReportEventPkgCallback)(const EventPackage **pkgs, uint32_t count); | Called to report input event data. | -6. Instantiate the input device and register it with the input manager after the touchscreen is initialized. - - -Perform the following steps: - -1. Add the touchscreen driver-related descriptions. - - Currently, the input driver is developed based on the HDF and is loaded and started by the HDF. Register the driver information, such as whether to load the driver and the loading priority in the configuration file. Then, the HDF starts the registered driver modules one by one. For details about the driver configuration, see [How to Develop](driver-hdf-development.md). - -2. Complete the board-level configuration and private data configuration of the touchscreen. - - Configure the required I/O pins. For example, configure a register for the I2C pin reserved for the touchscreen to use I2C for transmitting data. - -3. Implement differentiated adaptation APIs of the touchscreen. - - Use the platform APIs to perform operations for the reset pins, interrupt pins, and power based on the communications interfaces designed for boards. For details about the GPIO-related operations, see [GPIO](driver-platform-gpio-des.md#overview). - - -## Development Example - -This example describes how to develop the touchscreen driver. - -### Adding the Touchscreen Driver-related Description - -The information about modules of the input driver model is shown as follows and enables the HDF to load the modules in sequence. For details, see [Driver Development](driver-hdf-development.md). - -``` -input :: host { - hostName = "input_host"; - priority = 100; - device_input_manager :: device { - device0 :: deviceNode { - policy = 2; // Publish services externally. - priority = 100; // Loading priority. The input device manager in the input driver has the highest priority. - preload = 0; // Value 0 indicates that the driver is to be loaded, and value 1 indicates the opposite. - permission = 0660; - moduleName = "HDF_INPUT_MANAGER"; - serviceName = "input_dev_manager"; - deviceMatchAttr = ""; - } - } - device_hdf_touch :: device { - device0 :: deviceNode { - policy = 2; - priority = 120; - preload = 0; - permission = 0660; - moduleName = "HDF_TOUCH"; - serviceName = "event1"; - deviceMatchAttr = "touch_device1"; - } - } - - device_touch_chip :: device { - device0 :: deviceNode { - policy = 0; - priority = 130; - preload = 0; - permission = 0660; - moduleName = "HDF_TOUCH_SAMPLE"; - serviceName = "hdf_touch_sample_service"; - deviceMatchAttr = "zsj_sample_5p5"; - } - } -} -``` - -### Adding Board Configuration and Touchscreen Private Configuration - -The following describes the configuration of the board-level hardware and private data of the touchscreen. You can modify the configuration based on service requirements. - -``` -root { - input_config { - touchConfig { - touch0 { - boardConfig { - match_attr = "touch_device1"; - inputAttr { - inputType = 0; // Value 0 indicates that the input device is a touchscreen. - solutionX = 480; - solutionY = 960; - devName = "main_touch"; // Device name - } - busConfig { - busType = 0; // Value 0 indicates the I2C bus. - busNum = 6; - clkGpio = 86; - dataGpio = 87; - i2cClkIomux = [0x114f0048, 0x403]; // Register configuration of the i2c_clk pin - i2cDataIomux = [0x114f004c, 0x403]; // Register configuration of the i2c_data pin - } - pinConfig { - rstGpio = 3; - intGpio = 4; - rstRegCfg = [0x112f0094, 0x400]; // Register configuration of the reset pin - intRegCfg = [0x112f0098, 0x400]; // Register configuration of the interrupt pin - } - powerConfig { - vccType = 2; // Values 1, 2, and 3 indicate the low-dropout regulator (LDO), GPIO, and PMIC, respectively. - vccNum = 20; // The GPIO number is 20. - vccValue = 1800; // The voltage amplitude is 1800 mV. - vciType = 1; - vciNum = 12; - vciValue = 3300; - } - featureConfig { - capacitanceTest = 0; - gestureMode = 0; - gloverMOde = 0; - coverMode = 0; - chargerMode = 0; - knuckleMode = 0; - } - } - chipConfig { - template touchChip { - match_attr = ""; - chipName = "sample"; - vendorName = "zsj"; - chipInfo = "AAAA11222"; // The first four characters indicate the product name. The fifth and sixth characters indicate the IC model. The last three characters indicate the chip model. - busType = 0; - deviceAddr = 0x5D; - irqFlag = 2; // Values 1 and 2 indicate that the interrupt is triggered on the rising and falling edges, respectively. Values 4 and 8 indicate that the interrupt is triggered by the high and low levels, respectively. - maxSpeed = 400; - chipVersion = 0; - powerSequence { - /* Power-on sequence is described as follows: - [Type, status, direction, delay] - Value 0 indicates the power or pin is empty. Values 1 and 2 indicate the VCC (1.8 V) and VCI (3.3 V) power, respectively. Values 3 and 4 indicate the reset and interrupt pins, respectively. - Values 0 and 1 indicate the power-off or pull-down, and the power-on or pull-up, respectively. Value 2 indicates that no operation is performed. - Values 0 and 1 indicate the input and output directions, respectively. Value 2 indicates that no operation is performed. - Delay time, in milliseconds. - */ - powerOnSeq = [4, 0, 1, 0, - 3, 0, 1, 10, - 3, 1, 2, 60, - 4, 2, 0, 0]; - suspendSeq = [3, 0, 2, 10]; - resumeSeq = [3, 1, 2, 10]; - powerOffSeq = [3, 0, 2, 10, - 1, 0, 2, 20]; - } - } - chip0 :: touchChip { - match_attr = "zsj_sample_5p5"; - chipInfo = "ZIDN45100"; - chipVersion = 0; - } - } - } - } - } -} -``` - -### Adding the Touchscreen Driver - -The following example shows how to implement the differentiated APIs provided by the platform driver to obtain and parse the touchscreen data. You can adjust the development process based on the board and touchscreen in use. - -``` -/* Parse the touch reporting data read from the touchscreen into coordinates. */ -static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum) -{ - int32_t resX = device->driver->boardCfg->attr.resolutionX; - int32_t resY = device->driver->boardCfg->attr.resolutionY; - - for (int32_t i = 0; i < pointNum; i++) { - frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) | - ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET); - frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) | - ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET); - frame->fingers[i].valid = true; - } -} -/* Obtain the touch reporting data from the chip. */ -static int32_t ChipDataHandle(ChipDevice *device) -{ - int32_t ret; - uint8_t touchStatus = 0; - uint8_t pointNum; - uint8_t buf[GT_POINT_SIZE * MAX_SUPPORT_POINT] = {0}; - InputI2cClient *i2cClient = &device->driver->i2cClient; - uint8_t reg[GT_ADDR_LEN] = {0}; - FrameData *frame = &device->driver->frameData; - reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK; - reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK; - ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1); - if (ret < 0 || touchStatus == GT_EVENT_INVALID) { - return HDF_FAILURE; - } - OsalMutexLock(&device->driver->mutex); - (void)memset_s(frame, sizeof(FrameData), 0, sizeof(FrameData)); - if (touchStatus == GT_EVENT_UP) { - frame->realPointNum = 0; - frame->definedEvent = TOUCH_UP; - goto exit; - } - reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK; - reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK; - pointNum = touchStatus & GT_FINGER_NUM_MASK; - if (pointNum <= 0 || pointNum > MAX_SUPPORT_POINT) { - HDF_LOGE("%s: pointNum is invalid, %d", __func__, pointNum); - (void)ChipCleanBuffer(i2cClient); - OsalMutexUnlock(&device->driver->mutex); - return HDF_FAILURE; - } - frame->realPointNum = pointNum; - frame->definedEvent = TOUCH_DOWN; - /* Read the touch reporting data from the register. */ - (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum); - /* Parse the touch reporting data. */ - ParsePointData(device, frame, buf, pointNum); -exit: - OsalMutexUnlock(&device->driver->mutex); - if (ChipCleanBuffer(i2cClient) != HDF_SUCCESS) { - return HDF_FAILURE; - } - return HDF_SUCCESS; -} - -static struct TouchChipOps g_sampleChipOps = { - .Init = ChipInit, - .Detect = ChipDetect, - .Resume = ChipResume, - .Suspend = ChipSuspend, - .DataHandle = ChipDataHandle, -}; - -static TouchChipCfg *ChipConfigInstance(struct HdfDeviceObject *device) -{ - TouchChipCfg *chipCfg = (TouchChipCfg *)OsalMemAlloc(sizeof(TouchChipCfg)); - if (chipCfg == NULL) { - HDF_LOGE("%s: instance chip config failed", __func__); - return NULL; - } - (void)memset_s(chipCfg, sizeof(TouchChipCfg), 0, sizeof(TouchChipCfg)); - /* Parse the private configuration of the touchscreen. */ - if (ParseTouchChipConfig(device->property, chipCfg) != HDF_SUCCESS) { - HDF_LOGE("%s: parse chip config failed", __func__); - OsalMemFree(chipCfg); - chipCfg = NULL; - } - return chipCfg; -} - -static ChipDevice *ChipDeviceInstance(void) -{ - ChipDevice *chipDev = (ChipDevice *)OsalMemAlloc(sizeof(ChipDevice)); - if (chipDev == NULL) { - HDF_LOGE("%s: instance chip device failed", __func__); - return NULL; - } - (void)memset_s(chipDev, sizeof(ChipDevice), 0, sizeof(ChipDevice)); - return chipDev; -} - -static void FreeChipConfig(TouchChipCfg *config) -{ - if (config->pwrSeq.pwrOn.buf != NULL) { - OsalMemFree(config->pwrSeq.pwrOn.buf); - } - if (config->pwrSeq.pwrOff.buf != NULL) { - OsalMemFree(config->pwrSeq.pwrOff.buf); - } - OsalMemFree(config); -} - -static int32_t HdfSampleChipInit(struct HdfDeviceObject *device) -{ - TouchChipCfg *chipCfg = NULL; - ChipDevice *chipDev = NULL; - HDF_LOGE("%s: enter", __func__); - if (device == NULL) { - return HDF_ERR_INVALID_PARAM; - } - /* Parse the private configuration of the touchscreen. */ - chipCfg = ChipConfigInstance(device); - if (chipCfg == NULL) { - return HDF_ERR_MALLOC_FAIL; - } - /* Instantiate the touchscreen device. */ - chipDev = ChipDeviceInstance(); - if (chipDev == NULL) { - goto freeCfg; - } - chipDev->chipCfg = chipCfg; - chipDev->ops = &g_sampleChipOps; - chipDev->chipName = chipCfg->chipName; - chipDev->vendorName = chipCfg->vendorName; - - /* Register the touchscreen device with the platform driver. */ - if (RegisterChipDevice(chipDev) != HDF_SUCCESS) { - goto freeDev; - } - HDF_LOGI("%s: exit succ, chipName = %s", __func__, chipCfg->chipName); - return HDF_SUCCESS; - -freeDev: - OsalMemFree(chipDev); -freeCfg: - FreeChipConfig(chipCfg); - return HDF_FAILURE; -} - -struct HdfDriverEntry g_touchSampleChipEntry = { - .moduleVersion = 1, - .moduleName = "HDF_TOUCH_SAMPLE", - .Init = HdfSampleChipInit, -}; - -HDF_INIT(g_touchSampleChipEntry); -``` +- input_controller.h + | API | Description | + | --------------------------------------------------------------------------------------------------- |--------------- | + | int32_t (*SetPowerStatus)(uint32_t devIndex, uint32_t status); | Sets the power status. | + | int32_t (*GetPowerStatus)(uint32_t devIndex, uint32_t *status); | Obtains the power status. | + | int32_t (*GetDeviceType)(uint32_t devIndex, uint32_t *deviceType); | Obtains the device type. | + | int32_t (*GetChipInfo)(uint32_t devIndex, char *chipInfo, uint32_t length); | Obtains the chip information of a device.| + | int32_t (*GetVendorName)(uint32_t devIndex, char *vendorName, uint32_t length); | Obtains the module vendor name of a device. | + | int32_t (*GetChipName)(uint32_t devIndex, char *chipName, uint32_t length); | Obtains the driver chip name of a device. | + | int32_t (*SetGestureMode)(uint32_t devIndex, uint32_t gestureMode); | Sets the gesture mode. | + | int32_t (*RunCapacitanceTest)(uint32_t devIndex, uint32_t testType, char *result, uint32_t length); | Performs a capacitance test.| + | int32_t (*RunExtraCommand)(uint32_t devIndex, InputExtraCmd *cmd); | Executes the specified command. | + +For more information, see [input](https://gitee.com/openharmony/drivers_peripheral/tree/master/input). + +### Development Procedure + +The load process of the input driver model (for the touchscreen driver) is as follows: + +1. The device configuration, including the driver loading priority, board-specific hardware information, and private data, is complete. + +2. The HDF driver loads the input device manager driver to create and initialize the device manager. + +3. The HDF loads the platform driver to parse the board-specific configuration, initialize the hardware, and provide the API for registering the touchscreen. + +4. The HDF loads the touchscreen driver to instantiate the touchscreen device, parse the private data, and implement the differentiated APIs for the platform. + +5. The instantiated touchscreen device registers with the platform driver to bind the device and the driver and complete the device initialization, including interrupt registration and device power-on and power-off. + +6. The instantiated input device registers with the input device manager for unified management. + + +The development process of the touchscreen driver is as follows: + +1. Configure device information.
The input driver is developed based on the HDF. The HDF loads and starts the driver in a unified manner. You need to configure the driver information, such as whether to load the driver and the loading priority, in the configuration file. Then, the HDF starts the registered driver modules one by one. For details about how to configure the driver, see [Driver Development](../driver/driver-hdf-development.md#how-to-develop). + +2. Configure board-specific information and touchscreen private information.
Configure the I/O pin functions. For example, set registers for the I2C pins on the board for the touchscreen to enable I2C communication. + +3. Implement device-specific APIs.
Based on the communication interfaces designed for the board, use the pin operation APIs provided by the platform interface layer to configure the corresponding reset pin, interrupt pin, and power operations. For details about GPIO operations, see [GPIO](../driver/driver-platform-gpio-des.md). + + +### Development Example + +The following example describes how to develop the touchscreen driver for an RK3568 development board. + +1. Configure device information. + + Configure the modules of the input driver model in **drivers/adapter/khdf/linux/hcs/device_info/device_info.hcs**. For details, see [Driver Development](../driver/driver-hdf-development.md). Then, the HDF loads the modules of the input model in sequence based on the configuration information. + + ```c + input :: host { + hostName = "input_host"; + priority = 100; + device_input_manager :: device { + device0 :: deviceNode { + policy = 2; // The driver provides services externally. + priority = 100; // Loading priority. In the input model, the manager module has the highest priority. + preload = 0; // Whether to load the driver. The value 0 means to load the driver; 1 means the opposite. + permission = 0660; + moduleName = "HDF_INPUT_MANAGER"; + serviceName = "input_dev_manager"; + deviceMatchAttr = ""; + } + } + device_hdf_touch :: device { + device0 :: deviceNode { + policy = 2; + priority = 120; + preload = 0; + permission = 0660; + moduleName = "HDF_TOUCH"; + serviceName = "event1"; + deviceMatchAttr = "touch_device1"; + } + } + + device_touch_chip :: device { + device0 :: deviceNode { + policy = 0; + priority = 130; + preload = 0; + permission = 0660; + moduleName = "HDF_TOUCH_SAMPLE"; + serviceName = "hdf_touch_sample_service"; + deviceMatchAttr = "zsj_sample_5p5"; + } + } + } + ``` + +2. Configure board-specific and private data for the touchscreen. + + Configure the data in **drivers/adapter/khdf/linux/hcs/input/input_config.hcs**. The following is an example. You can modify the configuration as required. + + ```c + root { + input_config { + touchConfig { + touch0 { + boardConfig { + match_attr = "touch_device1"; + inputAttr { + inputType = 0; // 0 indicates touchscreen. + solutionX = 480; + solutionY = 960; + devName = "main_touch"; // Device name. + } + busConfig { + busType = 0; // 0 indicates I2C. + busNum = 6; + clkGpio = 86; + dataGpio = 87; + i2cClkIomux = [0x114f0048, 0x403]; // Register of the I2C_CLK pin. + i2cDataIomux = [0x114f004c, 0x403]; // Register of the I2C_DATA pin. + } + pinConfig { + rstGpio = 3; + intGpio = 4; + rstRegCfg = [0x112f0094, 0x400]; // Register of the reset pin. + intRegCfg = [0x112f0098, 0x400]; // Register of the interrupt pin. + } + powerConfig { + vccType = 2; // The value 1 stands for LDO, 2 for GPIO, and 3 for PMIC. + vccNum = 20; // Set the GPIO number to 20. + vccValue = 1800; // Set the voltage amplitude to 1800 mV. + vciType = 1; + vciNum = 12; + vciValue = 3300; + } + featureConfig { + capacitanceTest = 0; + gestureMode = 0; + gloverMOde = 0; + coverMode = 0; + chargerMode = 0; + knuckleMode = 0; + } + } + chipConfig { + template touchChip { + match_attr = ""; + chipName = "sample"; + vendorName = "zsj"; + chipInfo = "AAAA11222"; // The first four characters indicate the product name. The fifth and sixth characters indicate the IC model. The last three characters indicate the model number. + busType = 0; + deviceAddr = 0x5D; + irqFlag = 2; // The value 1 means to trigger an interrupt on the rising edge, 2 means to trigger an interrupt on the falling edge, 4 means to trigger an interrupt by the high level, and 8 means to trigger an interrupt by the low level. + maxSpeed = 400; + chipVersion = 0; + powerSequence { + /* Description of the power-on sequence: + [type, status, direction, delay] + 0 stands for null; 1 for VCC power (1.8 V); 2 for VCI power (3.3 V); 3 for reset pin; 4 for interrupt pin. + 0 stands for power-off or pull-down; 1 for power-on or pull-up; 2 for no operation. + 0 stands for input; 1 for output; 2 for no operation. + indicates the delay, in milliseconds. For example, 20 indicates 20 ms delay. + */ + powerOnSeq = [4, 0, 1, 0, + 3, 0, 1, 10, + 3, 1, 2, 60, + 4, 2, 0, 0]; + suspendSeq = [3, 0, 2, 10]; + resumeSeq = [3, 1, 2, 10]; + powerOffSeq = [3, 0, 2, 10, + 1, 0, 2, 20]; + } + } + chip0 :: touchChip { + match_attr = "zsj_sample_5p5"; + chipInfo = "ZIDN45100"; + chipVersion = 0; + } + } + } + } + } + } + ``` + +3. Add the touchscreen driver. + + Implement the touchscreen-specific APIs in **divers/framework/model/input/driver/touchscreen/touch_gt911.c**. The following uses the APIs for obtaining and parsing device data as an example. You can implement the related APIs to match your development. + + ```c + /* Parse the touch reporting data read from the touchscreen into coordinates. */ + static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum) + { + int32_t resX = device->driver->boardCfg->attr.resolutionX; + int32_t resY = device->driver->boardCfg->attr.resolutionY; + + for (int32_t i = 0; i < pointNum; i++) { + frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) | + ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET); + frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) | + ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET); + frame->fingers[i].valid = true; + } + } + /* Obtain the touch reporting data from the device. */ + static int32_t ChipDataHandle(ChipDevice *device) + { + int32_t ret; + uint8_t touchStatus = 0; + uint8_t pointNum; + uint8_t buf[GT_POINT_SIZE * MAX_SUPPORT_POINT] = {0}; + InputI2cClient *i2cClient = &device->driver->i2cClient; + uint8_t reg[GT_ADDR_LEN] = {0}; + FrameData *frame = &device->driver->frameData; + reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK; + reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK; + ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1); + if (ret < 0 || touchStatus == GT_EVENT_INVALID) { + return HDF_FAILURE; + } + OsalMutexLock(&device->driver->mutex); + (void)memset_s(frame, sizeof(FrameData), 0, sizeof(FrameData)); + if (touchStatus == GT_EVENT_UP) { + frame->realPointNum = 0; + frame->definedEvent = TOUCH_UP; + goto exit; + } + reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK; + reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK; + pointNum = touchStatus & GT_FINGER_NUM_MASK; + if (pointNum <= 0 || pointNum > MAX_SUPPORT_POINT) { + HDF_LOGE("%s: pointNum is invalid, %d", __func__, pointNum); + (void)ChipCleanBuffer(i2cClient); + OsalMutexUnlock(&device->driver->mutex); + return HDF_FAILURE; + } + frame->realPointNum = pointNum; + frame->definedEvent = TOUCH_DOWN; + /* Read the touch reporting data from the register. */ + (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum); + /* Parse the touch reporting data. */ + ParsePointData(device, frame, buf, pointNum); + exit: + OsalMutexUnlock(&device->driver->mutex); + if (ChipCleanBuffer(i2cClient) != HDF_SUCCESS) { + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + static struct TouchChipOps g_sampleChipOps = { + .Init = ChipInit, + .Detect = ChipDetect, + .Resume = ChipResume, + .Suspend = ChipSuspend, + .DataHandle = ChipDataHandle, + }; + + static TouchChipCfg *ChipConfigInstance(struct HdfDeviceObject *device) + { + TouchChipCfg *chipCfg = (TouchChipCfg *)OsalMemAlloc(sizeof(TouchChipCfg)); + if (chipCfg == NULL) { + HDF_LOGE("%s: instance chip config failed", __func__); + return NULL; + } + (void)memset_s(chipCfg, sizeof(TouchChipCfg), 0, sizeof(TouchChipCfg)); + /* Parse the touchscreen private configuration. */ + if (ParseTouchChipConfig(device->property, chipCfg) != HDF_SUCCESS) { + HDF_LOGE("%s: parse chip config failed", __func__); + OsalMemFree(chipCfg); + chipCfg = NULL; + } + return chipCfg; + } + + static ChipDevice *ChipDeviceInstance(void) + { + ChipDevice *chipDev = (ChipDevice *)OsalMemAlloc(sizeof(ChipDevice)); + if (chipDev == NULL) { + HDF_LOGE("%s: instance chip device failed", __func__); + return NULL; + } + (void)memset_s(chipDev, sizeof(ChipDevice), 0, sizeof(ChipDevice)); + return chipDev; + } + + static void FreeChipConfig(TouchChipCfg *config) + { + if (config->pwrSeq.pwrOn.buf != NULL) { + OsalMemFree(config->pwrSeq.pwrOn.buf); + } + if (config->pwrSeq.pwrOff.buf != NULL) { + OsalMemFree(config->pwrSeq.pwrOff.buf); + } + OsalMemFree(config); + } + + static int32_t HdfSampleChipInit(struct HdfDeviceObject *device) + { + TouchChipCfg *chipCfg = NULL; + ChipDevice *chipDev = NULL; + HDF_LOGE("%s: enter", __func__); + if (device == NULL) { + return HDF_ERR_INVALID_PARAM; + } + /* Parse the touchscreen private configuration. */ + chipCfg = ChipConfigInstance(device); + if (chipCfg == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + /* Instantiate the touchscreen device. */ + chipDev = ChipDeviceInstance(); + if (chipDev == NULL) { + goto freeCfg; + } + chipDev->chipCfg = chipCfg; + chipDev->ops = &g_sampleChipOps; + chipDev->chipName = chipCfg->chipName; + chipDev->vendorName = chipCfg->vendorName; + + /* Register the touchscreen device with the platform driver. */ + if (RegisterChipDevice(chipDev) != HDF_SUCCESS) { + goto freeDev; + } + HDF_LOGI("%s: exit succ, chipName = %s", __func__, chipCfg->chipName); + return HDF_SUCCESS; + + freeDev: + OsalMemFree(chipDev); + freeCfg: + FreeChipConfig(chipCfg); + return HDF_FAILURE; + } + + struct HdfDriverEntry g_touchSampleChipEntry = { + .moduleVersion = 1, + .moduleName = "HDF_TOUCH_SAMPLE", + .Init = HdfSampleChipInit, + }; + + HDF_INIT(g_touchSampleChipEntry); + ``` + +4. Call the Input HDI APIs. + + The following sample code shows how an upper-layer input system service calls Input HDI APIs. + + ```c + #include "input_manager.h" + #define DEV_INDEX 1 + + IInputInterface *g_inputInterface; + InputReportEventCb g_callback; + + /* Define the callback for data reporting. */ + static void ReportEventPkgCallback(const EventPackage **pkgs, uint32_t count) + { + if (pkgs == NULL || count > MAX_PKG_NUM) { + return; + } + for (uint32_t i = 0; i < count; i++) { + HDF_LOGI("%s: pkgs[%d] = 0x%x, 0x%x, %d", __func__, i, pkgs[i]->type, pkgs[i]->code, pkgs[i]->value); + } + } + + int InputServiceSample(void) + { + uint32_t devType = INIT_DEFAULT_VALUE; + + /* Obtain the input driver APIs. */ + int ret = GetInputInterface(&g_inputInterface); + if (ret != INPUT_SUCCESS) { + HDF_LOGE("%s: get input interfaces failed, ret = %d", __func__, ret); + return ret; + } + + INPUT_CHECK_NULL_POINTER(g_inputInterface, INPUT_NULL_PTR); + INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputManager, INPUT_NULL_PTR); + /* Open an input device. */ + ret = g_inputInterface->iInputManager->OpenInputDevice(DEV_INDEX); + if (ret) { + HDF_LOGE("%s: open input device failed, ret = %d", __func__, ret); + return ret; + } + + INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputController, INPUT_NULL_PTR); + /* Obtain the type of the input device. */ + ret = g_inputInterface->iInputController->GetDeviceType(DEV_INDEX, &devType); + if (ret) { + HDF_LOGE("%s: get device type failed, ret: %d", __FUNCTION__, ret); + return ret; + } + HDF_LOGI("%s: device1's type is %u\n", __FUNCTION__, devType); + + /* Register the data reporting callback for the input device. */ + g_callback.ReportEventPkgCallback = ReportEventPkgCallback; + INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputReporter, INPUT_NULL_PTR); + ret = g_inputInterface->iInputReporter->RegisterReportCallback(DEV_INDEX, &g_callback); + if (ret) { + HDF_LOGE("%s: register callback failed, ret: %d", __FUNCTION__, ret); + return ret; + } + HDF_LOGI("%s: wait 10s for testing, pls touch the panel now", __FUNCTION__); + OsalMSleep(KEEP_ALIVE_TIME_MS); + + /* Unregister the callback for the input device. */ + ret = g_inputInterface->iInputReporter->UnregisterReportCallback(DEV_INDEX); + if (ret) { + HDF_LOGE("%s: unregister callback failed, ret: %d", __FUNCTION__, ret); + return ret; + } + + /* Close the input device. */ + ret = g_inputInterface->iInputManager->CloseInputDevice(DEV_INDEX); + if (ret) { + HDF_LOGE("%s: close device failed, ret: %d", __FUNCTION__, ret); + return ret; + } + return 0; + } + ``` diff --git a/en/device-dev/driver/driver-peripherals-usb-des.md b/en/device-dev/driver/driver-peripherals-usb-des.md index f2b3ba9b44beaafbfd4cd4c83b33d647a1f75744..782684f470029435d0a3f1e3545f493f5844890d 100644 --- a/en/device-dev/driver/driver-peripherals-usb-des.md +++ b/en/device-dev/driver/driver-peripherals-usb-des.md @@ -1,224 +1,452 @@ # USB +## Introduction -## Overview +### Function Overview -USB host development aims to provide host-related functions, including protocol encapsulation, device management, and driver installation and uninstall. +The universal serial bus (USB) consists of a USB host and multiple USB devices. The USB host implement data transfer and port management in the USB bus, and the USB device can connect to various peripherals. Therefore, USB driver development is divided into USB host driver development and USB device driver development. -USB device development aims to provide device-related functions, including device management, configuration management, and I/O management. These functions implement creation, configuration, and data communication of USB devices. +The USB module of OpenHarmony supports the development of USB services, provides USB-related functions, provides interfaces to read and write USB device data of third-party function drivers in user mode, creates and deletes USB devices, obtains notification events, enables or disables event listening, implements non-isochronous and isochronous data transfer over USB pipes, and sets custom USB attributes. - The following figures show the USB host and device driver models. +The USB DriverDevelop Kit (DDK) is the USB driver development kit provided by the Framework of the OpenHarmony Driver Foundation (HDF). This kit consists of the USB Host DDK and USB Device DDK. It supports the development of USB device drivers based on the user mode and provides rich USB driver development capabilities that help you to efficiently develop USB drivers. + +### Basic Concepts + +- Pipe + + A pipe is a model for data transfer between the USB host and a device endpoint. Once a USB device is powered on, a pipe, that is, the default control pipe, is established. The USB host obtains the description, configuration, and status of the USB device through the pipe, and configures the device as requested. Pipes and endpoints are associated and share the same attributes, such as the supported transfer type, maximum packet length, and data transfer direction. + +- Endpoint + + The minimum unit that transfers and receives data in a USB device. It supports unidirectional or bidirectional data transfer. One USB device may include several endpoints, and different endpoints are distinguished by endpoint numbers and directions. Different endpoints can support different data transfer types, access intervals, and maximum packet sizes. All endpoints except endpoint 0 support data transfer in only one direction. Endpoint 0 is a special endpoint that supports bidirectional control transfer. + +- Interface + + The application implements device control and data transfer through exchanging data with the device. Because a pipe supports only one data transfer type, multiple pipes are usually required to complete data exchange in this process. A collection of pipes that are used together to control a device is called an interface. + +- Descriptor + + A data structure used to describe device attributes. The first byte indicates the descriptor size (number of bytes), and the second byte indicates the descriptor type. + +### Working Principles + +#### USB Host DDK + +The USB Host DDK provides the capability of developing USB drivers on the host. Based on functions, APIs of the USB Host DDK are classified into three types: DDK initialization, **interface** object operation, and **request** object operation. **Figure 1** USB host driver model ![](figures/USB_host_driver_model.png "USB host driver model") - +- The USB Interface Pool module manages USB interfaces. It applies for and reclaims USB interface objects, which are used to record device port information and resources. The module manages USB interfaces by USB port. In addition, it provides USB DDK APIs to read and write USB data. + +- The USB Protocol Layer module provides USB protocol encapsulation, translates and parses device I/O and control commands based on the USB protocol, manages device descriptors, and matches descriptors based on the enum information reported by the USB device. This module creates the corresponding USB interface objects and adds them to the USB Interface Pool module for management. + +- The Device I/O Manager module manages USB I/O requests and provides synchronous and asynchronous I/O management mechanisms. For the asynchronous I/O management mechanism, the module records the asynchronous I/O requests and processes the requests to be sent through the APIs provided by the Raw API Library module. After receiving the processing result from the USB controller, the I/O request receiving thread parses the processing result and reports it to the upper-layer caller. + +- The Raw API Library module abstracts underlying OS capabilities, defines unified OS capability APIs, and provides the USB RAW APIs needed to implement more complex driver functions. + +- The OS Adapter module encapsulates operations related to platforms (Linux and LiteOS). It compiles encapsulation APIs depending on the configuration of the specific platform. On the Linux platform, all USB FS access operations are encapsulated in this module. On the LiteOS platform, all device access operations based on the FreeBSD USB framework are encapsulated in this module. + +- The PNP Notify module dynamically monitors USB status changes. This module updates the device information when a device is added or removed. Meanwhile, it reports all USB device information to the PNP Notify Manager module on the UHDF side through the KHDF to load or uninstall third-party function drivers. + +#### USB Device DDK + +The USB Device DDK provides the capability of developing USB drivers on the device side. For example, with the dynamic registration and deregistration capabilities, you can dynamically add and combine USB ports based on the actual requirement; with the dynamic instantiation capability, you can create device instances and transmission channels based on dynamically delivered device, configuration, interface, and endpoint descriptors. In addition, the following functions are supported: sending and receiving data in user mode, isolating multiple logical devices from each other on a physical device, and accessing different logical devices from different application processes at the same time. + **Figure 2** USB device driver model ![](figures/USB_device_driver_model.png "USB device driver model") -The USB driver model offers the following APIs: +- The SDK IF module divides USB devices logically by device, interface, and pipe, and encapsulates functions including configuration management, device management, and I/O management. This module also provides APIs for device driver development, such as creating and obtaining devices, receiving events, and sending and receiving data. -- The USB host Driver Development Kit (DDK) provides driver capability APIs that can be directly called in user mode. The APIs can be classified into the DDK initialization class, interface operation class, and request operation class by function. These APIs can be used to perform DDK initialization, bind/release and open/close an interface, allocate/release a request, and implement synchronous or asynchronous transfer. +- The Configuration Manager module parses the .hcs file for the USB descriptor information, which will be used for creating USB devices. In addition, the module provides operations such as reading, creating, deleting, and modifying custom USB attributes. -- The USB device DDK provides device management, I/O management, and configuration management APIs, which can be used to create or delete a device, obtain or open an interface, and perform synchronous or asynchronous transfer. +- The Device Manager module parses USB descriptor information and creates USB devices accordingly. It also provides functions such as adding or deleting USB devices, obtaining USB device status, and obtaining USB device interface information. +- The IO Manager module reads and writes data, including common events and data read and write events. It supports data read and write in synchronous and asynchronous modes. -### Available APIs +- The Adapter IF module encapsulates device node operations of composite device configuration drivers and common function drivers to provide unified device management APIs for the upper layer. -The tables below describe the APIs provided by the USB host driver model. +- The Adapter module is provided by the composite device configuration driver and common function driver. - **Table 1** usb_ddk_interface.h +## Development Guidelines -| API| Description| -| -------- | -------- | -| int32_t UsbInitHostSdk(struct UsbSession \*\*session); | Initializes the USB host driver DDK.| -| int32_t UsbExitHostSdk(const struct UsbSession
\*session); | Exits the USB host driver DDK.| -| const struct UsbInterface \*UsbClaimInterface(const
struct UsbSession \*session, uint8_t busNum, uint8_t
usbAddr, uint8_t interfaceIndex); | Obtains a USB interface.| -| int UsbReleaseInterface(const struct UsbInterface
\*interfaceObj); | Releases a USB interface.| -| int UsbAddOrRemoveInterface(const struct UsbSession
\*session, uint8_t busNum, uint8_t usbAddr, uint8_t
interfaceIndex, UsbInterfaceStatus status); | Adds or removes a USB interface.| -| UsbInterfaceHandle \*UsbOpenInterface(const struct
UsbInterface \*interfaceObj); | Opens a USB interface.| -| int32_t UsbCloseInterface(const UsbInterfaceHandle
\*interfaceHandle); | Closes a USB interface.| -| int32_t UsbSelectInterfaceSetting(const
UsbInterfaceHandle \*interfaceHandle, uint8_t
settingIndex, struct UsbInterface \*\*interfaceObj); | Sets a USB interface.| -| int32_t UsbGetPipeInfo(const UsbInterfaceHandle
\*interfaceHandle, uint8_t settingIndex, uint8_t pipeId,
struct UsbPipeInfo \*pipeInfo); | Obtains USB pipe information.| -| int32_t UsbClearInterfaceHalt(const
UsbInterfaceHandle \*interfaceHandle, uint8_t
pipeAddress); | Clears the state of the pipe with the specified index.| -| struct UsbRequest \*UsbAllocRequest(const
UsbInterfaceHandle \*interfaceHandle, int isoPackets
, int length); | Allocates a request object.| -| int UsbFreeRequest(const struct UsbRequest
\*request); | Releases a request object.| -| int UsbSubmitRequestAsync(const struct UsbRequest
\*request); | Sends an asynchronous request.| -| int32_t UsbFillRequest(const struct UsbRequest
\*request, const UsbInterfaceHandle \*interfaceHandle,
const struct UsbRequestParams \*params); | Fills in a request.| -| sint UsbCancelRequest(const struct UsbRequest
\*request); | Cancels an asynchronous request.| -| int UsbSubmitRequestSync(const struct UsbRequest
\*request); | Sends a synchronous request.| - - **Table 2** usb_raw_api.h - -| API| Description| -| -------- | -------- | -| int UsbRawInit(struct UsbSession \*\*session); | Initializes the USB raw APIs.| -| int UsbRawExit(const struct UsbSession \*session); | Exits the USB raw APIs.| -| UsbRawHandle \*UsbRawOpenDevice(const struct
UsbSession \*session, uint8_t busNum, uint8_t
usbAddr); | Opens a USB device.| -| int UsbRawCloseDevice(const UsbRawHandle
\*devHandle); | Closes a USB device.| -| int UsbRawSendControlRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbControlRequestData
\*requestData); | Performs a control transfer synchronously.| -| int UsbRawSendBulkRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbRequestData
\*requestData); | Performs a bulk transfer synchronously.| -| int UsbRawSendInterruptRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbRequestData
\*requestData); | Performs an interrupt transfer synchronously.| -| int UsbRawGetConfigDescriptor(const UsbRawDevice
\*rawDev, uint8_t configIndex, struct
UsbRawConfigDescriptor \*\*config); | Obtains the configuration descriptor of a device.| -| void UsbRawFreeConfigDescriptor(const struct
UsbRawConfigDescriptor \*config); | Releases the memory space of a configuration descriptor.| -| int UsbRawGetConfiguration(const UsbRawHandle
\*devHandle, int \*config); | Obtains the configuration in use.| -| int UsbRawSetConfiguration(const UsbRawHandle
\*devHandle, int config); | Sets the configuration in use.| -| int UsbRawGetDescriptor(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawDescriptorParam \*param, const unsigned char
\*data); | Obtains descriptor information.| -| UsbRawDevice \*UsbRawGetDevice(const UsbRawHandle
\*devHandle); | Obtains the device pointer based on the device handle.| -| int UsbRawGetDeviceDescriptor(const UsbRawDevice
\*rawDev, struct
UsbDeviceDescriptor \*desc); | Obtains the device descriptor of the specified USB device.| -| int UsbRawClaimInterface(const UsbRawHandle
\*devHandle, int
interfaceNumber); | Declares the interface on the specified device handle.| -| int UsbRawReleaseInterface(const UsbRawHandle
\*devHandle, in
t interfaceNumber); | Releases the previously declared interface.| -| int UsbRawResetDevice(const UsbRawHandle
\*devHandle); | Resets a device.| -| struct UsbRawRequest \*UsbRawAllocRequest(const
UsbRawHandle
\*devHandle, int isoPackets, int length); | Allocates a transfer request with the specified number of sync packet descriptors.| -| int UsbRawFreeRequest(const struct UsbRawRequest
\*request); | Releases the previously allocated transfer request.| -| int UsbRawFillBulkRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in a bulk transfer request.| -| int UsbRawFillControlSetup(const unsigned char \*setup,
const struct UsbControlRequestData \*requestData); | Fills in a control setup packet.| -| int UsbRawFillControlRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in a control transfer request.| -| int UsbRawFillInterruptRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in an interrupt transfer request.| -| int UsbRawFillIsoRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in an isochronous transfer request.| -| int UsbRawSubmitRequest(const struct UsbRawRequest
\*request); | Submits a transfer request.| -| int UsbRawCancelRequest(const struct UsbRawRequest
\*request); | Cancels a transfer request.| -| int UsbRawHandleRequests(const UsbRawHandle
\*devHandle); | Handles a transfer request event.| - -The tables below describe the APIs provided by the USB device driver model. - - **Table 3** usbfn_device.h - -| API| Description| -| -------- | -------- | -| const struct UsbFnDevice \*UsbFnCreateDevice(const
char \*udcName, const struct UsbFnDescriptorData
\*descriptor); | Creates a USB device.| -| int UsbFnRemoveDevice(struct UsbFnDevice
\*fnDevice); | Deletes a USB device.| -| const struct UsbFnDevice \*UsbFnGetDevice(const char
\*udcName); | Obtains a USB device.| +The USB driver development in kernel mode is complex. Therefore, you need to have a deep understanding of the USB protocol. The USB DDK is introduced to help you to develop USB drivers in user mode more conveniently. - **Table 4** usbfn_interface.h +### When to Use -| API| Description| -| -------- | -------- | -| int UsbFnStartRecvInterfaceEvent(struct
UsbFnInterface \*interface, uint32_t eventMask,
UsbFnEventCallback callback, void \*context); | Starts receiving events.| -| int UsbFnStopRecvInterfaceEvent(struct
UsbFnInterface \*interface); | Stops receiving events.| -| UsbFnInterfaceHandle UsbFnOpenInterface(struct UsbFnInterface \*interface); | Opens an interface.| -| int UsbFnCloseInterface(UsbFnInterfaceHandle handle); | Closes an interface.| -| int UsbFnGetInterfacePipeInfo(struct UsbFnInterface
\*interface, uint8_t pipeId, struct UsbFnPipeInfo \*info); | Obtains pipe information.| -| int UsbFnSetInterfaceProp(const struct UsbFnInterface
\*interface, const char \*name, const char \*value); | Sets custom properties.| +The USB Host DDK comes with two modes, namely, common mode and expert mode. In common mode, you can directly read and write USB data by using USB DDK APIs without knowing details about data transfer at the bottom layer. In expert mode, you can use USB RAW APIs to directly access the USB channel interfaces provided by the OS platform to implement more complex functions. The USB Device DDk provides functions such as USB device management, interface definition, and USB data request. - **Table 5** usbfn_request.h +### Available APIs -| API| Description| -| -------- | -------- | -| struct UsbFnRequest
\*UsbFnAllocCtrlRequest(UsbFnInterfaceHandle handle,
uint32_t len); | Allocates a control transfer request.| -| struct UsbFnRequest \*UsbFnAllocRequest(UsbFnInterfaceHandle handle,
uint8_t pipe, uint32_t len); | Allocates a data request.| -| int UsbFnFreeRequest(struct UsbFnRequest \*req); | Releases a request.| -| int UsbFnSubmitRequestAsync(struct UsbFnRequest
\*req); | Sends an asynchronous request.| -| int UsbFnSubmitRequestSync(struct UsbFnRequest
\*req, uint32_t timeout); | Sends a synchronous request.| -| int UsbFnCancelRequest(struct UsbFnRequest \*req); | Cancels a request.| +The following table lists the APIs related to USB host driver development (common mode). For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/host/usb_ddk_interface.h). + **Table 1** APIs for USB host driver development (common mode) -## How to Develop +| API| Description| +| -------- | -------- | +| int32_t UsbInitHostSdk(struct UsbSession \*\*session); | Initializes the USB host driver DDK.| +| const struct UsbInterface \*UsbClaimInterface(const
struct UsbSession \*session, uint8_t busNum, uint8_t
usbAddr, uint8_t interfaceIndex); | Obtains a USB interface.| +| UsbInterfaceHandle \*UsbOpenInterface(const struct
UsbInterface \*interfaceObj); | Opens a USB interface.| +| int32_t UsbGetPipeInfo(const UsbInterfaceHandle
\*interfaceHandle, uint8_t settingIndex, uint8_t pipeId,
struct UsbPipeInfo \*pipeInfo); | Obtains USB pipe information.| +| struct UsbRequest \*UsbAllocRequest(const
UsbInterfaceHandle \*interfaceHandle, int32_t isoPackets
, int32_t length); | Allocates a request object.| +| int32_t UsbFillRequest(const struct UsbRequest
\*request, const UsbInterfaceHandle \*interfaceHandle,
const struct UsbRequestParams \*params); | Fills in a request.| +| int32_t UsbSubmitRequestSync(const struct UsbRequest
\*request); | Sends a synchronous request.| -The USB driver is developed based on the Hardware Driver Foundation (HDF), platform, and Operating System Abstraction Layer (OSAL) APIs. A unified driver model is provided for USB devices, irrespective of the operating system and chip architecture. This document uses a serial port as an example to describe how to develop drivers for the USB host and USB device. +The following table lists the APIs related to USB host driver development (expert mode). For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/host/usb_raw_api.h). + **Table 2** APIs for USB host driver development (expert mode) -### Developing Driver Using Host DDK APIs +| API| Description| +| -------- | -------- | +| int32_t UsbRawInit(struct UsbSession \*\*session); | Initializes the USB raw APIs.| +| UsbRawHandle \*UsbRawOpenDevice(const struct
UsbSession \*session, uint8_t busNum, uint8_t
usbAddr); | Opens a USB device.| +| int32_t UsbRawSendControlRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbControlRequestData
\*requestData); | Performs a control transfer synchronously.| +| int32_t UsbRawSendBulkRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbRequestData
\*requestData); | Performs a bulk transfer synchronously.| +| int32_t UsbRawSendInterruptRequest(const struct
UsbRawRequest \*request, const UsbRawHandle
\*devHandle, const struct UsbRequestData
\*requestData); | Performs an interrupt transfer synchronously.| +| int32_t UsbRawGetConfigDescriptor(const UsbRawDevice
\*rawDev, uint8_t configIndex, struct
UsbRawConfigDescriptor \*\*config); | Obtains the configuration descriptor of a device.| +| int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in an interrupt transfer request.| +| int32_t UsbRawFillIsoRequest(const struct UsbRawRequest
\*request, const UsbRawHandle \*devHandle, const struct
UsbRawFillRequestData \*fillData); | Fills in an isochronous transfer request.| +| int32_t UsbRawSubmitRequest(const struct UsbRawRequest
\*request); | Submits a transfer request.| +| int32_t UsbRawCancelRequest(const struct UsbRawRequest
\*request); | Cancels a transfer request.| +| int32_t UsbRawHandleRequests(const UsbRawHandle
\*devHandle); | Handles a transfer request event.| + +The following table lists the APIs for USB device management on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_device.h). + + **Table 3** APIs for USB device management on the device side + +| API| Description| +| -------- | -------- | +| const struct UsbFnDevice \*UsbFnCreateDevice(const
char \*udcName, const struct UsbFnDescriptorData
\*descriptor); | Creates a USB device.| +| int32_t UsbFnRemoveDevice(struct UsbFnDevice
\*fnDevice); | Deletes a USB device.| +| const struct UsbFnDevice \*UsbFnGetDevice(const char
\*udcName); | Obtains a USB device.| -1. Configure the driver mapping table. +The following table lists the APIs for USB interface definition on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_interface.h). -2. Initialize the USB host DDK. + **Table 4** APIs for USB interface definition on the device side -3. Obtain a **UsbInterface** object. +| API| Description| +| -------- | -------- | +| int32_t UsbFnStartRecvInterfaceEvent(struct
UsbFnInterface \*interface, uint32_t eventMask,
UsbFnEventCallback callback, void \*context); | Starts receiving events.| +| int32_t UsbFnStopRecvInterfaceEvent(struct
UsbFnInterface \*interface); | Stops receiving events.| +| UsbFnInterfaceHandle UsbFnOpenInterface(struct UsbFnInterface \*interface); | Opens an interface.| +| int32_t UsbFnCloseInterface(UsbFnInterfaceHandle handle); | Closes an interface.| +| int32_t UsbFnGetInterfacePipeInfo(struct UsbFnInterface
\*interface, uint8_t pipeId, struct UsbFnPipeInfo \*info); | Obtains pipe information.| +| int32_t UsbFnSetInterfaceProp(const struct UsbFnInterface
\*interface, const char \*name, const char \*value); | Sets custom properties.| -4. Open the **UsbInterface** object to obtain the **UsbInterfaceHandle** object. +The following table lists the APIs for USB data request on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_request.h). -5. Obtain pipe information of the specified **pipeIndex** based on the **UsbInterfaceHandle** object. + **Table 5** APIs for USB data request on the device side -6. Allocate an I/O request for the **UsbInterfaceHandle** object. +| API| Description| +| -------- | -------- | +| struct UsbFnRequest
\*UsbFnAllocCtrlRequest(UsbFnInterfaceHandle handle,
uint32_t len); | Allocates a control transfer request.| +| struct UsbFnRequest \*UsbFnAllocRequest(UsbFnInterfaceHandle handle,
uint8_t pipe, uint32_t len); | Allocates a data request.| +| int32_t UsbFnFreeRequest(struct UsbFnRequest \*req); | Releases a request.| +| int32_t UsbFnSubmitRequestAsync(struct UsbFnRequest
\*req); | Sends an asynchronous request.| +| int32_t UsbFnSubmitRequestSync(struct UsbFnRequest
\*req, uint32_t timeout); | Sends a synchronous request.| +| int32_t UsbFnCancelRequest(struct UsbFnRequest \*req); | Cancels a request.| + + +### How to Develop + +USB drivers are developed based on the Hardware Driver Foundation (HDF), platform, and Operating System Abstraction Layer (OSAL) APIs. A unified driver model is provided for USB devices, irrespective of the operating system and chip architecture. This section uses the serial port as an example to describe how to develop USB host and USB device drivers. + +#### Developing Driver Using Host DDK APIs + +1. Configure USB host driver information in the .hcs file of private device data. + + ```cpp + root { + module = "usb_pnp_device"; + usb_pnp_config { + match_attr = "usb_pnp_match"; + usb_pnp_device_id = "UsbPnpDeviceId"; + UsbPnpDeviceId { + idTableList = [ + "host_acm_table" + ]; + host_acm_table { + // Driver module name, which must be the same as the value of moduleName in the driver entry structure. + moduleName = "usbhost_acm"; + // Service name of the driver, which must be unique. + serviceName = "usbhost_acm_pnp_service"; + // Keyword for matching private driver data. + deviceMatchAttr = "usbhost_acm_pnp_matchAttr"; + // Data length starting from this field, in bytes. + length = 21; + // USB driver matching rule: vendorId+productId+interfaceSubClass+interfaceProtocol+interfaceNumber. + matchFlag = 0x0303; + // Vendor ID. + vendorId = 0x12D1; + // Product ID. + productId = 0x5000; + // The least significant 16 bits of the device sequence number. + bcdDeviceLow = 0x0000; + // The most significant 16 bits of the device sequence number. + bcdDeviceHigh = 0x0000; + // Device class code allocated by the USB. + deviceClass = 0; + // Child class code allocated by the USB. + deviceSubClass = 0; + // Device protocol code allocated by the USB. + deviceProtocol = 0; + // Interface type. You can enter multiple types as needed. + interfaceClass = [0]; + // Interface subtype. You can enter multiple subtypes as needed. + interfaceSubClass = [2, 0]; + // Protocol that the interface complies with. You can enter multiple protocols as needed. + interfaceProtocol = [1, 2]; + // Interface number. You can enter multiple interface numbers as needed. + interfaceNumber = [2, 3]; + } + } + } + } + ``` -7. Fill in the I/O request based on the input parameters. +2. Initialize the USB host driver DDK. -8. Submit the I/O request in synchronous or asynchronous mode. + ```cpp + int32_t UsbInitHostSdk(struct UsbSession **session); + ``` +3. Obtain the **UsbInterface** object after initialization. -### Developing Driver Using Host Raw APIs + ```cpp + const struct UsbInterface *UsbClaimInterface(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr, uint8_t interfaceIndex); + ``` -1. Configure the driver mapping table. +4. Open the **UsbInterface** object to obtain the **UsbInterfaceHandle** object. -2. Initialize the host raw data, open the USB device, obtain the descriptor, and then obtain interface and endpoint information based on the descriptor. + ```cpp + UsbInterfaceHandle *UsbOpenInterface(const struct UsbInterface *interfaceObj); + ``` -3. Allocate a request and fill in the request based on the transfer type. +5. Obtain pipe information of the specified **pipeIndex** based on the **UsbInterfaceHandle** object. -4. Submit the I/O request in synchronous or asynchronous mode. + ```cpp + int32_t UsbGetPipeInfo(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct UsbPipeInfo *pipeInfo); + ``` +6. Pre-allocate an I/O request for the **UsbInterfaceHandle** object. -### Developing Driver Using Device DDK APIs + ```cpp + struct UsbRequest *UsbAllocRequest(const UsbInterfaceHandle *interfaceHandle, int32_t isoPackets, int32_t length); + ``` -1. Construct a descriptor. +7. Fill in the I/O request based on the input parameters. -2. Instantiate a USB device using the descriptor constructed. + ```cpp + int32_t UsbFillRequest(const struct UsbRequest *request, const UsbInterfaceHandle *interfaceHandle, const struct UsbRequestParams *params); + ``` -3. Call **UsbFnDeviceGetInterface** to obtain an interface, call **UsbFnInterfaceGetPipeInfo** to obtain pipe information based on the interface, call **UsbFnInterfaceOpen** to open the interface to obtain the handle, and call **UsbFnRequestAlloc** to obtain the request based on the handle and pipe ID. +8. Submit the I/O request in synchronous or asynchronous mode. -4. Call **UsbFnInterfaceStartRecvEvent** to receive events such as Enable and Setup, and respond to the events in **UsbFnEventCallback**. + ```cpp + int32_t UsbSubmitRequestSync(const struct UsbRequest *request); // Send a synchronous I/O request. + int32_t UsbSubmitRequestAsync(const struct UsbRequest *request); // Send an asynchronous I/O request. + ``` -5. Send and receive data in synchronous or asynchronous mode. +#### Developing Driver Using Host Raw APIs +1. Configure USB host driver information in the .hcs file of private device data. For details, see step 1 in the previous section. -## Development Example +2. Initialize the host raw data, open the USB device, obtain the descriptor, and then obtain interface and endpoint information based on the descriptor. -The following examples help you better understand the development of the USB serial port driver. + ```cpp + int32_t UsbRawInit(struct UsbSession **session); + ``` + +3. Open the USB device. + + ```cpp + UsbRawHandle *UsbRawOpenDevice(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr); + ``` + +4. Obtain the device descriptor, and obtain the interface and endpoint information based on the descriptor. + + ```cpp + int32_t UsbRawGetConfigDescriptor(const UsbRawDevice *rawDev, uint8_t configIndex, struct UsbRawConfigDescriptor **config); + ``` + +5. Allocate a request and fill in the request based on the transfer type. + + ```cpp + int32_t UsbRawFillBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData); // Populate the request for bulk transfer. + int32_t UsbRawFillControlSetup(const unsigned char *setup, const struct UsbControlRequestData *requestData); + int32_t UsbRawFillControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData); // Populate the request for control transfer. + int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData); // Populate the request for interrupt transfer. + int32_t UsbRawFillIsoRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData); // Populate the request for isochronous transfer. + ``` + +6. Submit the I/O request in synchronous or asynchronous mode. + + ```cpp + int32_t UsbRawSendControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbControlRequestData *requestData); // Send a synchronous request for control transfer. + int32_t UsbRawSendBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData); // Send a synchronous request for bulk transfer. + int32_t UsbRawSendInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData); // Send a synchronous request for interrupt transfer. + int32_t UsbRawSubmitRequest(const struct UsbRawRequest *request); // Send an asynchronous I/O request. + ``` + +#### Developing Driver Using Device DDK APIs + +1. Construct a descriptor in the device function code. + + ```cpp + static struct UsbFnFunction g_acmFunction = { // Function descriptor + .enable = true, + .funcName = "f_generic.a", + .strings = g_acmStrings, + .fsDescriptors = g_acmFsFunction, + .hsDescriptors = g_acmHsFunction, + .ssDescriptors = g_acmSsFunction, + .sspDescriptors = NULL, + }; + struct UsbFnFunction *g_functions[] = { + #ifdef CDC_ECM + &g_ecmFunction, + #endif + #ifdef CDC_ACM + &g_acmFunction, + #endif + NULL + }; + static struct UsbFnConfiguration g_masterConfig = { // Configuration descriptor + .configurationValue = 1, + .iConfiguration = USB_FUNC_CONFIG_IDX, + .attributes = USB_CFG_BUS_POWERED, + .maxPower = POWER, + .functions = g_functions, + }; + static struct UsbFnConfiguration *g_configs[] = { + &g_masterConfig, + NULL, + }; + static struct UsbDeviceDescriptor g_cdcMasterDeviceDesc = { // Device descriptor + .bLength = sizeof(g_cdcMasterDeviceDesc), + .bDescriptorType = USB_DDK_DT_DEVICE, + .bcdUSB = CpuToLe16(BCD_USB), + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = USB_MAX_PACKET_SIZE, + .idVendor = CpuToLe16(DEVICE_VENDOR_ID), + .idProduct = CpuToLe16(DEVICE_PRODUCT_ID), + .bcdDevice = CpuToLe16(DEVICE_VERSION), + .iManufacturer = USB_FUNC_MANUFACTURER_IDX, + .iProduct = USB_FUNC_PRODUCT_IDX, + .iSerialNumber = USB_FUNC_SERIAL_IDX, + .bNumConfigurations = 1, + }; + static struct UsbFnDeviceDesc g_masterFuncDevice = { // Descriptor entry + .deviceDesc = &g_cdcMasterDeviceDesc, + .deviceStrings = g_devStrings, + .configs = g_configs, + }; + ``` + +2. Create a USB device. Call **UsbFnDeviceCreate** and pass in the UDC controller and **UsbFnDescriptorData** structure to create a USB device. + + ```cpp + if (useHcs == 0) { // Descriptor written in the code. + descData.type = USBFN_DESC_DATA_TYPE_DESC; + descData.descriptor = &g_acmFuncDevice; + } else { // Descriptor compiled by using the .hcs file. + descData.type = USBFN_DESC_DATA_TYPE_PROP; + descData.property = acm->device->property; + } + // Create a USB device. + fnDev = (struct UsbFnDevice *) UsbFnCreateDevice(acm->udcName, &descData); + ``` + +3. Call **UsbFnGetInterface** to obtain a **UsbInterface** object, and call **UsbFnGetInterfacePipeInfo** to obtain the USB pipe information. + + ```cpp + // Obtain an interface. + fnIface = (struct UsbFnInterface *)UsbFnGetInterface(fnDev, i); + // Obtain the pipe information. + UsbFnGetInterfacePipeInfo(fnIface, i, &pipeInfo); + // Obtain a handle. + handle = UsbFnOpenInterface(fnIface); + // Obtain a control (EP0) request. + req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle, + sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding)); + // Obtain the request. + req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle, + sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding)); + ``` + +4. Call **UsbFnStartRecvInterfaceEvent** to receive events, and call **UsbFnEventCallback** to respond to the events. + + ```cpp + // Start receiving events. + ret = UsbFnStartRecvInterfaceEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm); + // Process the event in the callback. + static void UsbAcmEventCallback(struct UsbFnEvent *event) + { + struct UsbAcmDevice *acm = NULL; + if (event == NULL || event->context == NULL) { + HDF_LOGE("%s: event is null", __func__); + return; + } + acm = (struct UsbAcmDevice *)event->context; + switch (event->type) { + case USBFN_STATE_BIND: + HDF_LOGI("%s: receive bind event", __func__); + break; + case USBFN_STATE_UNBIND: + HDF_LOGI("%s: receive unbind event", __func__); + break; + case USBFN_STATE_ENABLE: + HDF_LOGI("%s: receive enable event", __func__); + AcmEnable(acm); + break; + case USBFN_STATE_DISABLE: + HDF_LOGI("%s: receive disable event", __func__); + AcmDisable(acm); + acm->enableEvtCnt = 0; + break; + case USBFN_STATE_SETUP: + HDF_LOGI("%s: receive setup event", __func__); + if (event->setup != NULL) { + AcmSetup(acm, event->setup); + } + break; + case USBFN_STATE_SUSPEND: + HDF_LOGI("%s: receive suspend event", __func__); + AcmSuspend(acm); + break; + case USBFN_STATE_RESUME: + HDF_LOGI("%s: receive resume event", __func__); + AcmResume(acm); + break; + default: + break; + } + } + ``` -### Developing Driver Using Host DDK APIs +5. Send and receive data in synchronously or asynchronously. - -``` -root { - module = "usb_pnp_device"; - usb_pnp_config { - match_attr = "usb_pnp_match"; - usb_pnp_device_id = "UsbPnpDeviceId"; - UsbPnpDeviceId { - idTableList = [ - "host_acm_table" - ]; - host_acm_table { - // Driver module name, which must be the same as the value of moduleName in the driver entry structure. - moduleName = "usbhost_acm"; - // Service name of the driver, which must be unique. - serviceName = "usbhost_acm_pnp_service"; - // Keyword for matching private driver data. - deviceMatchAttr = "usbhost_acm_pnp_matchAttr"; - // Data length starting from this field, in bytes. - length = 21; - // USB driver matching rule: vendorId+productId+interfaceSubClass+interfaceProtocol+interfaceNumber. - matchFlag = 0x0303; - // Vendor ID. - vendorId = 0x12D1; - // Product ID. - productId = 0x5000; - // The least significant 16 bits of the device sequence number. - bcdDeviceLow = 0x0000; - // The most significant 16 bits of the device sequence number. - bcdDeviceHigh = 0x0000; - // Device class code allocated by the USB. - deviceClass = 0; - // Child class code allocated by the USB. - deviceSubClass = 0; - // Device protocol code allocated by the USB. - deviceProtocol = 0; - // Interface type. You can enter multiple types as needed. - interfaceClass = [0]; - // Interface subtype. You can enter multiple subtypes as needed. - interfaceSubClass = [2, 0]; - // Protocol that the interface complies with. You can enter multiple protocols as needed. - interfaceProtocol = [1, 2]; - // Interface number. You can enter multiple interface numbers as needed. - interfaceNumber = [2, 3]; - } - } + ```cpp + notify = (struct UsbCdcNotification *)req->buf; + ... + if (memcpy_s((void *)(notify + 1), length, data, length) != EOK) { + return HDF_FAILURE; } -} + ret = UsbFnSubmitRequestAsync(req); // Send data asynchronously. + ``` + +### Development Example +The following example helps you better understand the development of the USB serial port driver. + +#### Developing Driver Using Host DDK APIs + +```cpp #include "usb_serial.h" #include "hdf_base.h" #include "hdf_log.h" @@ -231,53 +459,52 @@ root { #define HDF_LOG_TAG USB_HOST_ACM #define STR_LEN 512 -static struct UsbRequest *g_syncRequest = NULL; +static struct UsbRequest *g_syncRequest = NULL; // Define a USB request. static struct UsbRequest *g_ctrlCmdRequest = NULL; static bool g_acmReleaseFlag = false; static uint8_t *g_acmReadBuffer = NULL; ... -static int SerialCtrlMsg(struct AcmDevice *acm, uint8_t request, +static int32_t SerialCtrlMsg(struct AcmDevice *acm, uint8_t request, uint16_t value, void *buf, uint16_t len) { - int ret; + int32_t ret; uint16_t index = acm->intPipe->interfaceId; struct UsbControlParams controlParams; - struct UsbRequestParams params; + struct UsbRequestParams params; // Define a UsbRequestParams object. if (acm == NULL || buf == NULL) { - HDF_LOGE("%s:invalid param", __func__); return HDF_ERR_IO; } if (acm->ctrlReq == NULL) { + // Pre-allocate the IO Request object to be sent to UsbInterfaceHandle. acm->ctrlReq = UsbAllocRequest(acm->ctrDevHandle, 0, len); if (acm->ctrlReq == NULL) { - HDF_LOGE("%s: UsbAllocRequest failed", __func__); return HDF_ERR_IO; } } controlParams.request = request; - controlParams.target = USB_REQUEST_TARGET_INTERFACE; - controlParams.reqType = USB_REQUEST_TYPE_CLASS; - controlParams.direction = USB_REQUEST_DIR_TO_DEVICE; + controlParams.target = USB_REQUEST_TARGET_INTERFACE; // Interface object + controlParams.reqType = USB_REQUEST_TYPE_CLASS; // Request type + controlParams.direction = USB_REQUEST_DIR_TO_DEVICE; // Data transfer from the host to the device controlParams.value = value; controlParams.index = index; controlParams.data = buf; controlParams.size = len; - params.interfaceId = USB_CTRL_INTERFACE_ID; + params.interfaceId = USB_CTRL_INTERFACE_ID; // Define the default ID of the USB control interface. params.pipeAddress = acm->ctrPipe->pipeAddress; params.pipeId = acm->ctrPipe->pipeId; - params.requestType = USB_REQUEST_PARAMS_CTRL_TYPE; - params.timeout = USB_CTRL_SET_TIMEOUT; + params.requestType = USB_REQUEST_PARAMS_CTRL_TYPE; // Control type. + params.timeout = USB_CTRL_SET_TIMEOUT; // Set the timeout interval. params.ctrlReq = UsbControlSetUp(&controlParams); + // Fill in the pre-allocated I/O request based on UsbRequestParams. ret = UsbFillRequest(acm->ctrlReq, acm->ctrDevHandle, ¶ms); - if (HDF_SUCCESS != ret) { - HDF_LOGE("%s: failed, ret=%d ", __func__, ret); + if (ret != HDF_SUCCESS) { return ret; } - ret = UsbSubmitRequestSync(acm->ctrlReq); // Send an I/O request synchronously. - if (HDF_SUCCESS != ret) { - HDF_LOGE("UsbSubmitRequestSync failed, ret=%d ", ret); + // Send an I/O request synchronously. + ret = UsbSubmitRequestSync(acm->ctrlReq); + if (ret != HDF_SUCCESS) { return ret; } if (!acm->ctrlReq->compInfo.status) { @@ -290,8 +517,8 @@ static struct UsbInterface *GetUsbInterfaceById(const struct AcmDevice *acm, uint8_t interfaceIndex) { struct UsbInterface *tmpIf = NULL; - tmpIf = (struct UsbInterface *)UsbClaimInterface(acm->session, acm->busNum, - acm->devAddr, interfaceIndex); // Obtain the UsbInterface object. + // Obtain a UsbInterface object. + tmpIf = (struct UsbInterface *)UsbClaimInterface(acm->session, acm->busNum, acm->devAddr, interfaceIndex); return tmpIf; } ... @@ -299,9 +526,9 @@ static struct UsbPipeInfo *EnumePipe(const struct AcmDevice *acm, uint8_t interfaceIndex, UsbPipeType pipeType, UsbPipeDirection pipeDirection) { uint8_t i; - int ret; - struct UsbInterfaceInfo *info = NULL; - UsbInterfaceHandle *interfaceHandle = NULL; + int32_t ret; + struct UsbInterfaceInfo *info = NULL; // Define a UsbInterfaceInfo object. + UsbInterfaceHandle *interfaceHandle = NULL; // Define a USB interface operation handle (that is, the void * type). if (pipeType == USB_PIPE_TYPE_CONTROL) { info = &acm->ctrIface->info; @@ -310,19 +537,20 @@ static struct UsbPipeInfo *EnumePipe(const struct AcmDevice *acm, else { info = &acm->iface[interfaceIndex]->info; + // Obtain the device handle based on interfaceIndex. interfaceHandle = InterfaceIdToHandle(acm, info->interfaceIndex); } for (i = 0; i <= info->pipeNum; i++) { struct UsbPipeInfo p; - ret = UsbGetPipeInfo(interfaceHandle, info->curAltSetting, i, &p);// Obtain information about the pipe with index i. + // Obtain the pipeInfo object whose index is i. + ret = UsbGetPipeInfo(interfaceHandle, info->curAltSetting, i, &p); if (ret < 0) { continue; } if ((p.pipeDirection == pipeDirection) && (p.pipeType == pipeType)) { - struct UsbPipeInfo *pi = OsalMemCalloc(sizeof(*pi)); + struct UsbPipeInfo *pi = OsalMemCalloc(sizeof(*pi)); // Allocate and initialize the memory. if (pi == NULL) { - HDF_LOGE("%s: Alloc pipe failed", __func__); return NULL; } p.interfaceId = info->interfaceIndex; @@ -338,7 +566,6 @@ static struct UsbPipeInfo *GetPipe(const struct AcmDevice *acm, { uint8_t i; if (acm == NULL) { - HDF_LOGE("%s: invalid params", __func__); return NULL; } for (i = 0; i < acm->interfaceCnt; i++) { @@ -346,6 +573,7 @@ static struct UsbPipeInfo *GetPipe(const struct AcmDevice *acm, if (!acm->iface[i]) { continue; } + // Obtain pipe information of the control pipe. p = EnumePipe(acm, i, pipeType, pipeDirection); if (p == NULL) { continue; @@ -362,40 +590,32 @@ static int32_t UsbSerialDriverBind(struct HdfDeviceObject *device) errno_t err; struct AcmDevice *acm = NULL; if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); return HDF_ERR_INVALID_OBJECT; } acm = (struct AcmDevice *)OsalMemCalloc(sizeof(*acm)); if (acm == NULL) { - HDF_LOGE("%s: Alloc usb serial device failed", __func__); return HDF_FAILURE; } + // Initialize the mutex. &acm->lock indicates the pointer pointing to the mutex. if (OsalMutexInit(&acm->lock) != HDF_SUCCESS) { - HDF_LOGE("%s:%d OsalMutexInit failed", __func__, __LINE__); goto error; } info = (struct UsbPnpNotifyServiceInfo *)device->priv; if (info != NULL) { - HDF_LOGD("%s:%d busNum=%d,devAddr=%d,interfaceLength=%d", - __func__, __LINE__, info->busNum, info->devNum, info->interfaceLength); acm->busNum = info->busNum; acm->devAddr = info->devNum; acm->interfaceCnt = info->interfaceLength; err = memcpy_s((void *)(acm->interfaceIndex), USB_MAX_INTERFACES, (const void*)info->interfaceNumber, info->interfaceLength); if (err != EOK) { - HDF_LOGE("%s:%d memcpy_s failed err=%d", - __func__, __LINE__, err); goto lock_error; } } else { - HDF_LOGE("%s:%d info is NULL!", __func__, __LINE__); goto lock_error; } acm->device = device; device->service = &(acm->service); acm->device->service->Dispatch = UsbSerialDeviceDispatch; - HDF_LOGD("UsbSerialDriverBind=========================OK"); return HDF_SUCCESS; lock_error: @@ -408,14 +628,14 @@ error: return HDF_FAILURE; } ... -static int AcmAllocReadRequests(struct AcmDevice *acm) +static int32_t AcmAllocReadRequests(struct AcmDevice *acm) { - int ret; + int32_t ret; struct UsbRequestParams readParams; - for (int i = 0; i < ACM_NR; i++) { - acm->readReq[i] = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), 0, acm->readSize); // Allocate the readReq I/O request to be sent. + for (int32_t i = 0; i < ACM_NR; i++) { + // Allocate the readReq I/O request to be sent. + acm->readReq[i] = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), 0, acm->readSize); if (!acm->readReq[i]) { - HDF_LOGE("readReq request failed"); goto error; } readParams.userData = (void *)acm; @@ -423,14 +643,14 @@ static int AcmAllocReadRequests(struct AcmDevice *acm) readParams.pipeId = acm->dataInPipe->pipeId; readParams.interfaceId = acm->dataInPipe->interfaceId; readParams.callback = AcmReadBulk; - readParams.requestType = USB_REQUEST_PARAMS_DATA_TYPE; + readParams.requestType = USB_REQUEST_PARAMS_DATA_TYPE; /* Data type */ readParams.timeout = USB_CTRL_SET_TIMEOUT; readParams.dataReq.numIsoPackets = 0; readParams.dataReq.direction = (acm->dataInPipe->pipeDirection >> USB_PIPE_DIR_OFFSET) & 0x1; readParams.dataReq.length = acm->readSize; - ret = UsbFillRequest(acm->readReq[i], InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), &readParams); // Fills in the readReq object to be sent. - if (HDF_SUCCESS != ret) { - HDF_LOGE("%s: UsbFillRequest failed, ret=%d n", __func__, ret); + // Fill in the readReq IO Request object to be sent based on readParams. + ret = UsbFillRequest(acm->readReq[i], InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), &readParams); + if (ret != HDF_SUCCESS) { goto error; } } @@ -441,13 +661,13 @@ error: return HDF_ERR_MALLOC_FAIL; } -static int AcmAllocNotifyRequest(struct AcmDevice *acm) +static int32_t AcmAllocNotifyRequest(struct AcmDevice *acm) { - int ret; + int32_t ret; struct UsbRequestParams intParams = {}; - acm->notifyReq = UsbAllocRequest(InterfaceIdToHandle(acm, acm->intPipe->interfaceId), 0, acm->intSize); // Allocate the interrupt I/O request object to be sent. + // Allocate the interrupt I/O request to be sent. + acm->notifyReq = UsbAllocRequest(InterfaceIdToHandle(acm, acm->intPipe->interfaceId), 0, acm->intSize); if (!acm->notifyReq) { - HDF_LOGE("notifyReq request failed"); return HDF_ERR_MALLOC_FAIL; } intParams.userData = (void *)acm; @@ -460,9 +680,9 @@ static int AcmAllocNotifyRequest(struct AcmDevice *acm) intParams.dataReq.numIsoPackets = 0; intParams.dataReq.direction = (acm->intPipe->pipeDirection >> USB_PIPE_DIR_OFFSET) & DIRECTION_MASK; intParams.dataReq.length = acm->intSize; - ret = UsbFillRequest(acm->notifyReq, InterfaceIdToHandle(acm, acm->intPipe->interfaceId), &intParams); // Fill in the interrupt I/O request. - if (HDF_SUCCESS != ret) { - HDF_LOGE("%s: UsbFillRequest failed, ret=%d n", __func__, ret); + // Fill in the interrupt I/O request. + ret = UsbFillRequest(acm->notifyReq, InterfaceIdToHandle(acm, acm->intPipe->interfaceId), &intParams); + if (ret != HDF_SUCCESS) { goto error; } return HDF_SUCCESS; @@ -474,8 +694,9 @@ error: static void AcmReleaseInterfaces(struct AcmDevice *acm) { - for (int i = 0; i < acm->interfaceCnt; i++) { + for (int32_t i = 0; i < acm->interfaceCnt; i++) { if (acm->iface[i]) { + // Release a USB interface object. UsbReleaseInterface(acm->iface[i]); acm->iface[i] = NULL; } @@ -488,31 +709,33 @@ static void AcmReleaseInterfaces(struct AcmDevice *acm) static int32_t AcmClaimInterfaces(struct AcmDevice *acm) { - for (int i = 0; i < acm->interfaceCnt; i++) { - acm->iface[i] = GetUsbInterfaceById((const struct AcmDevice *)acm, acm->interfaceIndex[i]); // Obtain the UsbInterface object. + for (int32_t i = 0; i < acm->interfaceCnt; i++) { + // Obtain a UsbInterface object. + acm->iface[i] = GetUsbInterfaceById((const struct AcmDevice *)acm, acm->interfaceIndex[i]); if (acm->iface[i] == NULL) { - HDF_LOGE("%s: interface%d is null", __func__, acm->interfaceIndex[i]); goto error; } } - acm->ctrIface = GetUsbInterfaceById((const struct AcmDevice *)acm, USB_CTRL_INTERFACE_ID); // Obtain the UsbInterface object corresponding to the control interface. + // Obtain the UsbInterface object corresponding to the control interface. + acm->ctrIface = GetUsbInterfaceById((const struct AcmDevice *)acm, USB_CTRL_INTERFACE_ID); if (acm->ctrIface == NULL) { - HDF_LOGE("%s: GetUsbInterfaceById null", __func__); goto error; } return HDF_SUCCESS; error: + // Release the UsbInterface object cyclically based on acm->interfaceCnt. AcmReleaseInterfaces(acm); return HDF_FAILURE; } static void AcmCloseInterfaces(struct AcmDevice *acm) { - for (int i = 0; i < acm->interfaceCnt; i++) { + for (int32_t i = 0; i < acm->interfaceCnt; i++) { if (acm->devHandle[i]) { + // Close a USB device object. UsbCloseInterface(acm->devHandle[i]); acm->devHandle[i] = NULL; } @@ -525,51 +748,51 @@ static void AcmCloseInterfaces(struct AcmDevice *acm) static int32_t AcmOpenInterfaces(struct AcmDevice *acm) { - for (int i = 0; i < acm->interfaceCnt; i++) { + for (int32_t i = 0; i < acm->interfaceCnt; i++) { if (acm->iface[i]) { - acm->devHandle[i] = UsbOpenInterface(acm->iface[i]); // Open the UsbInterface object obtained. + // Open the UsbInterface object obtained. + acm->devHandle[i] = UsbOpenInterface(acm->iface[i]); if (acm->devHandle[i] == NULL) { - HDF_LOGE("%s: UsbOpenInterface null", __func__); goto error; } } } acm->ctrDevHandle = UsbOpenInterface(acm->ctrIface); if (acm->ctrDevHandle == NULL) { - HDF_LOGE("%s: ctrDevHandle UsbOpenInterface null", __func__); goto error; } return HDF_SUCCESS; error: + // Disable all UsbInterface objects. AcmCloseInterfaces(acm); return HDF_FAILURE; } static int32_t AcmGetPipes(struct AcmDevice *acm) { - acm->dataInPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_IN);// Obtain pipe information of dataInPipe. + // Obtain pipe information of dataInPipe. + acm->dataInPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_IN); if (acm->dataInPipe == NULL) { - HDF_LOGE("dataInPipe is NULL"); goto error; } - acm->dataOutPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_OUT); // Obtain pipe information of dataOutPipe. + // Obtain pipe information of dataOutPipe. + acm->dataOutPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_OUT); if (acm->dataOutPipe == NULL) { - HDF_LOGE("dataOutPipe is NULL"); goto error; } - acm->ctrPipe = EnumePipe(acm, acm->ctrIface->info.interfaceIndex, USB_PIPE_TYPE_CONTROL, USB_PIPE_DIRECTION_OUT); // Obtain pipe information of the control pipe. + // Obtain pipe information of the control pipe. + acm->ctrPipe = EnumePipe(acm, acm->ctrIface->info.interfaceIndex, USB_PIPE_TYPE_CONTROL, USB_PIPE_DIRECTION_OUT); if (acm->ctrPipe == NULL) { - HDF_LOGE("ctrPipe is NULL"); goto error; } - acm->intPipe = GetPipe(acm, USB_PIPE_TYPE_INTERRUPT, USB_PIPE_DIRECTION_IN); // Obtain pipe information of the interrupt pipe. + //Obtain pipe information of the interrupt pipe. + acm->intPipe = GetPipe(acm, USB_PIPE_TYPE_INTERRUPT, USB_PIPE_DIRECTION_IN); if (acm->intPipe == NULL) { - HDF_LOGE("intPipe is NULL"); goto error; } @@ -577,10 +800,10 @@ static int32_t AcmGetPipes(struct AcmDevice *acm) acm->writeSize = acm->dataOutPipe->maxPacketSize; acm->ctrlSize = acm->ctrPipe->maxPacketSize; acm->intSize = acm->intPipe->maxPacketSize; - return HDF_SUCCESS; error: + // Release all pipe information on the device. AcmFreePipes(acm); return HDF_FAILURE; } @@ -602,29 +825,26 @@ static int32_t AcmAllocRequests(struct AcmDevice *acm) int32_t ret; if (AcmWriteBufAlloc(acm) < 0) { - HDF_LOGE("%s: AcmWriteBufAlloc failed", __func__); return HDF_ERR_MALLOC_FAIL; } - for (int i = 0; i < ACM_NW; i++) { + for (int32_t i = 0; i < ACM_NW; i++) { struct AcmWb *snd = &(acm->wb[i]); - snd->request = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataOutPipe->interfaceId), 0, acm->writeSize); // Allocate the I/O request object to be sent. + // Allocate the I/O request to be sent. + snd->request = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataOutPipe->interfaceId), 0, acm->writeSize); snd->instance = acm; if (snd->request == NULL) { - HDF_LOGE("%s:%d snd request failed", __func__, __LINE__); goto error_alloc_write_req; } } - ret = AcmAllocNotifyRequest(acm); // Allocate and fill in the interrupt I/O request object. + ret = AcmAllocNotifyRequest(acm); // Allocate and fill in the interrupt I/O request. if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:%d AcmAllocNotifyRequest failed", __func__, __LINE__); goto error_alloc_int_req; } - ret = AcmAllocReadRequests(acm); // Allocate and fill in the readReq I/O request object. + ret = AcmAllocReadRequests(acm); // Allocate and fill in the readReq I/O request. if (ret) { - HDF_LOGE("%s:%d AcmAllocReadRequests failed", __func__, __LINE__); goto error_alloc_read_req; } @@ -645,57 +865,56 @@ static int32_t AcmInit(struct AcmDevice *acm) struct UsbSession *session = NULL; if (acm->initFlag == true) { - HDF_LOGE("%s:%d: initFlag is true", __func__, __LINE__); return HDF_SUCCESS; } - ret = UsbInitHostSdk(NULL); // Initialize the Host DDK. + // Initialize the USB Host DDK. + ret = UsbInitHostSdk(NULL); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: UsbInitHostSdk failed", __func__); return HDF_ERR_IO; } acm->session = session; + // Obtain UsbInterface objects based on acm->interfaceIndex[i]. ret = AcmClaimInterfaces(acm); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: AcmClaimInterfaces failed", __func__); goto error_claim_interfaces; } + // Open UsbInterface objects based on acm->iface[i]. ret = AcmOpenInterfaces(acm); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: AcmOpenInterfaces failed", __func__); goto error_open_interfaces; } + // Obtain the pointer to the pipe information. ret = AcmGetPipes(acm); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: AcmGetPipes failed", __func__); goto error_get_pipes; } ret = AcmAllocRequests(acm); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: AcmAllocRequests failed", __func__); goto error_alloc_reqs; } - acm->lineCoding.dwDTERate = CpuToLe32(DATARATE); - acm->lineCoding.bCharFormat = CHARFORMAT; + acm->lineCoding.dwDTERate = CpuToLe32(DATARATE); // Convert to little-endian data. + acm->lineCoding.bCharFormat = CHARFORMAT; // 8 acm->lineCoding.bParityType = USB_CDC_NO_PARITY; acm->lineCoding.bDataBits = USB_CDC_1_STOP_BITS; acm->initFlag = true; - - HDF_LOGD("%s:%d========OK", __func__, __LINE__); return HDF_SUCCESS; error_alloc_reqs: AcmFreePipes(acm); error_get_pipes: + // Disable all UsbInterface objects. AcmCloseInterfaces(acm); error_open_interfaces: + // Release all UsbInterface objects. AcmReleaseInterfaces(acm); error_claim_interfaces: + // Exit the USB DDK on the host. acm->session indicates the pointer pointing to the session context. UsbExitHostSdk(acm->session); acm->session = NULL; return ret; @@ -704,7 +923,6 @@ error_claim_interfaces: static void AcmRelease(struct AcmDevice *acm) { if (acm->initFlag == false) { - HDF_LOGE("%s:%d: initFlag is false", __func__, __LINE__); return; } @@ -712,9 +930,9 @@ static void AcmRelease(struct AcmDevice *acm) AcmFreePipes(acm); AcmCloseInterfaces(acm); AcmReleaseInterfaces(acm); + // Exit the USB DDK on the host. UsbExitHostSdk(acm->session); acm->session = NULL; - acm->initFlag = false; } @@ -724,15 +942,15 @@ static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) struct AcmDevice *acm = NULL; if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); return HDF_ERR_INVALID_OBJECT; } acm = (struct AcmDevice *)device->service; + // Initialize the mutex. &acm->readLock indicates the pointer pointing to the mutex. OsalMutexInit(&acm->readLock); OsalMutexInit(&acm->writeLock); - HDF_LOGD("%s:%d busNum=%d,devAddr=%d", - __func__, __LINE__, acm->busNum, acm->devAddr); + HDF_LOGD("%s:%d busNum=%d,devAddr=%d", __func__, __LINE__, acm->busNum, acm->devAddr); + // Allocate space for the USB serial port device information and assign a value. ret = UsbSerialDeviceAlloc(acm); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: Serial Device alloc failed", __func__); @@ -740,9 +958,6 @@ static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) acm->initFlag = false; g_acmReleaseFlag = false; - - HDF_LOGD("%s:%d init ok!", __func__, __LINE__); - return ret; } @@ -751,30 +966,29 @@ static void UsbSerialDriverRelease(struct HdfDeviceObject *device) struct AcmDevice *acm = NULL; if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); return; } acm = (struct AcmDevice *)device->service; if (acm == NULL) { - HDF_LOGE("%s: acm is null", __func__); return; } g_acmReleaseFlag = true; if (acm->initFlag == true) { - HDF_LOGE("%s:%d AcmRelease", __func__, __LINE__); AcmRelease(acm); } + // Release the USB serial port device information. UsbSeriaDevicelFree(acm); + // Release the mutex. OsalMutexDestroy(&acm->writeLock); OsalMutexDestroy(&acm->readLock); OsalMutexDestroy(&acm->lock); OsalMemFree(acm); acm = NULL; - HDF_LOGD("%s:%d exit", __func__, __LINE__); } +// Perform Bind, Init, and Release operations on the driver. struct HdfDriverEntry g_usbSerialDriverEntry = { .moduleVersion = 1, .moduleName = "usbhost_acm", // Driver module name, which must be the same as that configured in the .hcs file. @@ -782,14 +996,12 @@ struct HdfDriverEntry g_usbSerialDriverEntry = { .Init = UsbSerialDriverInit, .Release = UsbSerialDriverRelease, }; -HDF_INIT(g_usbSerialDriverEntry); +HDF_INIT(g_usbSerialDriverEntry); // Driver entry. ``` +#### Developing Driver Using Host Raw APIs -### Developing Driver Using Host Raw APIs - - -``` +```cpp root { module = "usb_pnp_device"; usb_pnp_config { @@ -836,7 +1048,9 @@ root { } } } +``` +```cpp #include "usb_serial_rawapi.h" #include #include "osal_mem.h" @@ -846,7 +1060,7 @@ root { #include "hdf_log.h" #include "hdf_usb_pnp_manage.h" -#define HDF_LOG_TAG USB_HOST_ACM_RAW_API +#define HDF_LOG_TAG USB_HOST_ACM_RAW_API // Labels that can be queried in logs. #define USB_CTRL_REQ_SIZE 64 #define USB_IO_THREAD_STACK_SIZE 8192 #define USB_RAW_IO_SLEEP_MS_TIME 100 @@ -858,51 +1072,46 @@ struct OsalMutex g_stopIoLock; static bool g_rawAcmReleaseFlag = false; ...... -static int UsbGetConfigDescriptor(UsbRawHandle *devHandle, struct UsbRawConfigDescriptor **config) +static int32_t UsbGetConfigDescriptor(UsbRawHandle *devHandle, struct UsbRawConfigDescriptor **config) { UsbRawDevice *dev = NULL; - int activeConfig; - int ret; + int32_t activeConfig; + int32_t ret; if (devHandle == NULL) { - HDF_LOGE("%s:%d devHandle is NULL", - __func__, __LINE__); return HDF_ERR_INVALID_PARAM; } + // Obtain the configuration of the active device. ret = UsbRawGetConfiguration(devHandle, &activeConfig); - if (ret) { - HDF_LOGE("%s:%d UsbRawGetConfiguration failed, ret=%d", - __func__, __LINE__, ret); + if (ret != HDF_SUCCESS) { return HDF_FAILURE; } - HDF_LOGE("%s:%d activeConfig=%d", __func__, __LINE__, activeConfig); + + // Obtain the device pointer based on the specified device handle. dev = UsbRawGetDevice(devHandle); if (dev == NULL) { - HDF_LOGE("%s:%d UsbRawGetDevice failed", - __func__, __LINE__); return HDF_FAILURE; } + // Obtain the device configuration descriptor based on the specified device ID. ret = UsbRawGetConfigDescriptor(dev, activeConfig, config); - if (ret) { - HDF_LOGE("UsbRawGetConfigDescriptor failed, ret=%dn", ret); - return HDF_FAILURE; + if (ret != HDF_SUCCESS) { + HDF_LOGE("UsbRawGetConfigDescriptor failed, ret=%d\n", ret); } - - return HDF_SUCCESS; + return ret; } ... -static int UsbAllocWriteRequests(struct AcmDevice *acm) +static int32_t UsbAllocWriteRequests(struct AcmDevice *acm) { - int i; + int32_t i; for (i = 0; i < ACM_NW; i++) { struct AcmWb *snd = &acm->wb[i]; + // Allocate a transfer request with the specified number of sync packet descriptors. snd->request = UsbRawAllocRequest(acm->devHandle, 0, acm->dataOutEp->maxPacketSize); snd->instance = acm; if (snd->request == NULL) { - HDF_LOGE("%s: UsbRawAllocRequest failed", __func__); return HDF_ERR_MALLOC_FAIL; } } @@ -918,17 +1127,14 @@ static int32_t UsbSerialDriverBind(struct HdfDeviceObject *device) errno_t err; if (device == NULL) { - HDF_LOGE("%s: device is null", __func__); return HDF_ERR_INVALID_OBJECT; } acm = (struct AcmDevice *)OsalMemCalloc(sizeof(*acm)); if (acm == NULL) { - HDF_LOGE("%s: Alloc usb serial device failed", __func__); return HDF_FAILURE; } if (OsalMutexInit(&acm->lock) != HDF_SUCCESS) { - HDF_LOGE("%s:%d OsalMutexInit failed", __func__, __LINE__); goto error; } @@ -940,19 +1146,15 @@ static int32_t UsbSerialDriverBind(struct HdfDeviceObject *device) err = memcpy_s((void *)(acm->interfaceIndex), USB_MAX_INTERFACES, (const void*)info->interfaceNumber, info->interfaceLength); if (err != EOK) { - HDF_LOGE("%s:%d memcpy_s failed err=%d", - __func__, __LINE__, err); goto lock_error; } } else { - HDF_LOGE("%s:%d info is NULL!", __func__, __LINE__); goto lock_error; } device->service = &(acm->service); device->service->Dispatch = UsbSerialDeviceDispatch; acm->device = device; - HDF_LOGD("UsbSerialDriverBind=========================OK"); return HDF_SUCCESS; lock_error: @@ -965,16 +1167,16 @@ error: return HDF_FAILURE; } ... -static int UsbAllocReadRequests(struct AcmDevice *acm) +static int32_t UsbAllocReadRequests(struct AcmDevice *acm) { struct UsbRawFillRequestData reqData; - int size = acm->dataInEp->maxPacketSize; - int ret; + int32_t size = acm->dataInEp->maxPacketSize; + int32_t ret; - for (int i = 0; i < ACM_NR; i++) { + for (int32_t i = 0; i < ACM_NR; i++) { + // Allocate a transfer request with the specified number of sync packet descriptors. acm->readReq[i] = UsbRawAllocRequest(acm->devHandle, 0, size); if (!acm->readReq[i]) { - HDF_LOGE("readReq request failed"); return HDF_ERR_MALLOC_FAIL; } @@ -985,10 +1187,9 @@ static int UsbAllocReadRequests(struct AcmDevice *acm) reqData.timeout = USB_CTRL_SET_TIMEOUT; reqData.length = size; + // Fill the required information in the bulk transfer request. ret = UsbRawFillBulkRequest(acm->readReq[i], acm->devHandle, &reqData); - if (ret) { - HDF_LOGE("%s: FillBulkRequest failed, ret=%d n", - __func__, ret); + if (ret != HDF_SUCCESS) { return HDF_FAILURE; } } @@ -996,15 +1197,15 @@ static int UsbAllocReadRequests(struct AcmDevice *acm) return HDF_SUCCESS; } ... -static int UsbAllocNotifyRequest(struct AcmDevice *acm) +static int32_t UsbAllocNotifyRequest(struct AcmDevice *acm) { struct UsbRawFillRequestData fillRequestData; - int size = acm->notifyEp->maxPacketSize; - int ret; + int32_t size = acm->notifyEp->maxPacketSize; + int32_t ret; + // Allocate a transfer request with the specified number of sync packet descriptors. acm->notifyReq = UsbRawAllocRequest(acm->devHandle, 0, size); if (!acm->notifyReq) { - HDF_LOGE("notifyReq request failed"); return HDF_ERR_MALLOC_FAIL; } @@ -1015,9 +1216,9 @@ static int UsbAllocNotifyRequest(struct AcmDevice *acm) fillRequestData.userData = (void *)acm; fillRequestData.timeout = USB_CTRL_SET_TIMEOUT; + // Fill the required information in the interrupt transfer request. ret = UsbRawFillInterruptRequest(acm->notifyReq, acm->devHandle, &fillRequestData); - if (ret) { - HDF_LOGE("%s: FillInterruptRequest failed, ret=%d", __func__, ret); + if (ret != HDF_SUCCESS) { return HDF_FAILURE; } @@ -1031,62 +1232,55 @@ static int32_t UsbSerialInit(struct AcmDevice *acm) int32_t ret; if (acm->initFlag == true) { - HDF_LOGE("%s:%d: initFlag is true", __func__, __LINE__); return HDF_SUCCESS; } + // Initialize the USB DDK in expert mode. ret = UsbRawInit(NULL); - if (ret) { - HDF_LOGE("%s:%d UsbRawInit failed", __func__, __LINE__); + if (ret != HDF_SUCCESS) { return HDF_ERR_IO; } acm->session = session; + // Open a USB device object. devHandle = UsbRawOpenDevice(session, acm->busNum, acm->devAddr); if (devHandle == NULL) { - HDF_LOGE("%s:%d UsbRawOpenDevice failed", __func__, __LINE__); ret = HDF_FAILURE; goto err_open_device; } acm->devHandle = devHandle; + // Obtain the configuration of the active device, device pointer, and configuration descriptor. ret = UsbGetConfigDescriptor(devHandle, &acm->config); - if (ret) { - HDF_LOGE("%s:%d UsbGetConfigDescriptor failed", __func__, __LINE__); + if (ret != HDF_SUCCESS) { ret = HDF_FAILURE; goto err_get_desc; } ret = UsbParseConfigDescriptor(acm, acm->config); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:%d UsbParseConfigDescriptor failed", __func__, __LINE__); ret = HDF_FAILURE; goto err_parse_desc; } ret = AcmWriteBufAlloc(acm); if (ret < 0) { - HDF_LOGE("%s:%d AcmWriteBufAlloc failed", __func__, __LINE__); ret = HDF_FAILURE; goto err_alloc_write_buf; } ret = UsbAllocWriteRequests(acm); if (ret < 0) { - HDF_LOGE("%s:%d UsbAllocWriteRequests failed", __func__, __LINE__); ret = HDF_FAILURE; goto err_alloc_write_reqs; } ret = UsbAllocNotifyRequest(acm); if (ret) { - HDF_LOGE("%s:%d UsbAllocNotifyRequests failed", __func__, __LINE__); goto err_alloc_notify_req; } ret = UsbAllocReadRequests(acm); if (ret) { - HDF_LOGE("%s:%d UsbAllocReadRequests failed", __func__, __LINE__); goto err_alloc_read_reqs; } ret = UsbStartIo(acm); if (ret) { - HDF_LOGE("%s:%d UsbAllocReadRequests failed", __func__, __LINE__); goto err_start_io; } @@ -1097,18 +1291,14 @@ static int32_t UsbSerialInit(struct AcmDevice *acm) ret = UsbRawSubmitRequest(acm->notifyReq); if (ret) { - HDF_LOGE("%s:%d UsbRawSubmitRequest failed", __func__, __LINE__); goto err_submit_req; } acm->initFlag = true; - - HDF_LOGD("%s:%d=========================OK", __func__, __LINE__); - return HDF_SUCCESS; err_submit_req: - UsbStopIo(acm); + UsbStopIo(acm); // Stop the I/O thread and release all resources. err_start_io: UsbFreeReadRequests(acm); err_alloc_read_reqs: @@ -1123,9 +1313,9 @@ err_parse_desc: UsbRawFreeConfigDescriptor(acm->config); acm->config = NULL; err_get_desc: - (void)UsbRawCloseDevice(devHandle); + (void)UsbRawCloseDevice(devHandle); // Close the USB device object. err_open_device: - UsbRawExit(acm->session); + UsbRawExit(acm->session); // Exit the expert mode of the USB DDK. return ret; } @@ -1133,7 +1323,6 @@ err_open_device: static void UsbSerialRelease(struct AcmDevice *acm) { if (acm->initFlag == false) { - HDF_LOGE("%s:%d: initFlag is false", __func__, __LINE__); return; } @@ -1151,6 +1340,7 @@ static void UsbSerialRelease(struct AcmDevice *acm) UsbReleaseInterfaces(acm); UsbRawFreeConfigDescriptor(acm->config); acm->config = NULL; + // Exit the expert mode of the USB DDK. UsbRawExit(acm->session); acm->initFlag = false; @@ -1162,7 +1352,6 @@ static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) int32_t ret; if (device == NULL) { - HDF_LOGE("%s:%d device is null", __func__, __LINE__); return HDF_ERR_INVALID_OBJECT; } acm = (struct AcmDevice *)device->service; @@ -1176,9 +1365,6 @@ static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) acm->initFlag = false; g_rawAcmReleaseFlag = false; - - HDF_LOGD("%s:%d init ok!", __func__, __LINE__); - return ret; } @@ -1186,20 +1372,17 @@ static void UsbSerialDriverRelease(struct HdfDeviceObject *device) { struct AcmDevice *acm = NULL; if (device == NULL) { - HDF_LOGE("%s: device is NULL", __func__); return; } acm = (struct AcmDevice *)device->service; if (acm == NULL) { - HDF_LOGE("%s: acm is null", __func__); return; } g_rawAcmReleaseFlag = true; if (acm->initFlag == true) { - HDF_LOGE("%s:%d UsbSerialRelease", __func__, __LINE__); UsbSerialRelease(acm); } UsbSeriaDevicelFree(acm); @@ -1208,12 +1391,11 @@ static void UsbSerialDriverRelease(struct HdfDeviceObject *device) OsalMutexDestroy(&acm->lock); OsalMemFree(acm); acm = NULL; - HDF_LOGD("%s:%d exit", __func__, __LINE__); } struct HdfDriverEntry g_usbSerialRawDriverEntry = { .moduleVersion = 1, - ..moduleName = "usbhost_acm_rawapi", // Driver module name, which must be the same as that configured in the .hcs file. + .moduleName = "usbhost_acm_rawapi", // Driver module name, which must be the same as that configured in the .hcs file. .Bind = UsbSerialDriverBind, .Init = UsbSerialDriverInit, .Release = UsbSerialDriverRelease, @@ -1221,100 +1403,133 @@ struct HdfDriverEntry g_usbSerialRawDriverEntry = { HDF_INIT(g_usbSerialRawDriverEntry); ``` +#### Developing Driver Using Device DDK APIs -### Developing Driver Using Device DDK APIs +The core code of the USB ACM device is stored in **drivers\peripheral\usb\gadget\function\acm\cdcacm.c**. The following sample code implements driver development by using the Device DDK APIs. To develop a driver, you must create a device based on the descriptor, obtain the interface, open the interface to obtain the pipe information, receive events, and then perform USB communication (such as read and write). When the device is uninstalled, you need to close the interface, stop receiving events, and remove the device. -The core code of the USB Abstract Control Model (ACM) device is available in **drivers\peripheral\usb\gadget\function\acm\cdcacm.c**. The following is an example. +1. Create a USB device. - -``` -1. Create a device. -static int32_t AcmCreateFuncDevice(struct UsbAcmDevice *acm, - struct DeviceResourceIface *iface) -{ - struct UsbFnDevice *fnDev = NULL; -struct UsbFnDescriptorData descData; -uint8_t useHcs; - ... -if (useHcs == 0) { - descData.type = USBFN_DESC_DATA_TYPE_DESC; - descData.descriptor = &g_masterFuncDevice; -} else { - descData.type = USBFN_DESC_DATA_TYPE_PROP; - descData.property = device->property; -} -/* Create a device. */ - fnDev = (struct UsbFnDevice *)UsbFnDeviceCreate(acm->udcName, &descData); - if (fnDev == NULL) { - HDF_LOGE("%s: create usb function device failed", __func__); - return HDF_FAILURE; - } - ... -} -2. Obtain an interface and open the interface for pipe information. -static int32_t AcmParseEachPipe(struct UsbAcmDevice *acm, struct UsbAcmInterface *iface) -{ - ... - for (i = 0; i < fnIface->info.numPipes; i++) { - struct UsbFnPipeInfo pipeInfo; -/* Obtain pipe information. */ - ret = UsbFnInterfaceGetPipeInfo(fnIface, i, &pipeInfo); + ```cpp + static int32_t AcmCreateFuncDevice(struct UsbAcmDevice *acm, struct DeviceResourceIface *iface) + { + struct UsbFnDevice *fnDev = NULL; + struct UsbFnDescriptorData descData; + uint8_t useHcs; + ... + if (useHcs == 0) { // The descriptor is sourced from the code. + descData.type = USBFN_DESC_DATA_TYPE_DESC; + descData.descriptor = &g_masterFuncDevice; + } else {// The descriptor is sourced from the .hcs file. + descData.type = USBFN_DESC_DATA_TYPE_PROP; + descData.property = device->property; + } + /* Create a device. */ + fnDev = (struct UsbFnDevice *)UsbFnDeviceCreate(acm->udcName, &descData); + if (fnDev == NULL) { + return HDF_FAILURE; + } ... } - return HDF_SUCCESS; -} -/* Obtain the interface and open the interface to obtain the handle. */ -static int32_t AcmParseEachIface(struct UsbAcmDevice *acm, struct UsbFnDevice *fnDev) -{ - ... - for (i = 0; i < fnDev->numInterfaces; i++) { - /* Obtain an interface.*/ - fnIface = (struct UsbFnInterface *)UsbFnDeviceGetInterface(fnDev, i); + ``` + +2. Obtain an interface and open the interface to obtain the pipe information. + + ```cpp + static int32_t AcmParseEachPipe(struct UsbAcmDevice *acm, struct UsbAcmInterface *iface) + { ... - /* Open the interface. */ - handle = UsbFnInterfaceOpen(fnIface); + for (i = 0; i < fnIface->info.numPipes; i++) { + struct UsbFnPipeInfo pipeInfo; + /* Obtain pipe information. */ + ret = UsbFnInterfaceGetPipeInfo(fnIface, i, &pipeInfo); + ... + } + return HDF_SUCCESS; + } + /* Obtain an interface and open the interface to obtain the handle. */ + static int32_t AcmParseEachIface(struct UsbAcmDevice *acm, struct UsbFnDevice *fnDev) + { ... + for (i = 0; i < fnDev->numInterfaces; i++) { + /* Obtain an interface.*/ + fnIface = (struct UsbFnInterface *)UsbFnGetInterface(fnDev, i); + ... + /* Open the interface. */ + handle = UsbFnInterfaceOpen(fnIface); + ... + } + return HDF_SUCCESS; } - return HDF_SUCCESS; -} -3. Receive events. -static int32_t AcmAllocCtrlRequests(struct UsbAcmDevice *acm, int num) -{ - ... + ``` + +3. Receive events (EP0 control transfer). + + ```cpp + static int32_t AcmAllocCtrlRequests(struct UsbAcmDevice *acm, int32_t num) + { + ... req = UsbFnCtrlRequestAlloc(acm->ctrlIface.handle, sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding)); - ... -} -static int32_t AcmDriverInit(struct HdfDeviceObject *device) -{ -... -/* Start to receive events.*/ - ret = UsbFnInterfaceStartRecvEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm); - ... -} + ... + } + static int32_t AcmDriverInit(struct HdfDeviceObject *device) + { + ... + /* Start to receive events.*/ + ret = UsbFnInterfaceStartRecvEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm); + ... + } + ``` + 4. Perform USB communication (read and write). -static int32_t AcmSendNotifyRequest(struct UsbAcmDevice *acm, uint8_t type, - uint16_t value, void *data, uint32_t length) -{ -... -/* Send an asynchronous request.*/ - ret = UsbFnRequestSubmitAsync(req); - ... -} -5. Close the interface, stop receiving events, and remove the device. -static int32_t AcmReleaseFuncDevice(struct UsbAcmDevice *acm) -{ -int32_t ret; -/* Close the interface. */ - (void)UsbFnInterfaceClose(acm->ctrlIface.handle); -(void)UsbFnInterfaceClose(acm->dataIface.handle); -/* Stop receiving events. */ -(void)UsbFnInterfaceStopRecvEvent(acm->ctrlIface.fn); -/* Remove the device. */ - ret = UsbFnDeviceRemove(acm->fnDev); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: remove usb function device failed", __func__); + + ```cpp + static int32_t AcmSendNotifyRequest(struct UsbAcmDevice *acm, uint8_t type, + uint16_t value, void *data, uint32_t length) + { + ... + /* Send an asynchronous request.*/ + ret = UsbFnRequestSubmitAsync(req); + ... } - return ret; -} -``` + ``` + +5. Close the interface, stop receiving events, and delete the device. + + ```cpp + static int32_t AcmReleaseFuncDevice(struct UsbAcmDevice *acm) + { + int32_t ret; + /* Close the interface. */ + (void)UsbFnInterfaceClose(acm->ctrlIface.handle); + (void)UsbFnInterfaceClose(acm->dataIface.handle); + /* Stop receiving the Event EP0 control transfer. */ + (void)UsbFnInterfaceStopRecvEvent(acm->ctrlIface.fn); + /* Delete the device. */ + ret = UsbFnDeviceRemove(acm->fnDev); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: remove usb function device failed", __func__); + } + return ret; + } + ``` + +## References + +- Code repositories: + + **[drivers\_hdf\_core](https://gitee.com/openharmony/drivers_hdf_core)** + + [drivers\_peripheral](https://gitee.com/openharmony/drivers_peripheral) + + [drivers\_interface](https://gitee.com/openharmony/drivers_interface) + +- Code paths: + + USB driver model adaptation for LiteOS: //drivers/hdf_core/adapter/khdf/liteos/model/usb + + USB DDK driver loading: //drivers/hdf_core/framework/model/usb + + USB HDI server implementation: //drivers/peripheral/usb/hdi_service + + USB HDI external APIs: //out/{product_name}/gen/drivers/interface/usb/v1_0 diff --git a/en/device-dev/driver/driver-peripherals-vibrator-des.md b/en/device-dev/driver/driver-peripherals-vibrator-des.md index 6a6164eef36695838ee3bfb9a0590c7e727d2b42..b9b12e246236afe77f4ff1f9a28883b51c5fbab6 100644 --- a/en/device-dev/driver/driver-peripherals-vibrator-des.md +++ b/en/device-dev/driver/driver-peripherals-vibrator-des.md @@ -2,9 +2,9 @@ ## Overview -### Introduction +### Function -Developed on the Hardware Driver Foundation (HDF), the vibrator driver model makes vibrator driver development easier. The motor driver model shields the interaction between the device driver and the system and provides unified and stable driver interface capabilities for the hardware service layer. It also provides open interfaces and interface parsing capabilities for developers to develop vibrator drivers and deploy vibrators in different OSs.
The figure below shows the vibrator driver model. +The vibrator driver model is developed based on OpenHarmony Hardware Driver Foundation (HDF) to facilitate vibrator driver development. The vibrator driver model shields the interaction between the device driver and the system, and provides unified and stable driver interface capabilities for the hardware service layer. It also provides open interfaces and interface parsing capabilities for developing vibrator drivers and deploying vibrators in different OSs.
The figure below shows the vibrator driver model. **Figure 1** Vibrator driver model @@ -12,15 +12,25 @@ Developed on the Hardware Driver Foundation (HDF), the vibrator driver model mak ### Basic Concepts -The system controls device vibration by invoking the vibrator. There are two vibration modes: +Vibrators can be classified into the following types based on vibration mechanism: + +- Rotor vibrator + + The rotor vibrator uses magnetic field caused by current to drive the rotor to rotate and produce vibration. Rotor vibrators include ordinary rotor vibrators and coin rotor vibrators. The rotor vibrators cannot start or stop quickly or implement multiple vibration modes. However, they have small footprint and are cost-efficient. + +- Linear vibrator + + The linear vibrator drives the spring mass for linear movement and produce vibration. Linear vibrators include longitudinal and transverse linear vibrators. The linear vibrators start and stop quickly, produce different vibration inductions, and have good sense of direction. + +The system calls the vibrator driver APIs to control the vibration of the device. There are two vibration modes: - One-shot vibration - The vibrator vibrates for a specified duration. + The vibrator vibrates once for a given duration. - Periodic vibration - The vibrator vibrates with a preset effect. For example, if **haptic.clock.timer** is set to **[600, 600, 200, 600]**, the vibrator waits for 600 ms, vibrates for 600 ms, waits for 200 ms, and vibrates for 600 ms. + The vibrator vibrates at preset intervals. For example, if **haptic.clock.timer** is set to **[600, 600, 200, 600]**, the vibrator waits for 600 ms, vibrates for 600 ms, waits for 200 ms, and vibrates for 600 ms. ### Working Principles @@ -30,18 +40,18 @@ The figure below shows how a vibrator driver is loaded. ![How vibrator driver works](figures/vibrator_working.png) -The following uses the vibrator driver on the Hi3516D V300 development board of the standard system as an example to describe the driver loading and running process. +The following describes how a vibrator module driver loads and starts on a Hi3516DV300 that runs the standard system. -1. The vibrator driver reads the vibrator management configuration information from **Vibrator Host** in the **device_info.hcs** file. -2. The HCS parser parses the vibrator management configuration and associates it with the vibrator abstract driver. -3. The vibrator chipset driver reads the vibrator data configuration from the **linear_vibrator_config.hcs** file. -4. The HCS parser parses the vibrator data configuration and associates it with the vibrator haptic driver. -5. The vibrator proxy delivers an instruction to the vibrator stub. -6. The vibrator stub calls the vibrator controller. -7. The vibrator host initializes the vibrator abstract driver interfaces. -8. The vibrator haptic driver starts a thread to parse the vibrator haptic module. -9. The vibrator haptic driver calls the **Start** interface in the vibrator abstract driver. -10. The vibrator abstract driver calls the **Start** interface in the vibrator chipset driver. +1. The Device Manager reads the vibrator management configuration from the **device_info.hcs** file. +2. The HDF Configuration Source (HCS) Parser parses the vibrator management configuration and loads the vibrator abstract driver. +3. The Device Manager reads the Vibrator data configuration from the **linear_vibrator_config.hcs** file. +4. The HCS Parser parses the vibrator data configuration and loads the haptic driver. +5. The Vibrator Proxy obtains the vibrator HDI service instance and sends it to the Vibrator Stub over Inter-Process Communication (IPC). +6. The Vibrator Stub processes IPC-related service logic and calls the Light Controller after parameter deserialization. +7. The Vibrator Controller implements the HDI APIs and calls the Vibrator abstract driver APIs over IPC. +8. The haptic driver starts a thread to parse the vibrator haptic module. +9. The haptic driver calls **Start()** in the vibrator abstract driver. +10. The vibrator abstract driver calls **Start()** in the vibrator differentiated driver to control the vibrator device to vibrate with a given effect. ## Development Guidelines @@ -51,17 +61,17 @@ You can set different vibration effects as needed, for example, customizing vibr ### Available APIs -The vibrator driver model supports static HDF Configuration Source (HCS) configuration and dynamic parameter configuration. The vibrator hardware service calls **StartOnce()** to trigger continuous vibration and calls **Start()** to trigger vibration with a specified effect. The table below lists the APIs provided by the vibrator driver model for the hardware service layer. +The vibrator driver model supports static HCS and dynamic parameter configuration. The vibrator hardware service calls **StartOnce()** to trigger continuous vibration and calls **Start()** to trigger vibration with a specified effect. The following table describes the APIs provided by the vibrator driver model for the hardware service layer. **Table 1** APIs of the vibrator driver model | API | Description | | -------------------------------------- | ------------------------------------------------ | -| int32_t StartOnce(uint32_t duration) | Triggers vibration with a given **duration**. | -| int32_t Start(const char *effectType) | Triggers vibration with a given effect, which is specified by **effectType**. | -| int32_t Stop(enum VibratorMode mode) | Stops vibration. | -| int32_t EnableVibratorModulation(uint32_t duration, int32_t intensity, int32_t frequency) | Triggers vibration with the given **duration**, **frequency**, and **intensity**.| -| int32_t GetVibratorInfo(struct VibratorInfo **vibratorInfo); | Obtains vibrator information, including whether the intensity and frequency can be set and the intensity and frequency range.| +| int32_t (*StartOnce)([in] uint32_t duration) | Triggers a one-short vibration with a given duration.
**duration** specifies the duration of the one-short vibration. | +| int32_t (*Start)([in] const char *effectType) | Triggers periodic vibrations with a preset effect.
**effectType** indicates the pointer to the preset effect type. | +| int32_t (*Stop)([in] enum VibratorMode mode) | Stops vibration.
**mode** indicates the vibration mode, which can be one-short or periodic vibration. | +| int32_t (*EnableVibratorModulation)(uint32_t duration, int32_t intensity, int32_t frequency) | Triggers a vibration with the given duration, frequency, and intensity.
**duration** indicates the duration of the vibration.
**intensity** indicates the vibration amplitude.
**frequency** indicates the vibrator frequency in the vibration period.| +| int32_t (*GetVibratorInfo)([out] struct VibratorInfo **vibratorInfo) | Obtains information about all vibrators whose amplitude and frequency can be set in the system.
**vibratorInfo** indicates the pointer to the vibrator information obtained.| ### How to Develop @@ -69,25 +79,25 @@ The vibrator driver model provides APIs for the upper-layer hardware service to 1. Develop the vibrator abstract driver based on the driver entry. Specifically, implement the **Bind**, **Init**, **Release**, and **Dispatch** functions, configure resources, and parse the HCS. - - Call **HDF_INIT** to register the driver entry with the HDF. During driver loading, the HDF calls the **Bind** function and then the **Init** function to load the driver. If the **Init** function fails to be called, the HDF calls **Release** to release the driver resources and exit the vibrator driver model. The vibrator driver model uses the HCS as the configuration source code. For details about HCS configuration fields, see [Configuration Management](driver-hdf-manage.md). The driver entry function is defined as follows: + - Call **HDF_INIT** to register the driver entry with the HDF. The HDF calls **Bind** and then **Init** to load the driver. If **Init** fails to be called, the HDF calls **Release** to release the driver resources and exit the vibrator driver model. The vibrator driver model uses the HCS as the configuration source code. For details about HCS fields, see [Configuration Management](driver-hdf-manage.md). The driver entry function is defined as follows: ```c /* Register the entry structure object of the vibrator abstract driver. */ struct HdfDriverEntry g_vibratorDriverEntry = { - .moduleVersion = 1, // Version of the vibrator module. - .moduleName = "HDF_VIBRATOR", // Vibrator module name, which must be the same as moduleName in the device_info.hcs file. - .Bind = BindVibratorDriver, // Function for binding a vibrator. - .Init = InitVibratorDriver, // Function for initializing a vibrator. - .Release = ReleaseVibratorDriver, // Function for releasing vibrator resources. + .moduleVersion = 1, // Vibrator module version. + .moduleName = "HDF_VIBRATOR", // Vibrator module name, which must be the same as moduleName in the device_info.hcs file. + .Bind = BindVibratorDriver, // Bind function for the vibrator driver. + .Init = InitVibratorDriver, // Ini function for the vibrator driver. + .Release = ReleaseVibratorDriver, // Release function for the vibrator driver. }; - + /* Call HDF_INIT to register the driver entry with the HDF. */ HDF_INIT(g_vibratorDriverEntry); ``` - Develop the vibrator abstract driver. Specifically, implement the **Bind**, **Init**, **Release**, and **Dispatch** functions. ```c - /* Message exchange capability of the vibrator driver. */ + /* External service published by the vibrator driver. */ static int32_t DispatchVibrator(struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply) { @@ -152,18 +162,19 @@ The vibrator driver model provides APIs for the upper-layer hardware service to ```c /* Device information HCS. */ vibrator :: host { - hostName = "vibrator_host"; - device_vibrator :: device { - device0 :: deviceNode { - policy = 2; // Policy for publishing the driver service. - priority = 100; // Driver startup priority (0–200). A larger value indicates a lower priority. The default value 100 is recommended. The sequence for loading devices with the same priority is random. - preload = 0; // Whether to load the driver on demand. The value 0 means to load the driver on demand, and 2 means the opposite. - permission = 0664; // Permission for the driver to create a device node. - moduleName = "HDF_VIBRATOR"; // Driver name, which must be the same as moduleName in the driver entry structure. - serviceName = "hdf_misc_vibrator"; // Name of the service provided by the driver. The name must be unique. - deviceMatchAttr = "hdf_vibrator_driver"; // Keyword matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. - } - } + hostName = "vibrator_host"; + device_vibrator :: device { + device0 :: deviceNode { + policy = 2; // Policy for the driver to publish services. + priority = 100; // Priority (0–200) for starting the vibrator driver. A larger value indicates a lower priority. The recommended value is 100. If the priorities are the same, the device loading sequence is not ensured. + preload = 0; // The value 0 means to load the driver by default during the startup of the system. The value 2 means the opposite. + permission = 0664; // Permission for the device node created. + moduleName = "HDF_VIBRATOR"; // Vibrator driver name. It must be the same as moduleName in the driver entry structure. + serviceName = "hdf_misc_vibrator"; // Service published by the vibrator driver. The service name must be unique. + deviceMatchAttr = "hdf_vibrator_driver"; // Keyword matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. + } + } + } ``` 2. Create a vibrator haptic model and parse the haptic HCS. @@ -204,21 +215,21 @@ The vibrator driver model provides APIs for the upper-layer hardware service to - The vibrator haptic model uses the HCS. For details about the HCS fields, see [Configuration Management](driver-hdf-manage.md). - ```hcs + ```c /* Vibrator data configuration template (vibrator_config.hcs). */ root { vibratorConfig { boardConfig { - match_attr = "hdf_vibrator_driver"; // The value must be the same as that of the match_attr field configured for the vibrator. + match_attr = "hdf_vibrator_driver"; // The value must be the same as that of match_attr in the vibrator device configuration file. vibratorAttr { /* The value 0 means a rotor vibrator, and 1 means a linear vibrator. */ - deviceType = 1; // Device type. - supportPreset = 1; // Supported preset type. + deviceType = 1; // Device type. + upportPreset = 1; // Supported preset type. } vibratorHapticConfig { haptic_clock_timer { effectName = "haptic.clock.timer"; - type = 1; // The value 0 means the built-in mode, and 1 means the time sequence. + type = 1; // The value 0 indicates the built-in mode, and 1 indicates the time sequence. seq = [600, 600, 200, 600]; // Time sequence. } haptic_default_effect { @@ -232,12 +243,12 @@ The vibrator driver model provides APIs for the upper-layer hardware service to } ``` -3. Develop the interfaces for starting and stopping vibration. A timer will be created and destroyed based on the vibration effect. +3. Implement the APIs for obtaining vibrator information, triggering and stopping a vibration, and creating and destroying a timer based on the vibration mode. - The vibrator hardware service calls **StartOnce** to start one-shot vibration with a given duration and calls **StartEffect** to start vibration with a specified effect. + The vibrator hardware service calls **StartOnce** to start a one-shot vibration with a given duration and calls **StartEffect** to start vibration with a specified effect. ```c - /* Trigger vibration with a given duration. */ + /* Trigger a one-short vibration with a given duration. */ static int32_t StartOnce(struct HdfSBuf *data, struct HdfSBuf *reply) { uint32_t duration; @@ -259,7 +270,7 @@ The vibrator driver model provides APIs for the upper-layer hardware service to return HDF_SUCCESS; } - /* Trigger vibration with a given effect. */ + /* Trigger a vibration with a given effect. */ static int32_t StartEffect(struct HdfSBuf *data, struct HdfSBuf *reply) { int32_t ret; @@ -313,11 +324,11 @@ The vibrator driver model provides APIs for the upper-layer hardware service to int32_t intensity; int32_t frequency; int32_t ret; - ..... + ..... (void)OsalMutexLock(&drvData->mutex); drvData->mode = VIBRATOR_MODE_ONCE; (void)OsalMutexUnlock(&drvData->mutex); - /* Set the vibration intensity and frequency. */ + /* Set the vibration intensity and frequency. */ ret = drvData->ops.SetParameter(intensity, frequency); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: set parameter failed", __func__); @@ -380,7 +391,7 @@ The vibrator driver model provides APIs for the upper-layer hardware service to } /* Register vibrator information. */ - int32_t RegisterVibratorInfo(struct VibratorInfo *vibratorInfo) + int32_t RegisterVibratorInfo(struct VibratorInfo *vibratorInfo) { struct VibratorDriverData *drvData = GetVibratorDrvData(); @@ -407,29 +418,29 @@ The vibrator driver model provides APIs for the upper-layer hardware service to static int32_t StopModulationParameter() { uint8_t value[DRV2605L_VALUE_BUTT]; - struct Drv2605lDriverData *drvData = NULL; + struct Drv2605lDriverData *drvData = NULL; drvData = GetDrv2605lDrvData(); - CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); + CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData->drv2605lCfgData, HDF_FAILURE); value[DRV2605L_ADDR_INDEX] = (uint8_t)DRV2605_REG_MODE; value[DRV2605L_VALUE_INDEX] = (uint8_t)DRV2605_MODE_STANDBY; if (WriteDrv2605l(&drvData->drv2605lCfgData->vibratorBus.i2cCfg, value, sizeof(value)) != HDF_SUCCESS) { HDF_LOGE("%s: i2c addr [%0X] write failed", __func__, value[DRV2605L_ADDR_INDEX]); - return HDF_FAILURE; + return HDF_FAILURE; } value[DRV2605L_ADDR_INDEX] = (uint8_t)DRV2605_REG_RTPIN; value[DRV2605L_VALUE_INDEX] = (uint8_t)&drvData->drv2605lCfgData->vibratorAttr.defaultIntensity; if (WriteDrv2605l(&drvData->drv2605lCfgData->vibratorBus.i2cCfg, value, sizeof(value)) != HDF_SUCCESS) { - HDF_LOGE("%s: i2c addr [%0X] write failed", __func__, value[DRV2605L_ADDR_INDEX]); + HDF_LOGE("%s: i2c addr [%0X] write failed", __func__, value[DRV2605L_ADDR_INDEX]); } value[DRV2605L_ADDR_INDEX] = (uint8_t)DRV2605_REG_LRARESON; value[DRV2605L_VALUE_INDEX] = (uint8_t)&drvData->drv2605lCfgData->vibratorAttr.defaultFrequency; if (WriteDrv2605l(&drvData->drv2605lCfgData->vibratorBus.i2cCfg, value, sizeof(value)) != HDF_SUCCESS) { - HDF_LOGE("%s: i2c addr [%0X] write failed", __func__, value[DRV2605L_ADDR_INDEX]); + HDF_LOGE("%s: i2c addr [%0X] write failed", __func__, value[DRV2605L_ADDR_INDEX]); } return HDF_SUCCESS; @@ -439,7 +450,7 @@ The vibrator driver model provides APIs for the upper-layer hardware service to static void SetModulationParameter(int32_t intensity, int32_t frequency) { uint8_t value[DRV2605L_VALUE_BUTT]; - struct Drv2605lDriverData *drvData = NULL; + struct Drv2605lDriverData *drvData = NULL; drvData = GetDrv2605lDrvData(); CHECK_VIBRATOR_NULL_PTR_RETURN_VALUE(drvData, HDF_FAILURE); @@ -453,7 +464,7 @@ The vibrator driver model provides APIs for the upper-layer hardware service to } } else { HDF_LOGD("%s: the setting of intensity 0 is not supported and \ - will be set as the system default intensity", __func__); + will be set as the system default intensity", __func__); } if (frequency != 0) { @@ -472,9 +483,9 @@ The vibrator driver model provides APIs for the upper-layer hardware service to ### Verification -After the driver is developed, develop test cases in the sensor unit test to verify the basic functions of the driver. Use the developer self-test platform as the test environment. +After the driver is developed, develop test cases in the vibrator unit test to verify the basic functionalities of the driver. Use your own platform as the test environment. -``` +```c++ /* Initialize the vibrator interface instance before executing test cases. */ void HdfVibratorTest::SetUpTestCase() { diff --git a/en/device-dev/driver/driver-platform-adc-des.md b/en/device-dev/driver/driver-platform-adc-des.md index 76c73fb3e947e45190831c6f060ae1c1ac1d97db..29a7a3787258b63611a92e23aba2aea8fdb31654 100644 --- a/en/device-dev/driver/driver-platform-adc-des.md +++ b/en/device-dev/driver/driver-platform-adc-des.md @@ -4,78 +4,69 @@ ### Function -An analog-to-digital converter (ADC) is a device that converts analog signals into digital signals. +An analog-to-digital converter (ADC) converts analog signals into digital signals for storage and computing. In addition to the power cable and ground cable, the ADC requires only one cable to connect to the target device. The following figure shows the physical connection of the ADC. + +**Figure 1** ADC physical connection + +![](figures/ADC_physical_connection.png "ADC_physical_connection") + +The ADC module provides a set of APIs to complete AD conversion, including: -The ADC APIs provide a set of common functions for ADC data transfer, including: - Opening or closing an ADC device - Obtaining the analog-to-digital (AD) conversion result ### Basic Concepts -The ADC converts analog parameters into digital parameters for easy storage and computing. The technical specifications of the ADC include the following: - - Resolution - + The number of binary bits that can be converted by an ADC. A greater number of bits indicates a higher resolution. + - Conversion error - + Difference between the actual and theoretical digital values output by an ADC. It is expressed by a multiple of the least significant bit. Generally, the maximum output error is used. + - Transition time - + Time required by an ADC to perform a complete conversion. ### Working Principles In the Hardware Driver Foundation (HDF), the ADC module uses the unified service mode for API adaptation. In this mode, a service is used as the ADC manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. -The ADC module is divided into the following layers: - -- Interface layer: provides APIs for opening or closing a device and writing data. -- Core layer: provides the capabilities of binding, initializing, and releasing devices. -- Adaptation layer: implements driver-specific functions. - -In addition to the power and ground cables, the ADC requires only one cable to connect to the target device. The figure below shows the physical connection. - -**Figure 1** ADC physical connection - - -![](figures/ADC_physical_connection.png "ADC_physical_connection") - ### Constraints -Currently, the ADC module supports only the kernels (LiteOS) of mini and small systems. +The ADC module can read data only in polling mode. ## Usage Guidelines ### When to Use -An ADC is usually used to convert an analog voltage into a digital parameter, for example, it is used with a microphone to collect sound, used with an NTC resistor to measure temperature, or converts the output of analog sensors into digital parameters. +ADC devices are used to convert analog voltage or current into digital parameters. For example, an ADC can be used with an NTC resistor to measure temperature, or can be used to convert the output of an analog sensor into a digital parameter. ### Available APIs -The table below describes the APIs of the ADC module. For more details, see API Reference. +The following table describes the APIs of the ADC module. For more information, see **//drivers/hdf_core/framework/include/platform/adc_if.h**. **Table 1** APIs of the ADC driver - -| API | Description | +| API | Description | | -------- | ---------------- | -| AdcOpen | Opens an ADC device. | -| AdcClose | Closes an ADC device. | -| AdcRead | Obtains the AD conversion result.| +| DevHandle AdcOpen(uint32_t number) | Opens an ADC device. | +| void AdcClose(DevHandle handle) | Closes an ADC device. | +| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | Obtains the AD conversion result.| ### How to Develop -The figure below shows the general development process. +The following figure illustrates how to use ADC APIs. - **Figure 2** Process of using ADC APIs + **Figure 2** Process of using ADC APIs ![](figures/using-ADC-process.png) -#### Opening an ADC Device. +#### Opening an ADC Device -Call **AdcOpen** to open an ADC device. +Call **AdcOpen()** to open an ADC device. ```c DevHandle AdcOpen(int16_t number); @@ -83,12 +74,11 @@ DevHandle AdcOpen(int16_t number); **Table 2** Description of AdcOpen - | Parameter | Description | | ---------- | ----------------- | | number | ADC device number. | | **Return Value**| **Description** | -| NULL | The operation failed. | +| NULL | The operation fails. | | Device handle | The operation is successful. The handle of the ADC device opened is returned.| Example: Open device 1 of the two ADCs (numbered 0 and 1) in the system. @@ -99,7 +89,7 @@ DevHandle adcHandle = NULL; /* ADC device handle / /* Open ADC device 1. */ adcHandle = AdcOpen(1); if (adcHandle == NULL) { - HDF_LOGE("AdcOpen: failed\n"); + HDF_LOGE("AdcOpen: fail\n"); return; } ``` @@ -112,7 +102,6 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val); **Table 3** Description of AdcRead - | Parameter | Description | | ---------- | -------------- | | handle | ADC device handle. | @@ -120,7 +109,7 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val); | val | Pointer to the AD conversion result. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the AD conversion result of channel 1. @@ -137,13 +126,12 @@ if (ret != 0) { #### Closing an ADC Device -Call **AdcClose** to close the ADC device after the ADC communication is complete. +Call **AdcClose()** to close the ADC device after the ADC communication is complete. ```c void AdcClose(DevHandle handle); ``` **Table 4** Description of AdcClose - | Parameter | Description | | ------ | ----------- | | handle | ADC device handle.| @@ -158,13 +146,13 @@ AdcClose(adcHandle); /* Close the ADC device. */ ### Example -This following example shows how to use ADC APIs to manage an ADC device on a Hi3516D V300 board. +The following example shows how to use ADC APIs to manage an ADC device on a Hi3516D V300 board. -The basic hardware information is as follows: +The hardware information is as follows: - SoC: hi3516dv300 -- The potentiometer is connected to channel 1 of ADC device 0. +- The potentiometer is connected to channel 1 of ADC 0. Perform continuous read operations on the ADC device to check whether the ADC is functioning. @@ -175,16 +163,17 @@ Example: #include "hdf_log.h" /* Header file for log APIs */ /* Define device 0, channel 1. */ -#define ADC_DEVICE_NUM 0 +#define ADC_DEVICE_NUM 0 #define ADC_CHANNEL_NUM 1 +#define ADC_TEST_NUM 30 /* Main entry of ADC routines. */ static int32_t TestCaseAdc(void) { int32_t i; int32_t ret; - DevHandle adcHandle; - uint32_t readBuf[30] = {0}; + DevHandle adcHandle = NULL; + uint32_t readBuf[ADC_TEST_NUM] = {0}; /* Open the ADC device. */ adcHandle = AdcOpen(ADC_DEVICE_NUM); @@ -194,10 +183,10 @@ static int32_t TestCaseAdc(void) } /* Perform 30 times of AD conversions continuously and read the conversion results. */ - for (i = 0; i < 30; i++) { + for (i = 0; i < ADC_TEST_NUM; i++) { ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: Failed to read ADC!:%d", __func__, ret); + HDF_LOGE("%s: ADC read fail!:%d", __func__, ret); AdcClose(adcHandle); return -1; } diff --git a/en/device-dev/driver/driver-platform-adc-develop.md b/en/device-dev/driver/driver-platform-adc-develop.md index 8f4c980a10cb0462f31f7ea01b803e509886bd42..314e70aaa70cb2be284b12ea687a68641ab6658c 100644 --- a/en/device-dev/driver/driver-platform-adc-develop.md +++ b/en/device-dev/driver/driver-platform-adc-develop.md @@ -1,38 +1,112 @@ # ADC - ## Overview -An analog-to-digital converter (ADC) is a device that converts analog signals into digital signals. In the Hardware Driver Foundation (HDF), the ADC module uses the unified service mode for API adaptation. In this mode, a device service is used as the ADC manager to handle external access requests in a unified manner, which is reflected in the configuration file. The unified service mode applies to the scenario where there are many device objects of the same type, for example, when the ADC has more than 10 controllers. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services. +### Function - **Figure 1** Unified service mode +An analog-to-digital converter (ADC) is a device that converts analog signals into digital signals. - ![](figures/unified-service-mode.png "ADC Unified Service Mode") +### Basic Concepts +- Resolution -## Available APIs + The number of binary bits that can be converted by an ADC. A greater number of bits indicates a higher resolution. -**AdcMethod**: +- Conversion error + Difference between the actual and theoretical digital values output by an ADC. It is expressed by a multiple of the least significant bit. Generally, the maximum output error is used. -``` +- Transition time + + Time required by an ADC to perform a complete conversion. + + +### Working Principles + +In the Hardware Driver Foundation (HDF), the ADC module uses the unified service mode for API adaptation. In this mode, a device service is used as the ADC manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure illustrates the unified service mode of the ADC module. + +The ADC module is divided into the following layers: + +- Interface layer: provides the capabilities of opening a device, writing data, and closing a device. +- Core layer: binds services, initializes and releases the PlatformManager, and provides the capabilities of adding, deleting, and obtaining controllers. +- Adaptation layer: implements hardware-related functions, such as controller initialization. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. + +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png "ADC Unified Service Mode") + +## Usage Guidelines + +### When to Use + +ADC devices are used to convert analog voltage into digital parameters. For example, an ADC can be used with an NTC resistor to measure temperature, or can be used to convert the output of an analog sensor into a digital parameter. Before using ADC devices with OpenHarmony, you need to adapt the ADC driver to OpenHarmony. The following describes how to do it. + +### Available APIs + +To enable the upper layer to successfully operate the hardware by calling the ADC APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/adc/adc_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +Definitions of **AdcMethod** and **AdcLockMethod**: + +```c struct AdcMethod { - int32_t (*read)(struct AdcDevice *device, uint32_t channel, uint32_t *val); - int32_t (*start)(struct AdcDevice *device); - int32_t (*stop)(struct AdcDevice *device); + int32_t (*read)(struct AdcDevice *device, uint32_t channel, uint32_t *val); + int32_t (*start)(struct AdcDevice *device); + int32_t (*stop)(struct AdcDevice *device); }; + +struct AdcLockMethod { + int32_t (*lock)(struct AdcDevice *device); + void (*unlock)(struct AdcDevice *device); +}; + ``` - **Table 1** Description of the callback functions in AdcMethod +At the adaptation layer, **AdcMethod** must be implemented, and **AdcLockMethod** can be implemented based on service requirements. The core layer provides the default **AdcLockMethod**, in which a spinlock is used to protect the critical section. + +```c +static int32_t AdcDeviceLockDefault(struct AdcDevice *device) +{ + if (device == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + return OsalSpinLock(&device->spin); +} + +static void AdcDeviceUnlockDefault(struct AdcDevice *device) +{ + if (device == NULL) { + return; + } + (void)OsalSpinUnlock(&device->spin); +} + +static const struct AdcLockMethod g_adcLockOpsDefault = { + .lock = AdcDeviceLockDefault, + .unlock = AdcDeviceUnlockDefault, +}; + +``` + +If spinlock cannot be used, you can use another type of lock to implement **AdcLockMethod**. The custom **AdcLockMethod** will replace the default **AdcLockMethod**. + + **Table 1** Hook functions in **AdcMethod** | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| read | **device**: structure pointer to the ADC controller at the core layer.
**channel**: channel ID, which is of the uint32_t type.| **val**: pointer to the signal data to be transmitted. It is of the uint32_t type.| HDF_STATUS| Reads the signal data sampled by the ADC.| +| read | **device**: structure pointer to the ADC controller at the core layer.
**channel**: channel number, which is of the uint32_t type.| **val**: pointer to the signal data to be transmitted. It is of the uint32_t type.| HDF_STATUS| Reads the signal data sampled by the ADC.| | stop | **device**: structure pointer to the ADC controller at the core layer.| –| HDF_STATUS| Stops an ADC device.| | start | **device**: structure pointer to the ADC controller at the core layer.| –| HDF_STATUS| Starts an ADC device.| +**Table 2** Functions in **AdcLockMethod** + +| Function| Input Parameter| Output Parameter| Return Value| Description| +| -------- | -------- | -------- | -------- | -------- | +| lock | **device**: structure pointer to the ADC device object at the core layer.| –| HDF_STATUS| Acquires the critical section lock.| +| unlock | **device**: structure pointer to the ADC device object at the core layer.| –| HDF_STATUS| Releases the critical section lock.| -## How to Develop +### How to Develop The ADC module adaptation involves the following steps: @@ -44,137 +118,144 @@ The ADC module adaptation involves the following steps: - Add the **deviceNode** information to the **device_info.hcs** file. - (Optional) Add the **adc_config.hcs** file. -3. Instantiate the ADC controller object. +3. Instantiate the core layer APIs. - Initialize **AdcDevice**. - Instantiate **AdcMethod** in the **AdcDevice** object. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details, see [Available APIs](#available-apis). - -4. Debug the driver. - - (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and check whether signals are successfully collected. + > For details about the functions in **AdcMethod**, see [Available APIs](#available-apis). +### Example -## Development Example +The following uses the Hi3516D V300 driver **//device/soc/hisilicon/common/platform/adc/adc_hi35xx.c** as an example to describe how to perform the ADC driver adaptation. - The following uses **adc_hi35xx.c** as an example to present the information required for implementing device functions. 1. Instantiate the driver entry. - + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - + Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - + ADC driver entry example: - - Many devices may connect to the ADC. In the HDF, a manager object needs to be created for the ADC. When a device needs to be started, the manager object locates the target device based on the specified parameters. - - The driver of the ADC manager is implemented by the core layer. The **Init** function of the driver layer calls **AdcDeviceAdd** of the core layer to add devices to the ADC manager. Therefore, you do not need to implement this part. - - ``` - static struct HdfDriverEntry g_hi35xxAdcDriverEntry = { - .moduleVersion = 1, - .Init = Hi35xxAdcInit, - .Release = Hi35xxAdcRelease, - .moduleName = "hi35xx_adc_driver", // (Mandatory) This parameter must be the same as that in the .hcs file. + + Multiple devices may connect to the ADC controller. In the HDF, a manager object needs to be created for this type of devices. When a device needs to be started, the manager object locates the target device based on the specified parameters. + + You do not need to implement the driver of the ADC manager, which is implemented by the core layer. However, the **AdcDeviceAdd** function of the core layer must be invoked in the **Init** function to implement the related features. + + ```c + static struct HdfDriverEntry g_hi35xxAdcDriverEntry = { + .moduleVersion = 1, + .Init = Hi35xxAdcInit, + .Release = Hi35xxAdcRelease, + .moduleName = "hi35xx_adc_driver", // (Mandatory) The value must be the same as the module name in the device_info.hcs file. }; - HDF_INIT(g_hi35xxAdcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. - - // Driver entry of the adc_core.c manager service at the core layer - struct HdfDriverEntry g_adcManagerEntry = { - .moduleVersion = 1, - .Init = AdcManagerInit, - .Release = AdcManagerRelease, - .moduleName = "HDF_PLATFORM_ADC_MANAGER", // The value is device0 in the device_info file. - }; - HDF_INIT(g_adcManagerEntry); - ``` - -2. Add **deviceNode** to the **device_info.hcs** file, and configure the device attributes in the **adc_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or restriction ranges of the **AdcDevice** members at the core layer. - - In the unified service mode, the first device node in the **device_info** file must be the ADC manager. The parameters must be set as follows: - - | Parameter| Value| - | -------- | -------- | - | moduleName | **HDF_PLATFORM_ADC_MANAGER**| - | serviceName | –| - | policy | **0**, which indicates that no service is published.| - | deviceMatchAttr | Reserved.| - - Configure ADC controller information from the second node. This node specifies a type of ADC controllers rather than an ADC controller. In this example, there is only one ADC device. If there are multiple ADC devices, add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **adc_config** file for each device. - - - **device_info.hcs** configuration example - - - ``` + HDF_INIT(g_hi35xxAdcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + + /* Driver entry of the adc_core.c manager service at the core layer */ + struct HdfDriverEntry g_adcManagerEntry = { + .moduleVersion = 1, + .Init = AdcManagerInit, + .Release = AdcManagerRelease, + .moduleName = "HDF_PLATFORM_ADC_MANAGER", // The value must be that of device0 in the device_info.hcs file. + }; + HDF_INIT(g_adcManagerEntry); + ``` + +2. Add the **deviceNode** information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file and configure the device attributes in **adc_config.hcs**. + + The **deviceNode** information is related to the driver entry registration. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **AdcDevice** members at the core layer. + + In the unified service mode, the first device node in the **device_info.hcs** file must be the ADC manager. The parameters must be set as follows: + + | Parameter| Value| + | -------- | -------- | + | moduleName | **HDF_PLATFORM_ADC_MANAGER**| + | serviceName | –| + | policy | **0**, which indicates that no service is published.| + | deviceMatchAttr | Reserved.| + + Configure ADC controller information from the second node. This node specifies a type of ADC controllers rather than an ADC controller. In this example, there is only one ADC device. If there are multiple ADC devices, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **adc_config** file for each device. + + - **device_info.hcs** example + + ```c root { device_info { - platform :: host { - device_adc :: device { - device0 :: deviceNode { - policy = 0; - priority = 50; - permission = 0644; - moduleName = "HDF_PLATFORM_ADC_MANAGER"; - serviceName = "HDF_PLATFORM_ADC_MANAGER"; - } - device1 :: deviceNode { - policy = 0; // The value 0 indicates that no service is published. - priority = 55; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "hi35xx_adc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HI35XX_ADC_DRIVER"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_adc";// (Mandatory) Private data of the controller. The value must be the same as that of the corresponding controller in adc_config.hcs. - // The specific controller information is configured in adc_config.hcs. - } - } + platform :: host { + device_adc :: device { + device0 :: deviceNode { + policy = 0; + priority = 50; + permission = 0644; + moduleName = "HDF_PLATFORM_ADC_MANAGER"; + serviceName = "HDF_PLATFORM_ADC_MANAGER"; + } + device1 :: deviceNode { + policy = 0; // The value 0 indicates that no service is published. + priority = 55; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi35xx_adc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HI35XX_ADC_DRIVER"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hisilicon_hi35xx_adc"; // (Mandatory) Private data of the controller. The value must be the same as that of the controller in adc_config.hcs. + // The specific controller information is in adc_config.hcs. + } + } + } } - } } ``` - - **adc_config.hcs** configuration example - - ``` + - **adc_config.hcs** example + + The following uses Hi3516D V300 as an example. Some fields are unique to Hi3516D V300. You can delete or add fields as required. + + ```c root { - platform { - adc_config_hi35xx { - match_attr = "hisilicon_hi35xx_adc"; - template adc_device { - regBasePhy = 0x120e0000;// Physical base address of the register. - regSize = 0x34; // Bit width of the register. - deviceNum = 0; // Device number. - validChannel = 0x1; // Valid channel. - dataWidth = 10; // Bit width of the received signal. - scanMode = 1; // Scan mode. - delta = 0; // Delta parameter. - deglitch = 0; - glitchSample = 5000; - rate = 20000; + platform { + adc_config_hi35xx { + match_attr = "hisilicon_hi35xx_adc"; + template adc_device { + regBasePhy = 0x120e0000; // Physical base address of the register. + regSize = 0x34; // Bit width of the register. + deviceNum = 0; // Device number. + validChannel = 0x1; // Valid channel. + dataWidth = 10; // Data width after AD conversion, that is, the resolution. + scanMode = 1; // Scan mode. + delta = 0; // Error range of the conversion result. + deglitch = 0; // Setting of the deglitch. + glitchSample = 5000; // Deglitch time window. + rate = 20000; // Conversion rate. + } + device_0 :: adc_device { + deviceNum = 0; + validChannel = 0x2; + } + } } - device_0 :: adc_device { - deviceNum = 0; - validChannel = 0x2; - } - } - } } ``` -3. Initialize the **AdcDevice** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **AdcMethod** in **AdcDevice** (so that the underlying driver functions can be called). - - Defining a custom structure + After the **adc_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface()** function provided by the HDF reads **adc_config.hcs** to initialize the custom structure and passes some important parameters, such as the device number and bus number, to the **AdcDevice** object at the core layer. + For example, if the **adc_config.hcs** file is in **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/**, add the following statement to **hdf.hcs** of the product: - + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs" // Relative path of the configuration file ``` + + This example is based on the Hi3516D V300 development board that runs the LiteOS. The corresponding **hdf.hcs** file is in **vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs** and **//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs**. You can modify the file as required. + +3. Initialize the **AdcDevice** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **AdcMethod** in **AdcDevice** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The DeviceResourceIface() function provided by the HDF reads **adc_config.hcs** to initialize the custom structure and passes some important parameters, such as the device number and bus number, to the **AdcDevice** object at the core layer. + + ```c struct Hi35xxAdcDevice { - struct AdcDevice device; // (Mandatory) Control object at the core layer. For details, see the description of AdcDevice. + struct AdcDevice device; // (Mandatory) Control object at the core layer. It must be the first member of the custom structure. For details, see the following description. volatile unsigned char *regBase; // (Mandatory) Register base address. volatile unsigned char *pinCtrlBase; uint32_t regBasePhy; // (Mandatory) Physical base address of the register. - uint32_t regSize; // (Mandatory) Bit width of the register. + uint32_t regSize; // (Mandatory) Register bit width. uint32_t deviceNum; // (Mandatory) Device number. uint32_t dataWidth; // (Mandatory) Data bit width of received signals. uint32_t validChannel; // (Mandatory) Valid channel. @@ -185,7 +266,7 @@ The ADC module adaptation involves the following steps: uint32_t rate; // (Mandatory) Sampling rate. }; - // AdcDevice is the core layer controller structure. The **Init()** function assigns values to the members of AdcDevice. + /* AdcDevice is the core layer controller structure. The **Init()** function assigns values to the members of AdcDevice. */ struct AdcDevice { const struct AdcMethod *ops; OsalSpinlock spin; @@ -196,25 +277,27 @@ The ADC module adaptation involves the following steps: }; ``` - - Instantiating **AdcMethod** in **AdcDevice**
This example does not provide the implementation of the **AdcLockMethod** callback. For details, see I2C driver development. Other members are initialized by the **Init** function. + - Instantiate the hook function structure **AdcMethod** of **AdcDevice**. - - ``` + The **AdcLockMethod** is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in the **Init** function. + + ```c static const struct AdcMethod g_method = { .read = Hi35xxAdcRead, .stop = Hi35xxAdcStop, .start = Hi35xxAdcStart, }; ``` - - **Init** function - **Input parameter**: + - Implement the **Init** function. + + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. | Status| Description| | -------- | -------- | @@ -225,19 +308,19 @@ The ADC module adaptation involves the following steps: | HDF_SUCCESS | Transmission successful.| | HDF_FAILURE | Transmission failed.| - **Function description**: + Function description: - Initializes the custom structure object and **AdcDevice**, and calls the **AdcDeviceAdd** function at the core layer. + Initializes the custom structure object and **AdcDevice**, and calls the **AdcDeviceAdd** function at the core layer. - ``` + ```c static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device) { int32_t ret; struct DeviceResourceNode *childNode = NULL; ... - // Traverse and parse all nodes in adc_config.hcs and call the **Hi35xxAdcParseInit** function to initialize the devices separately. + /* Traverse and parse all nodes in adc_config.hcs and call the **Hi35xxAdcParseInit** function to initialize the devices separately. */ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = Hi35xxAdcParseInit(device, childNode);// The function definition is as follows: + ret = Hi35xxAdcParseInit(device, childNode); // The function definition is as follows: ... } return ret; @@ -245,87 +328,111 @@ The ADC module adaptation involves the following steps: static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node) { - int32_t ret; - struct Hi35xxAdcDevice *hi35xx = NULL; // (Mandatory) Custom structure object. - (void)device; - - hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); // (Mandatory) Allocate memory. - ... - ret = Hi35xxAdcReadDrs(hi35xx, node); // (Mandatory) Fill the default values in the adc_config file to the structure. - ... - hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize);// (Mandatory) Address mapping. - ... - hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE); - ... - Hi35xxAdcDeviceInit(hi35xx); // (Mandatory) Initialize the ADC. - hi35xx->device.priv = (void *)node; // (Mandatory) Store device attributes. - hi35xx->device.devNum = hi35xx->deviceNum;// (Mandatory) Initialize AdcDevice members. - hi35xx->device.ops = &g_method; // (Mandatory) Attach the AdcMethod instance object. - ret = AdcDeviceAdd(&hi35xx->device); // (Mandatory) Call this function to set the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. - ... - return HDF_SUCCESS; + int32_t ret; + struct Hi35xxAdcDevice *hi35xx = NULL; // (Mandatory) Custom structure object. + (void)device; + + hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); // (Mandatory) Allocate memory. + ... + ret = Hi35xxAdcReadDrs(hi35xx, node); // (Mandatory) Use the default values in the adc_config file to fill in the structure. The function definition is as follows. + ... + hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // (Mandatory) Address mapping. + ... + hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE); + ... + Hi35xxAdcDeviceInit(hi35xx); // (Mandatory) Initialize the ADC. + hi35xx->device.priv = (void *)node; // (Mandatory) Save device attributes. + hi35xx->device.devNum = hi35xx->deviceNum;// (Mandatory) Initialize AdcDevice. + hi35xx->device.ops = &g_method; // (Mandatory) Attach the AdcMethod instance object. + ret = AdcDeviceAdd(&hi35xx->device)); // (Mandatory) Call this function to set the structure at the core layer. The driver can access the platform core layer only after a success signal is returned. + ... + return HDF_SUCCESS; __ERR__: - if (hi35xx != NULL) { // If the operation fails, execute the initialization function reversely. - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - hi35xx->regBase = NULL; + if (hi35xx != NULL) { // If the operation fails, deinitialize related functions. + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + hi35xx->regBase = NULL; + } + AdcDeviceRemove(&hi35xx->device); + OsalMemFree(hi35xx); } - AdcDeviceRemove(&hi35xx->device); - OsalMemFree(hi35xx); + return ret; } - return ret; + + static int32_t Hi35xxAdcReadDrs(struct Hi35xxAdcDevice *hi35xx, const struct DeviceResourceNode *node) + { + int32_t ret; + struct DeviceResourceIface *drsOps = NULL; + + /* Obtain the drsOps method. */ + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL) { + HDF_LOGE("%s: invalid drs ops", __func__); + return HDF_ERR_NOT_SUPPORT; + } + /* Read the configuration parameters in sequence and fill them in the structure. */ + ret = drsOps->GetUint32(node, "regBasePhy", &hi35xx->regBasePhy, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regBasePhy failed", __func__); + return ret; + } + ret = drsOps->GetUint32(node, "regSize", &hi35xx->regSize, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regSize failed", __func__); + return ret; + } + ··· + return HDF_SUCCESS; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + Input parameter: - **Return value**: + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. - No value is returned. + Return value: - **Function description**: + No value is returned. - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Function description: - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. + Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. - - ``` + ```c static void Hi35xxAdcRelease(struct HdfDeviceObject *device) { - const struct DeviceResourceNode *childNode = NULL; - ... - // Traverse and parse all nodes in adc_config.hcs and perform the release operation on each node. - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - Hi35xxAdcRemoveByNode(childNode);// The function definition is as follows: - } + const struct DeviceResourceNode *childNode = NULL; + ... + /* Traverse and parse all nodes in adc_config.hcs and perform the release operation on each node. */ + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + Hi35xxAdcRemoveByNode(childNode);// The function definition is as follows: + } } static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node) { - int32_t ret; - int32_t deviceNum; - struct AdcDevice *device = NULL; - struct Hi35xxAdcDevice *hi35xx = NULL; - struct DeviceResourceIface *drsOps = NULL; - - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - ... - ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0); - ... - // You can call the AdcDeviceGet function to obtain the AdcDevice object based on deviceNum and call the AdcDeviceRemove function to release the AdcDevice object. - device = AdcDeviceGet(deviceNum); - if (device != NULL && device->priv == node) { - AdcDevicePut(device); - AdcDeviceRemove(device); // (Mandatory) Remove the AdcDevice object from the driver manager. - hi35xx = (struct Hi35xxAdcDevice *)device;// (Mandatory) Obtain the custom object through forced conversion and release the operation. - OsalIoUnmap((void *)hi35xx->regBase); - OsalMemFree(hi35xx); + int32_t ret; + int32_t deviceNum; + struct AdcDevice *device = NULL; + struct Hi35xxAdcDevice *hi35xx = NULL; + struct DeviceResourceIface *drsOps = NULL; + + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + ... + ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0); + ... + /* You can use AdcDeviceGet() to obtain the AdcDevice object based on deviceNum and use AdcDeviceRemove() to release the AdcDevice object. */ + device = AdcDeviceGet(deviceNum); + if (device != NULL && device->priv == node) { + AdcDevicePut(device); + AdcDeviceRemove(device); // (Mandatory) Remove the AdcDevice object from the driver manager. + hi35xx = (struct Hi35xxAdcDevice *)device; // (Mandatory) Obtain the custom object through forcible conversion and perform the Release operation. To perform this operation, the device must be the first member of the custom structure. + OsalIoUnmap((void *)hi35xx->regBase); + OsalMemFree(hi35xx); + } + return; } - return ``` diff --git a/en/device-dev/driver/driver-platform-dac-des.md b/en/device-dev/driver/driver-platform-dac-des.md index 4cffd91b8618ab4f466ce32f1ff0ca3e959f1e6b..c13d9ddcf6abf07cec9121e87b32dd0a4e5aab46 100644 --- a/en/device-dev/driver/driver-platform-dac-des.md +++ b/en/device-dev/driver/driver-platform-dac-des.md @@ -2,18 +2,19 @@ ## Overview -### DAC +### Function -A digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics. +A digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics. DAC devices are used to: -The DAC APIs provide a set of methods for DAC data transfer, including: +- Provide the output channel for the process control computer system and connect to the executor to implement automatic control of the production process. +- Serve as an important module in the analog-to-digital converter using feedback technologies. + +The DAC module provides a set of methods for DAC data transfer, including: - Opening or closing a DAC device - Setting the target digital-to-analog (DA) value ### Basic Concepts -The DAC module provides the output channel for the process control computer system. It connects to the executor to implement automatic control of the production process. It is also an important module in the analog-to-digital converter using feedback technologies. - - Resolution The number of binary bits that can be converted by a DAC. A greater number of bits indicates a higher resolution. @@ -32,7 +33,7 @@ The DAC module provides the output channel for the process control computer syst ### Working Principles -In the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a service is used as the DAC manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The figure below shows the unified service mode. +In the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a service is used as the DAC manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services. The following figure illustrates the unified service mode of the DAC module. The DAC module is divided into the following layers: @@ -40,9 +41,7 @@ The DAC module is divided into the following layers: - Core layer: provides the capabilities of binding, initializing, and releasing devices. - Adaptation layer: implements driver-specific functions. ->![](../public_sys-resources/icon-note.gif) **NOTE** -> -> The core layer can call the functions of the interface layer and uses a hook to call functions of the adaptation layer. In this way, the adaptation layer can indirectly call the functions of the interface layer, but the interface layer cannot call the functions of the adaptation layer. +![](../public_sys-resources/icon-note.gif)**NOTE**
The core layer can call the functions of the interface layer and uses the hook to call functions of the adaptation layer. In this way, the adaptation layer can indirectly call the functions of the interface layer, but the interface layer cannot call the functions of the adaptation layer. **Figure 1** Unified service mode @@ -50,7 +49,7 @@ The DAC module is divided into the following layers: ### Constraints -Currently, the DAC module supports only the kernels (LiteOS) of mini and small systems. +The DAC module supports only the kernel (LiteOS-A) for mini and small systems. ## Usage Guidelines @@ -60,11 +59,11 @@ The DAC module converts digital signals into analog signals in the form of curre ### Available APIs -The table below describes the APIs of the DAC module. For more details, see API Reference. +The following table describes the APIs of the DAC module. For more information about the APIs, see **//drivers/hdf_core/framework/include/platform/dac_if.h**. **Table 1** DAC driver APIs -| API | Description | +| API | Description | | ------------------------------------------------------------------ | ------------ | | DevHandle DacOpen(uint32_t number) | Opens a DAC device. | | void DacClose(DevHandle handle) | Closes a DAC device. | @@ -72,12 +71,11 @@ The table below describes the APIs of the DAC module. For more details, see API ### How to Develop -The figure below shows the general development process. +The following figure illustrates how to use DAC APIs. **Figure 2** Process of using DAC APIs - -![](figures/using-DAC-process.png) +![Process of using a DAC](figures/using-DAC-process.png "Process of using a DAC") #### Opening a DAC Device @@ -93,13 +91,13 @@ DevHandle DacOpen(uint32_t number); | --------- | ---------------- | | number | DAC device number. | | **Return Value**| **Description** | -| NULL | The operation failed. | +| NULL | The operation fails. | | Device handle | The operation is successful. The handle of the DAC device opened is returned.| -Example: Open device 1 of the two DAC devices (numbered 0 and 1) in the system. +Open device 1 of the two DAC devices (numbered 0 and 1) in the system. ```c++ -DevHandle dacHandle = NULL; /* DAC device handle / +DevHandle dacHandle = NULL; // DAC device handle. /* Open DAC device 1. */ dacHandle = DacOpen(1); @@ -109,7 +107,7 @@ if (dacHandle == NULL) { } ``` -#### Setting a Target DA Value +#### Setting a DA Value ```c++ int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val); @@ -124,16 +122,16 @@ int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val); | val | DA value to set. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | ```c++ /* Write the target DA value through the DAC_CHANNEL_NUM channel. */ - ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret); - DacClose(dacHandle); - return -1; - } +ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); +if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret); + DacClose(dacHandle); + return -1; +} ``` #### Closing a DAC Device @@ -168,8 +166,8 @@ The procedure is as follows: You can obtain the operation result by printing the log information based on the **val**. ```c++ -#include "dac_if.h" /* Header file for DAC APIs */ -#include "hdf_log.h" /* Header file for log APIs */ +#include "dac_if.h" /* Header file for DAC APIs. */ +#include "hdf_log.h" /* Header file for log APIs. */ /* Define device 0, channel 1. */ #define DAC_DEVICE_NUM 0 diff --git a/en/device-dev/driver/driver-platform-dac-develop.md b/en/device-dev/driver/driver-platform-dac-develop.md index 61ddbd1caf1b3d187ee9bcf110c2597c819e9fa6..f033d06226f7785f50cdf32908a62a6ce23679c4 100644 --- a/en/device-dev/driver/driver-platform-dac-develop.md +++ b/en/device-dev/driver/driver-platform-dac-develop.md @@ -6,7 +6,10 @@ A digit-to-analog converter (DAC) is a device that converts a digital signal into an analog signal in electronics. -The DAC module supports development of digital-to-analog conversion. The DAC module provides the output channel for the process control computer system. It connects to the executor to implement automatic control of the production process. It is also an important module in the analog-to-digital converter using feedback technologies. +The DAC module supports development of digital-to-analog conversion. The DAC devices can be used to: + +- Provide the output channel for the process control computer system and connect to the executor to implement automatic control of the production process. +- Serve as an important module in the analog-to-digital converter using feedback technologies. ### Basic Concepts @@ -28,54 +31,96 @@ The DAC module supports development of digital-to-analog conversion. The DAC mod ### Working Principles -In the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a device service is used as the DAC manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The figure below shows the unified service mode. +In the Hardware Driver Foundation (HDF), the DAC module uses the unified service mode for API adaptation. In this mode, a device service is used as the DAC manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The DAC module uses the unified service mode, as shown in Figure 1. The DAC module is divided into the following layers: - -- Interface layer: provides APIs for opening or closing a device and writing data. +- Interface layer: provides the capabilities of opening and closing a device and writing data. - Core layer: provides the capabilities of binding, initializing, and releasing devices. -- Adaptation layer: implements other functions. +- Adaptation layer: implements hardware-related functions, such as controller initialization. -![](../public_sys-resources/icon-note.gif) **NOTE**
The core layer can call the APIs of the interface layer and uses hooks to call APIs of the adaptation layer. In this way, the adaptation layer can indirectly call the APIs of the interface layer, but the interface layer cannot call the APIs of the adaptation layer. +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. -**Figure 1** Unified service mode +>![](../public_sys-resources/icon-note.gif) **NOTE**
The core layer can call the APIs of the interface layer and uses hooks to call APIs of the adaptation layer. In this way, the adaptation layer can indirectly call the APIs of the interface layer, but the interface layer cannot call the APIs of the adaptation layer. + +**Figure 1** Unified service mode ![](figures/unified-service-mode.png "DAC unified service mode") ### Constraints -Currently, the DAC module supports only the kernels (LiteOS) of mini and small systems. +The DAC module supports only the kernel (LiteOS-A) for mini and small systems. ## Development Guidelines ### When to Use -The DAC module is used for digital-to-analog conversion, audio output, and motor control. The DAC driver is used when the digital signals input by the DAC module are converted into analog signals to output. +The DAC module is used for digital-to-analog conversion, audio output, and motor control. The DAC driver is used when the digital signals input by the DAC module are converted into analog signals to output. Before using DAC devices with OpenHarmony, you need to adapt the DAC driver to OpenHarmony. The following describes how to do it. ### Available APIs -The **DacMethod** structure is used to call the DAC driver APIs. +To enable the upper layer to successfully operate the hardware by calling the DAC APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/dac/dac_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. -**DacMethod**: +Definitions of **DacMethod** and **DacLockMethod**: ```c++ struct DacMethod { - // Hook used to write data. + /* Hook used to write data. */ int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val); - // Hook used to start a DAC device. + /* Hook used to start a DAC device. */ int32_t (*start)(struct DacDevice *device); - // Hook used to stop a DAC device + /* Hook used to stop a DAC device. */ int32_t (*stop)(struct DacDevice *device); }; + +struct DacLockMethod { + int32_t (*lock)(struct DacDevice *device); + void (*unlock)(struct DacDevice *device); +}; +``` +At the adaptation layer, **DacMethod** must be implemented, and **DacLockMethod** can be implemented based on service requirements. The core layer provides the default **DacLockMethod**, in which a spinlock is used to protect the critical section. + +```c +static int32_t DacDeviceLockDefault(struct DacDevice *device) +{ + if (device == NULL) { + HDF_LOGE("%s: device is null", __func__); + return HDF_ERR_INVALID_OBJECT; + } + return OsalSpinLock(&device->spin); +} + +static void DacDeviceUnlockDefault(struct DacDevice *device) +{ + if (device == NULL) { + HDF_LOGE("%s: device is null", __func__); + return; + } + (void)OsalSpinUnlock(&device->spin); +} + +static const struct DacLockMethod g_dacLockOpsDefault = { + .lock = DacDeviceLockDefault, + .unlock = DacDeviceUnlockDefault, +}; ``` -**Table 1** Description of the DacMethod structure +If spinlock cannot be used, you can use another type of lock to implement **DacLockMethod**. The implemented **DacLockMethod** will replace the default **DacLockMethod**. + +**Table 1** Hook functions in **DacMethod** | Function| Input Parameter | Output Parameter| Return Value | Description | | -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- | | write | **device**: structure pointer to the DAC controller at the core layer.
**channel**: channel ID, which is of the uint32_t type.
**val**: data to write, which is of the uint32_t type.| - | HDF_STATUS| Writes the target digit-to-analog (DA) value.| | start | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Starts a DAC device. | -| stop | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Stops a DAC device. | +| stop | **device**: structure pointer to the DAC controller at the core layer. | - | HDF_STATUS| Stops a DAC device. | + +**Table 2** Functions in **DacLockMethod** + +| Function| Input Parameter| Output Parameter| Return Value| Description| +| -------- | -------- | -------- | -------- | -------- | +| lock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Acquires the critical section lock.| +| unlock | **device**: structure pointer to the DAC device object at the core layer.| -| HDF_STATUS| Releases the critical section lock.| + ### How to Develop @@ -86,31 +131,31 @@ The DAC module adaptation procedure is as follows: 3. Instantiate the core layer APIs. 4. Debug the driver. -### Development Example +### Example -The following presents the information required for implementing device functions. +The following uses the Hi3516D V300 driver **//device/soc/hisilicon/common/platform/dac/dac_hi35xx.c** as an example to describe how to perform the DAC driver adaptation. -1. Instantiate the driver entry. +1. Instantiate the driver entry. + + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the module name must be the same as that in **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + + Generally, the HDF calls **Init()** to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Init()** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - ```c++ static struct HdfDriverEntry g_dacDriverEntry = { .moduleVersion = 1, .Init = VirtualDacInit, .Release = VirtualDacRelease, .moduleName = "virtual_dac_driver",// (Mandatory) The value must be the same as that in the .hcs file. - }; - HDF_INIT(g_dacDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + }; + HDF_INIT(g_dacDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. ``` - + 2. Configure attribute files. - - Add the device node description to the **vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + - Add the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. - The device attribute values are closely related to the driver implementation and the default values or restriction ranges of the **DacDevice** members at the core layer, for example, the number of device channels and the maximum transmission speed. + The device attribute values are closely related to the driver implementation and the default values or value ranges of the **DacDevice** members at the core layer, for example, the number of device channels and the maximum transmission speed. In the unified service mode, the first device node in the **device_info.hcs** file must be the DAC manager. The parameters must be set as follows: @@ -123,14 +168,14 @@ The following presents the information required for implementing device function | serviceName | **HDF_PLATFORM_DAC_MANAGER** | | deviceMatchAttr | Reserved. | - Configure DAC controller information from the second node. This node specifies a type of DAC controllers rather than a specific DAC controller. In this example, there is only one DAC device. If there are multiple DAC devices, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **dac_config** file. + Configure DAC controller information from the second node. This node specifies a type of DAC controllers rather than a specific DAC controller. In this example, there is only one DAC device. If there are multiple DAC devices, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **dac_config.hcs** file for each device. - **device_info.hcs** configuration example: + **device_info.hcs** example: ```hcs root { device_dac :: device { - // device0 is a DAC manager. + /* device0 is the DAC manager. */ device0 :: deviceNode { policy = 0; priority = 52; @@ -139,21 +184,23 @@ The following presents the information required for implementing device function moduleName = "HDF_PLATFORM_DAC_MANAGER"; } } - // dac_virtual is a DAC controller. + /* dac_virtual is a DAC controller. */ dac_virtual :: deviceNode { policy = 0; priority = 56; permission = 0644; moduleName = "virtual_dac_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. serviceName = "VIRTUAL_DAC_DRIVER"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "virtual_dac"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in dac_config.hcs. + deviceMatchAttr = "virtual_dac"; // (Mandatory) Controller private data, which must be same as that of the controller in dac_config.hcs. } } ``` - Configure the **dac_test_config.hcs** file. - Add a file, for example, **vendor/vendor_hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** and configure driver parameters. + Add a file to the directory of a product to configure driver parameters. For example, add the **vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs** file for the hispark_taurus development board. + + The configuration parameters are as follows: ```hcs root { @@ -162,304 +209,352 @@ The following presents the information required for implementing device function match_attr = "virtual_dac"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. template dac_device { deviceNum = 0; // Device number. - validChannel = 0x1; // Valid channel 1. - rate = 20000; // Transmission speed. + validChannel = 0x1; // Valid channel 1. + rate = 20000; // Transmission speed. } device_0 :: dac_device { deviceNum = 0; // Device number. - validChannel = 0x2; // Valid channel 2. + validChannel = 0x2; // Valid channel 2. } } } } ``` -3. Instantiate the core layer APIs. - - - Initializing the **DacDevice** object - - Initialize the **DacDevice** member in the **VirtualDacParseAndInit** function. + After the **dac_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + For example, if the path of **dac_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs**, add the following statement to **hdf.hcs** of the product: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs" // Relative path of the file. + ``` + +3. Instantiate the core layer APIs. + + - Initialize the **DacDevice** object. + + Initialize **DacDevice** in the **VirtualDacParseAndInit** function. ```c++ - // Custom structure of the virtual driver + /* Custom structure of the virtual driver. */ struct VirtualDacDevice { - // DAC device structure + /* DAC device structure. */ struct DacDevice device; - // DAC device number + /* DAC device number. */ uint32_t deviceNum; - // Valid channel + /* Valid channel. */ uint32_t validChannel; - // DAC rate + /* DAC rate. */ uint32_t rate; }; - // Parse and initialize the **DacDevice** object of the core layer. + /* Parse and initialize the **DacDevice** object of the core layer. */ static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) { - // Define the return values. + /* Define the return values. */ int32_t ret; - // Pointer to the virtual DAC device + /* Pointer to the virtual DAC device. */ struct VirtualDacDevice *virtual = NULL; (void)device; - // Allocate space for this pointer. + /* Allocate space for this pointer. */ virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); if (virtual == NULL) { - // If the value is null, return an error code. + /* If the value is null, return an error code. */ HDF_LOGE("%s: Malloc virtual fail!", __func__); return HDF_ERR_MALLOC_FAIL; } - // Read the content of the attribute file. + /* Read the attribute file. */ ret = VirtualDacReadDrs(virtual, node); if (ret != HDF_SUCCESS) { - // Failed to read the file. + /* Failed to read the file. */ HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - // Release the space for the virtual DAC device. + /* Release the space for the virtual DAC device. */ OsalMemFree(virtual); - // Set the pointer to 0. + /* Set the pointer to 0. */ virtual = NULL; return ret; } - // Initialize the pointer to the virtual DAC device. + /* Initialize the pointer to the virtual DAC device. */ VirtualDacDeviceInit(virtual); - // Initialize the priv object in DacDevice. + /* Initialize the priv object in DacDevice. */ virtual->device.priv = (void *)node; - // Initialize the devNum object in DacDevice. + /* Initialize the devNum object in DacDevice. */ virtual->device.devNum = virtual->deviceNum; - // Initialize the ops object in DacDevice. + /* Initialize the ops object in DacDevice. */ virtual->device.ops = &g_method; - // Add a DAC device. + /* Add a DAC device. */ ret = DacDeviceAdd(&virtual->device); if (ret != HDF_SUCCESS) { - // Failed to add the device. + /* Failed to add the device. */ HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); - // Release the space for the virtual DAC device. + /* Release the space for the virtual DAC device. */ OsalMemFree(virtual); - // Set this pointer to null. + /* Set this pointer to null. */ virtual = NULL; return ret; } return HDF_SUCCESS; - } - ``` - - - Defining a custom structure - - The custom structure holds parameters and data for the driver. Define the custom structure based on the function parameters of the device. The **DacTestReadConfig()** provided by the HDF reads the values in the **dac_config.hcs** file, and **DeviceResourceIface()** initializes the custom structure and passes some important parameters, such as the device number and bus number, to the **DacDevice** object at the core layer. - - ```c++ - struct VirtualDacDevice { - struct DacDevice device;// (Mandatory) Control object at the core layer. For details, see the description below. - uint32_t deviceNum; // (Mandatory) Device number. - uint32_t validChannel; // (Mandatory) Valid channel. - uint32_t rate; // (Mandatory) Sampling rate. - }; - - // DacDevice is the core layer controller structure. The Init() function assigns values to the members of DacDevice. - struct DacDevice { - const struct DacMethod *ops; - OsalSpinlock spin; // Spinlock. - uint32_t devNum; // Device number. - uint32_t chanNum; // Device channel number. - const struct DacLockMethod *lockOps; - void *priv; - }; - ``` - - - Instantiating **DacDevice** in **DacMethod**. - - The **VirtualDacWrite**, **VirtualDacStop**, and **VirtualDacStart** functions are instantiated in **dac_virtual.c**. + } + ``` + + + + - Define a custom structure. + + The custom structure holds parameters and data for the driver. Define the custom structure based on the function parameters of the device. The **DacTestReadConfig()** provided by the HDF reads the values in the **dac_config.hcs** file, and **DeviceResourceIface()** initializes the custom structure and passes some important parameters, such as the device number and bus number, to the **DacDevice** object at the core layer. + + ```c++ + struct VirtualDacDevice { + struct DacDevice device;// (Mandatory) Control object at the core layer. For details, see the description below. + uint32_t deviceNum; // (Mandatory) Device number. + uint32_t validChannel; // (Mandatory) Valid channel. + uint32_t rate; // (Mandatory) Sampling rate. + }; + + /* DacDevice is the core layer controller structure. The Init() function assigns values to the members of DacDevice. */ + struct DacDevice { + const struct DacMethod *ops; + OsalSpinlock spin; // Spinlock. + uint32_t devNum; // Device number. + uint32_t chanNum; // Device channel number. + const struct DacLockMethod *lockOps; + void *priv; + }; + ``` + + + - Instantiate **DacDevice** in **DacMethod**. + + The **VirtualDacWrite**, **VirtualDacStop**, and **VirtualDacStart** functions are instantiated in **dac_virtual.c**. + ```c++ static const struct DacMethod g_method = { .write = VirtualDacWrite, // Write data to a DAC device. - .stop = VirtualDacStop, // Stop a DAC device. + .stop = VirtualDacStop, // Stop a DAC device. .start = VirtualDacStart, // Start a DAC device. }; ``` - - ![](../public_sys-resources/icon-note.gif) **NOTE**
- For details about **DacMethod**, see [Available APIs](#available-apis). - - - **Init** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. - - | Status | Description | - | ---------------------- | ------------- | - | HDF_ERR_INVALID_OBJECT | Invalid controller object.| - | HDF_ERR_INVALID_PARAM | Invalid parameter. | - | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | - | HDF_ERR_IO | I/O error. | - | HDF_SUCCESS | Transmission successful. | - | HDF_FAILURE | Transmission failed. | - - **Function description**: - - Initializes the custom structure object and **DacDevice**, and calls the **AdcDeviceAdd** function at the core layer. - - ```c++ - static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) - { - // Define return value parameters. - int32_t ret; - // Pointer to the DAC device. - struct VirtualDacDevice *virtual = NULL; - (void)device; - // Allocate memory of the specified size. - virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); - if (virtual == NULL) { - // Failed to allocate memory. - HDF_LOGE("%s: Malloc virtual fail!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - // Read the node parameters in the .hcs file. - ret = VirtualDacReadDrs(virtual, node); - if (ret != HDF_SUCCESS) { - // Failed to read the node data. - HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - goto __ERR__; - } - // Initialize the DAC device pointer. - VirtualDacDeviceInit(virtual); - // Pass private node data. - virtual->device.priv = (void *)node; - // Pass the device number. - virtual->device.devNum = virtual->deviceNum; - // Pass the method. - virtual->device.ops = &g_method; - // Add a DAC device. - ret = DacDeviceAdd(&virtual->device); - if (ret != HDF_SUCCESS) { - // Failed to add the DAC device. - HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); - goto __ERR__; - } - // The DAC device is added. - return HDF_SUCCESS; - __ERR__: - // If the pointer is null - if (virtual != NULL) { - // Release the memory. - OsalMemFree(virtual); - // Set this pointer to null. - virtual = NULL; - } - - return ret; - } - - static int32_t VirtualDacInit(struct HdfDeviceObject *device) - { - // Define return value parameters. - int32_t ret; - // Child node of the device structure - const struct DeviceResourceNode *childNode = NULL; - // Check the input parameter pointer. - if (device == NULL || device->property == NULL) { - // The input parameter pointer is null. - HDF_LOGE("%s: device or property is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - // The input parameter pointer is not null. - ret = HDF_SUCCESS; - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - // Parse the child node. - ret = VirtualDacParseAndInit(device, childNode); - if (ret != HDF_SUCCESS) { - // Failed to parse the child node. - break; - } - } - // The child node is parsed. - return ret; - } - ``` + + >![](../public_sys-resources/icon-note.gif) **NOTE**
For details about **DacMethod**, see [Available APIs](#available-apis). + - - **Release** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - No value is returned. - - **Function description**: + + - Implement the **Init** function. + + Input parameter: + + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. + + Return value: + + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + | Status | Description | + | ---------------------- | -------------------------- | + | HDF_ERR_INVALID_OBJECT | Invalid controller object. | + | HDF_ERR_INVALID_PARAM | Invalid parameter. | + | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | + | HDF_ERR_IO | I/O error. | + | HDF_SUCCESS | Transmission successful. | + | HDF_FAILURE | Transmission failed. | + - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. - >![](../public_sys-resources/icon-note.gif) **NOTE** - > - >All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - + Function description: + + Initializes the custom structure object and **DacDevice**, and calls the **DacDeviceAdd** function at the core layer. + ```c++ - static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node) - { - // Define return value parameters. - int32_t ret; - // Define the DAC device number. - int16_t devNum; - // Pointer to the DacDevice structure. - struct DacDevice *device = NULL; - // Pointer to the VirtualDacDevice structure. - struct VirtualDacDevice *virtual = NULL; - // Pointer to the DeviceResourceIface structure. - struct DeviceResourceIface *drsOps = NULL; - // Obtain device resources through the instance entry. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - // Check the input parameter pointer. - if (drsOps == NULL || drsOps->GetUint32 == NULL) { - // The pointer is null. - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return; - } - // Obtain data of the devNum node. - ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0); - if (ret != HDF_SUCCESS) { - // Failed to obtain node data. - HDF_LOGE("%s: read devNum fail!", __func__); - return; - } - // Obtain the DAC device number. - device = DacDeviceGet(devNum); - // Check whether the DAC device number and data are null. - if (device != NULL && device->priv == node) { - // Release the DAC device number if the device data is null. - DacDevicePut(device); - // Remove the DAC device number. - DacDeviceRemove(device); - virtual = (struct VirtualDacDevice *)device; - // Release the virtual pointer. - OsalMemFree(virtual); + static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) + { + /* Define the return values. */ + int32_t ret; + /* Pointer to the VirtualDacDevice structure. */ + struct VirtualDacDevice *virtual = NULL; + (void)device; + /* Allocate memory of the specified size. */ + virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); + if (virtual == NULL) { + /* Failed to allocate memory. */ + HDF_LOGE("%s: Malloc virtual fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + /* Read the node parameters in the HCS. The function definition is as follows. */ + ret = VirtualDacReadDrs(virtual, node); + if (ret != HDF_SUCCESS) { + /* Failed to read the node data. */ + HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); + goto __ERR__; + } + /* Initialize the DAC device pointer. */ + VirtualDacDeviceInit(virtual); + /* Pass in the private data of the node. */ + virtual->device.priv = (void *)node; + /* Pass in the device number. */ + virtual->device.devNum = virtual->deviceNum; + /* Pass in the method. */ + virtual->device.ops = &g_method; + /* Add a DAC device. */ + ret = DacDeviceAdd(&virtual->device); + if (ret != HDF_SUCCESS) { + /* Failed to add the DAC device. */ + HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); + goto __ERR__; + } + /* The DAC device is added successfully. */ + return HDF_SUCCESS; + __ERR__: + /* If the pointer is null */ + if (virtual != NULL) { + / Release the memory. */ + OsalMemFree(virtual); + /* Set this pointer to null. */ + virtual = NULL; + } + + return ret; } - return; - } - - static void VirtualDacRelease(struct HdfDeviceObject *device) - { - // Define the child node structure pointer to the DeviceResourceNode. - const struct DeviceResourceNode *childNode = NULL; - // Check whether the input parameter pointer is null. - if (device == NULL || device->property == NULL) { - // The input parameter pointer is null. - HDF_LOGE("%s: device or property is NULL", __func__); - return; + + static int32_t VirtualDacInit(struct HdfDeviceObject *device) + { + /* Define return values. */ + int32_t ret; + /* Child node of the device structure. */ + const struct DeviceResourceNode *childNode = NULL; + /* Check the input parameter pointer. */ + if (device == NULL || device->property == NULL) { + /* The input parameter pointer is null. */ + HDF_LOGE("%s: device or property is NULL", __func__); + return HDF_ERR_INVALID_OBJECT; + } + /* The input parameter pointer is not null. */ + ret = HDF_SUCCESS; + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + /* Parse the child node. */ + ret = VirtualDacParseAndInit(device, childNode); + if (ret != HDF_SUCCESS) { + /* Failed to parse the child node. */ + break; + } + } + /* The child node is parsed. */ + return ret; } - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - // Remove the DAC through the node. - VirtualDacRemoveByNode(childNode); + static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node) + { + struct DeviceResourceIface *drsOps = NULL; + + /* Obtain the drsOps method. */ + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: Invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + /* Read the configuration parameters in sequence and fill them in the structure. */ + if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read deviceNum fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read validChannel fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read rate fail!", __func__); + return HDF_ERR_IO; + } + return HDF_SUCCESS; } - } ``` + + + - Implement the **Release** function. + + Input parameter: + + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. + + Return value: + + No value is returned. + + Function description: + + Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + + >![](../public_sys-resources/icon-note.gif) **NOTE**
+ > + >All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. + + + + ```c++ + static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node) + { + /* Define return values. */ + int32_t ret; + /*Define the DAC device number. */ + int16_t devNum; + /* Pointer to the DacDevice structure. */ + struct DacDevice *device = NULL; + // Pointer to the VirtualDacDevice structure. */ + struct VirtualDacDevice *virtual = NULL; + /* Pointer to the DeviceResourceIface structure. */ + struct DeviceResourceIface *drsOps = NULL; + /* Obtain device resources through the instance entry. */ + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + /* Check the input parameter pointer. */ + if (drsOps == NULL || drsOps->GetUint32 == NULL) { + /* The pointer is null. */ + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return; + } + /* Obtain data of the devNum node. */ + ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0); + if (ret != HDF_SUCCESS) { + /* The information fails to be obtained. */ + HDF_LOGE("%s: read devNum fail!", __func__); + return; + } + /* Obtain the DAC device number. */ + device = DacDeviceGet(devNum); + /* Check whether the DAC device number and data are null. */ + if (device != NULL && device->priv == node) { + /* Release the DAC device number if the device data is null. */ + DacDevicePut(device); + /* Remove the DAC device number. */ + DacDeviceRemove(device); + virtual = (struct VirtualDacDevice *)device; + /* Release the virtual pointer. */ + OsalMemFree(virtual); + } + return; + } + + static void VirtualDacRelease(struct HdfDeviceObject *device) + { + /* Define the child node structure pointer to the DeviceResourceNode. */ + const struct DeviceResourceNode *childNode = NULL; + /* Check whether the input parameter pointer is null. */ + if (device == NULL || device->property == NULL) { + /* The input parameter pointer is null. */ + HDF_LOGE("%s: device or property is NULL", __func__); + return; + } + + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + /* Remove the DAC by node. */ + VirtualDacRemoveByNode(childNode); + } + } + ``` + + + + 4. Debug the driver. (Optional) Verify the basic functions of the new driver, for example, check whether the test cases are successful after the driver is loaded. diff --git a/en/device-dev/driver/driver-platform-gpio-des.md b/en/device-dev/driver/driver-platform-gpio-des.md index 4368a41d463d17ea912690b837d31e15d7a76085..34d6279ffab201e88471dc0a043de62f0a0413c3 100644 --- a/en/device-dev/driver/driver-platform-gpio-des.md +++ b/en/device-dev/driver/driver-platform-gpio-des.md @@ -1,281 +1,368 @@ # GPIO - ## Overview +### Function + A general-purpose input/output (GPIO) controller manages all GPIO pins by group. Each group of GPIO pins is associated with one or more registers. The GPIO controller manages the pins by reading data from and writing data to the registers. -The GPIO APIs define a set of standard functions for performing operations on GPIO pins, including: +The GPIO module provides APIs for performing operations on GPIO pins, including: - Setting the pin direction, which can be input or output (high impedance is not supported currently) - - Reading and writing the pin level, which can be low or high +- Setting an interrupt service routine (ISR) function and interrupt trigger mode for a pin +- Enabling or disabling interrupts for a pin + +### Basic Concepts + +A GPIO can be used as an input, an output, or both, and is controllable by software. + +- GPIO input -- Setting the interrupt service routine (ISR) function and interrupt trigger mode for a pin + When a GPIO is used as an input, it reads the level state (high or low) of each pin. Common input modes include analog input, floating input, pull-up input, and pull-down input. -- Enabling or disabling pin interrupts +- GPIO output + When a GPIO is used as an output, it sets the pin level. Common output modes include open-drain output, push-pull output, multiplexed open-drain output, and multiplexed push-pull output. -## Available APIs +### Working Principles - **Table 1** GPIO driver APIs +In the Hardware Driver Foundation (HDF), the GPIO module uses the unified service mode for API adaptation. In this mode, a device service is used as the GPIO manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. -| Category| Description| -| -------- | -------- | -| GPIO read/write| - **GpioRead**: reads the pin level.
- **GpioWrite**: writes the pin level.| -| GPIO settings| - **GpioSetDir**: sets the pin direction.
- **GpioGetDir**: obtains the pin direction.| -| GPIO interrupt settings| - **GpioSetIrq**: sets the ISR function for a GPIO pin.
- **GpioUnsetIrq**: cancels the ISR function setting for a GPIO pin.
- **GpioEnableIrq**: enables interrupts for a pin.
- **GpioDisableIrq**: disables interrupts for a pin.| +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in kernel mode. +The GPIO module is divided into the following layers: +- Interface layer: provides APIs for operating GPIO pins. +- Core layer: provides the capabilities of adding and removing the GPIO controller and managing GPIO pins. This layer interacts with the adaptation layer through hook functions to allow the GPIO chip drivers of different vendors to quickly access the HDF. +- Adaptation layer: instantiates hook functions to implement specific features. + +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) ## Usage Guidelines +### When to Use -### How to Use +As a concept at the software layer, GPIO is used to manage GPIO pin resources. You can use the GPIO APIs to control pins. -The figure below shows the general GPIO development process. In the APIs, a GPIO pin is specified by the pin number. +### Available APIs -**Figure 1** Using GPIO driver APIs +The following table describes the APIs provided by the GPIO module. -![](figures/using-GPIO-process.png "using-gpio-process.png") +**Table 1** GPIO driver APIs +| API | Description | +| ------------------------------------------------------------ | ------------------------------ | +| GpioGetByName(const char *gpioName) | Obtains the GPIO pin number. | +| int32_t GpioRead(uint16_t gpio, uint16_t *val) | Reads the level of a GPIO pin. | +| int32_t GpioWrite(uint16_t gpio, uint16_t val) | Writes the level of a GPIO pin. | +| int32_t GpioGetDir(uint16_t gpio, uint16_t *dir) | Obtains the direction of a GPIO pin. | +| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | Sets the direction for a GPIO pin. | +| int32_t GpioUnsetIrq(uint16_t gpio, void *arg); | Cancels the ISR function for a GPIO pin. | +| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg) | Sets an ISR function for a GPIO pin. | +| int32_t GpioEnableIrq(uint16_t gpio) | Enables interrupts for a GPIO pin. | +| int32_t GpioDisableIrq(uint16_t gpio) | Disables interrupts for a GPIO pin. | -### Determining the GPIO Pin Number +>![](../public_sys-resources/icon-note.gif) **NOTE** +> +>All GPIO APIs described in this document can be used in kernel mode and user mode. -The method for determining the GPIO pin number varies depending on the GPIO controller model, parameters, and controller driver of the system on chip (SoC). +### How to Develop -- Hi3516D V300 - A controller manages 12 groups of GPIO pins. Each group contains 8 GPIO pins. The group number ranges from 0 to 11. +The fillowing figure shows how to use the GPIO APIs to manage pins. In the APIs, a GPIO pin is identified by the pin number. - GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group +**Figure 2** Using GPIO driver APIs - Example: +![](figures/using-GPIO-process.png) - GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 - -- Hi3518E V300 - A controller manages 10 groups of GPIO pins. Each group contains 10 GPIO pins. The group number ranges from 0 to 9. +#### Determining the GPIO Pin Number - GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group +You can determine the GPIO pin number in either of the following ways: - Example: - - GPIO pin number of GPIO7_3 = 7 x 10 + 3 = 73 +- Calculating the pin number based on the system on chip (SoC) + The method for determining the GPIO pin number varies depending on the GPIO controller model, parameters, and controller driver of the SoC. -### Using APIs to Operate GPIO Pins + - Hi3516D V300 -- Set the GPIO pin direction. - Before performing read/write operations on a GPIO pin, call **GpioSetDir()** to set the pin direction. + A controller manages 12 groups of GPIO pins. Each group contains 8 GPIO pins. The group number ranges from 0 to 11. - ```c - int32_t GpioSetDir(uint16_t gpio, uint16_t dir); - ``` + GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group - **Table 2** Description of GpioSetDir - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | dir | Direction to set.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| + Example: -- Read or write the pin level. + GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 - Call **GpioRead()** to read the level of a GPIO pin. + - Hi3518E V300 - ```c - int32_t GpioRead(uint16_t gpio, uint16_t *val); - ``` + A controller manages 10 groups of GPIO pins. Each group contains 10 GPIO pins. The group number ranges from 0 to 9. - **Table 3** Description of GpioRead - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | val | Pointer to the level to read.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| + GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group - Call **GpioWrite()** to write the level for a GPIO pin. + Example: + + GPIO pin number of GPIO7_3 = 7 x 10 + 3 = 73 + +- Obtaining the pin number based on the pin alias + + Use **GpioGetByName()** to obtain the pin number based on the pin alias. The global pin number is returned. ```c - int32_t GpioWrite(uint16_t gpio, uint16_t val); - ``` - - **Table 4** Description of GpioWrite - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | val | Level to write.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| - - Sample code: - - - ``` - int32_t ret; - uint16_t val; - /* Set the direction of GPIO pin 3 to output. */ - ret = GpioSetDir(3, GPIO_DIR_OUT); - if (ret != 0) { - HDF_LOGE("GpioSerDir: failed, ret %d\n", ret); - return; - } - /* Write the low level GPIO_VAL_LOW for GPIO pin 3. */ - ret = GpioWrite(3, GPIO_VAL_LOW); - if (ret != 0) { - HDF_LOGE("GpioWrite: failed, ret %d\n", ret); - return; - } - /* Set the direction of GPIO pin 6 to input. */ - ret = GpioSetDir(6, GPIO_DIR_IN); - if (ret != 0) { - HDF_LOGE("GpioSetDir: failed, ret %d\n", ret); - return; - } - /* Read the level of GPIO pin 6. */ - ret = GpioRead(6, &val); + GpioGetByName(const char *gpioName); ``` -- Set the ISR function for a GPIO pin. +#### Setting the GPIO Pin Direction - Call **GpioSetIrq()** to set the ISR function for a GPIO pin. +Before performing read/write operations on a GPIO pin, use **GpioSetDir()** to set the pin direction. - ```c - int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); - ``` - - **Table 5** Description of GpioSetIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | mode | Interrupt trigger mode.| - | func | ISR function to set.| - | arg | Pointer to the parameters passed to the ISR function.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| - - > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
- > Only one ISR function can be set for a GPIO pin. If **GpioSetIrq** is called repeatedly, the previous IRS function will be replaced. - - If the ISR function is no longer required, call **GpioUnsetIrq()** to cancel it. +```c +int32_t GpioSetDir(uint16_t gpio, uint16_t dir); +``` - ```c - int32_t GpioUnsetIrq(uint16_t gpio, void *arg); - ``` +**Table 2** Description of GpioSetDir - **Table 6** Description of GpioUnsetIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | arg | Pointer to the GPIO interrupt parameters.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| dir | Direction to set. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | - After the ISR function is set, call **GpioEnableIrq()** to enable interrupts for the GPIO pin. +Example: Set the direction of GPIO pin 3 to output. - ```c - int32_t GpioEnableIrq(uint16_t gpio); - ``` +```c +int32_t ret; - **Table 7** Description of GpioEnableIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +ret = GpioSetDir(3, GPIO_DIR_OUT); // Set GPIO pin 3 as an output. +if (ret != 0) { + HDF_LOGE("GpioSerDir: failed, ret %d\n", ret); + return ret; +} +``` - > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
- > The configured ISR function can be responded only after interrupts are enabled for the GPIO pin. +#### Obtaining the GPIO Pin Direction - You can call **GpioDisableIrq** to disable interrupts for the pin. +Use **GpioGetDir()** to obtain the GPIO pin direction. - ```c - int32_t GpioDisableIrq(uint16_t gpio); - ``` +```c +int32_t GpioGetDir(uint16_t gpio, uint16_t *dir); +``` - **Table 8** Description of GpioDisableIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +**Table 3** Description of GpioGetDir - Sample code: +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| dir | Pointer to the direction value obtained. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | - - ``` - /* Set the ISR function. */ - int32_t MyCallBackFunc(uint16_t gpio, void *data) - { - HDF_LOGI("%s: gpio:%u interrupt service in! data=%p\n", __func__, gpio, data); - return 0; - } - - int32_t ret; - /* Set the ISR function to MyCallBackFunc, with input parameter of NULL and the interrupt trigger mode of rising edge. */ - ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); - if (ret != 0) { - HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); - return; - } - - /* Enable interrupts for GPIO pin 3. */ - ret = GpioEnableIrq(3); - if (ret != 0) { - HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); - return; - } - - /* Disable interrupts for GPIO pin 3. */ - ret = GpioDisableIrq(3); - if (ret != 0) { - HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); - return; - } - - /* Cancel the ISR function setting for GPIO pin 3. */ - ret = GpioUnsetIrq(3, NULL); - if (ret != 0) { - HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); - return; - } - ``` +Example: Obtain the direction of GPIO pin 3. +```c +int32_t ret; +uin16_t dir; -## Example +ret = GpioGetDir(3, &dir); // Obtain the direction of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioGetDir: failed, ret %d\n", ret); + return ret; +} +``` -The procedure is as follows: +#### Reading the GPIO Pin Level -1. Select an idle GPIO pin, for example, pin GPIO10\_3 on a Hi3516D V300 development board. +Use **GpioRead()** to read the level of a GPIO pin. - The pin number is 83. +```c +int32_t GpioRead(uint16_t gpio, uint16_t *val); +``` - GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 +**Table 4** Description of GpioRead - You can select an idle GPIO pin as required. +| **Parameter** | **Description** | +| ---------- | -------------------- | +| gpio | GPIO pin number. | +| val | Pointer to the level value read. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | -2. Set the ISR function for the pin, with the trigger mode of rising edge and failing edge. - -3. Write high and low levels to the pin alternately, and observe the execution of the ISR function. +Example: Read the level of GPIO pin 3. + +```c +int32_t ret; +uint16_t val; + +ret = GpioRead(3, &val); // Read the level of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioRead: failed, ret %d\n", ret); + return ret; +} +``` + +#### Writing the GPIO Pin Level + +Use **GpioWrite()** to write the level for a GPIO pin. + +```c +int32_t GpioWrite(uint16_t gpio, uint16_t val); +``` + +**Table 5** Description of GpioWrite + +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| val | Level to write. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +Example: Write a low level value to the register of GPIO pin 3. + +```c +int32_t ret; + +ret = GpioWrite(3, GPIO_VAL_LOW); // Write a low level value to the register of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioRead: failed, ret %d\n", ret); + return ret; +} ``` + +#### Setting an ISR Function for a GPIO Pin + +Use **GpioSetIrq()** to set an ISR function for a GPIO pin. + +```c +int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); +``` + +**Table 6** Description of GpioSetIrq + +| **Parameter** | **Description** | +| ---------- | ------------------------ | +| gpio | GPIO pin number. | +| mode | Interrupt trigger mode. | +| func | ISR function to set. | +| arg | Pointer to the parameters passed to the ISR function.| +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> Only one ISR function can be set for a GPIO pin. If **GpioSetIrq** is called repeatedly, the previous IRS function will be replaced. + +#### Canceling the ISR Function for a GPIO Pin + +If the ISR function is no longer required, call **GpioUnsetIrq()** to cancel it. + +```c +int32_t GpioUnsetIrq(uint16_t gpio, void *arg); +``` + +**Table 7** Description of GpioUnsetIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| arg | Pointer to the GPIO interrupt parameters. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +#### Enabling Interrupts for a GPIO Pin + +After the ISR function is set, call **GpioEnableIrq()** to enable interrupts for the GPIO pin. + +```c +int32_t GpioEnableIrq(uint16_t gpio); +``` + +**Table 8** Description of GpioEnableIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> The configured ISR function can be responded only after interrupts are enabled for the GPIO pin. + +#### Disabling Interrupts for a GPIO Pin + +Use **GpioDisableIrq()** to disable interrupts for a pin. + +```c +int32_t GpioDisableIrq(uint16_t gpio); +``` +**Table 9** Description of GpioDisableIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +Example: + +```c +/* Set an ISR function. */ +int32_t MyCallBackFunc(uint16_t gpio, void *data) +{ + HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio); + return 0; +} + +int32_t ret; +/* Set the ISR function to MyCallBackFunc, with input parameter of NULL and the interrupt trigger mode of rising edge. */ +ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); +if (ret != 0) { + HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); + return ret; +} + +/* Enable interrupts for GPIO pin 3. */ +ret = GpioEnableIrq(3); +if (ret != 0) { + HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); + return ret; +} + +/* Disable interrupts for GPIO pin 3. */ +ret = GpioDisableIrq(3); +if (ret != 0) { + HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); + return ret; +} + +/* Cancel the ISR function for GPIO pin 3. */ +ret = GpioUnsetIrq(3, NULL); +if (ret != 0) { + HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); + return ret; +} +``` + +## Example + +The following example shows how to trigger an interrupt for a GPIO pin. The procedure is as follows: + +1. Select an idle GPIO pin, for example, pin GPIO10_3 on a Hi3516D V300 development board. The pin number of GPIO10_3 is 83. You can select an idle GPIO pin as required. +2. Set an ISR function for the pin, with the interrupt trigger mode of rising edge and falling edge. +3. Write high and low levels to the pin alternately, and observe the execution of the ISR function. + +Sample code: + +```c #include "gpio_if.h" #include "hdf_log.h" #include "osal_irq.h" @@ -286,7 +373,7 @@ static uint32_t g_irqCnt; /* ISR function */ static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data) { - HDF_LOGE("%s: irq triggered! on gpio:%u, data=%p", __func__, gpio, data); + HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio); g_irqCnt++; /* If the ISR function is triggered, the global interrupt counter is incremented by 1. */ return GpioDisableIrq(gpio); } diff --git a/en/device-dev/driver/driver-platform-gpio-develop.md b/en/device-dev/driver/driver-platform-gpio-develop.md index 4e8b93552c64ff7faae7ee814c0e2cb0c23776b2..76077fc64d1415b51616150c50d6383ebc082fe8 100644 --- a/en/device-dev/driver/driver-platform-gpio-develop.md +++ b/en/device-dev/driver/driver-platform-gpio-develop.md @@ -1,216 +1,255 @@ # GPIO - ## Overview -In the Hardware Driver Foundation (HDF), the general-purpose input/output (GPIO) module uses the service-free mode for API adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the operating system (OS) that does not distinguish the user mode and the kernel mode. In the service-free mode, **DevHandle** (a void pointer) directly points to the kernel-mode address of the device object. +### Function - **Figure 1** Service-free mode +A general-purpose input/output (GPIO) controller manages all GPIO pins by group. Each group of GPIO pins is associated with one or more registers. The GPIO controller manages the pins by reading data from and writing data to the registers. - ![](figures/service-free-mode.png "service-free-mode") +### Basic Concepts +A GPIO can be used as an input, an output, or both, and is controllable by software. -## Available APIs +- GPIO input -**GpioMethod**: + When a GPIO is used as an input, it reads the level state (high or low) of each pin. Common input modes include analog input, floating input, pull-up input, and pull-down input. +- GPIO output -``` + When a GPIO is used as an output, it sets the pin level. Common output modes include open-drain output, push-pull output, multiplexed open-drain output, and multiplexed push-pull output. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the GPIO module uses the unified service mode for API adaptation. In this mode, a device service is used as the GPIO manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. + +The GPIO module is divided into the following layers: + +- Interface layer: provides APIs for operating GPIO pins. +- Core layer: provides the capabilities of adding and removing a GPIO controller and managing GPIO pins. This layer interacts with the adaptation layer through hook functions to allow the GPIO chip drivers of different vendors to quickly access the HDF. +- Adaptation layer: instantiates hook functions to implement specific features. + +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) + +## Development Guidelines + +### When to Use + +As a concept at the software layer, GPIO is used to manage GPIO pin resources. You can use the APIs provided by the GPIO module to control pins. Before using your GPIO driver with OpenHarmony, you need to perform GPIO driver adaptation. The following sections describe how to adapt the GPIO driver. + +### Available APIs + +To enable the upper layer to successfully operate GPIO pins by calling the GPIO APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/gpio/gpio_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**GpioMethod**: + +```c struct GpioMethod { - int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);// Reserved - int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);// Reserved - int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val); - int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val); - int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); - int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir); - int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq);// Reserved - int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg); - int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local); - int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); - int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local); // Reserved. + int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local); // Reserved. + int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val); + int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val); + int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); + int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir); + int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq); // Reserved. + int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg); + int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); } ``` - **Table 1** Description of the callback functions in GpioMethod +**Table 1** Hook functions in **GpioMethod** | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| write | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**val**: input level, which is of the uint16_t type.| –| HDF_STATUS| Writes the level of a GPIO pin.| -| read | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **val**: pointer to the output level, which is of the uint16_t type.| HDF_STATUS| Reads the level of a GPIO pin.| +| write | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**val**: level value to write, which is of the uint16_t type.| –| HDF_STATUS| Writes the level for a GPIO pin.| +| read | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **val**: level value read, which is of the uint16_t type.| HDF_STATUS| Reads the level of a GPIO pin.| | setDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**dir**: pin direction to set, which is of the uint16_t type.| –| HDF_STATUS| Sets the direction (input or output) for a GPIO pin.| -| getDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **dir**: pointer to the pin direction obtained, which is of the uint16_t type.| HDF_STATUS| Obtains the input or output direction of a GPIO pin.| -| setIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**mode**: trigger mode, which can be edge or level of the uint16_t type.
**func**: pointer to the interrupt request (IRQ) handler.
**arg**: void pointer to the input parameters of the IRQ handler.| –| HDF_STATUS| Sets an IRQ for a GPIO pin.| -| unsetIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Cancels the IRQ settings for a GPIO pin.| +| getDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **dir**: pin direction read, which is of the uint16_t type.| HDF_STATUS| Obtains the input or output direction of a GPIO pin.| +| setIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**mode**: interrupt trigger mode, which can be edge or level. The value is of the uint16_t type.
**func**: pointer to the interrupt request (IRQ) handler.
**arg**: void pointer to the input parameters of the IRQ handler.| –| HDF_STATUS| Sets an IRQ function for a GPIO pin.| +| unsetIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Cancels the IRQ function for a GPIO pin.| | enableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Enables interrupts for a GPIO pin.| | disableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Disables interrupts for a GPIO pin.| +### How to Develop -## How to Develop - -The GPIO controller manages all pins by group. The related parameters are defined in attribute files. You need to instantiate the driver entry and APIs for the vendor driver to access the HDF. - -The GPIO module adaptation involves the following steps: +The GPIO module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **gpio_config.hcs** file. - 3. Instantiate the GPIO controller object. - - Initialize **GpioCntlr**. - - Instantiate **GpioMethod** in the **GpioCntlr** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **GpioMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - - (Optional) For new drivers, verify the basic functions, such as the GPIO status control and response to interrupts. +### Example -## Development Example +The following uses the **//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the driver adaptation. -The following uses **gpio_hi35xx.c** as an example to present the information required for implementing device functions. +1. Instantiate the driver entry. -1. Instantiate the driver entry. - - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf\_device\_desc.h**), and the value of **moduleName** must be the same as that in **device\_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers are collected to form a segment address space similar to an array for the upper layer to invoke. Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. GPIO driver entry example: - - ``` + + ```c struct HdfDriverEntry g_gpioDriverEntry = { - .moduleVersion = 1, - .Bind = Pl061GpioBind, // Bind does not need to be implemented for GPIO. It is an empty method in this example. You can add related operations as required. - .Init = Pl061GpioInit, // See the Init function. - .Release = Pl061GpioRelease, // See the Release function. - .moduleName = "hisi_pl061_driver",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .moduleVersion = 1, + .Bind = Pl061GpioBind, // GPIO does not use the Bind function, which is an empty implementation in this example. You can add related operations as required. + ..Init = Pl061GpioInit, // See the Init function. + .Release = Pl061GpioRelease, // See the Release function. + .moduleName = "hisi_pl061_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_gpioDriverEntry); + HDF_INIT(g_gpioDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. ``` - -2. Add the **deviceNode** information to the **device_info.hcs** file, and configure the device attributes in the **gpio_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **GpioCntlr** members at the core layer. - In this example, there is only one GPIO controller. If there are multiple GPIO controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **gpio_config** file for each controller. - - - **device_info.hcs** configuration example - - ``` + +2. Configure attribute files. + + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is closely related to driver entry registration. In this example, there is only one GPIO controller. If there are multiple GPIO controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values are closely related to the default values or value ranges of the **GpioCntlr** members at the core layer, and are configured in **gpio_config.hcs**. + + - **device_info.hcs** example + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_gpio :: device { - device0 :: deviceNode { - policy = 0; // No service is published. - priority = 10; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "hisi_pl061_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - deviceMatchAttr = "hisilicon_hi35xx_pl061"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs. - // Add private information about all controllers in this file. + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_gpio :: device { + device0 :: deviceNode { + policy = 0; // The value 0 indicates that no service needs to be published. + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + oduleName = "hisi_pl061_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + deviceMatchAttr = "hisilicon_hi35xx_pl061"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs. + // The private information about all controllers is in the gpio_config.hcs file. + } + } } } - } - } } ``` - - **gpio_config.hcs** configuration example - - ``` + + - **gpio_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs** file. The parameters are as follows: + + ```c root { - platform { - gpio_config { - controller_0x120d0000 { - match_attr = "hisilicon_hi35xx_pl061"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - groupNum = 12; // (Mandatory) GPIO group number. - bitNum = 8; // (Mandatory) Number of GPIO pins in each group. - regBase = 0x120d0000; // (Mandatory) Physical base address. - regStep = 0x1000; // (Mandatory) Register offset step. - irqStart = 48; // (Mandatory) Enable interrupts. - irqShare = 0; // (Mandatory) Whether to share an interrupt. - } + platform { + gpio_config { + controller_0x120d0000 { + match_attr = "hisilicon_hi35xx_pl061"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + groupNum = 12; // (Mandatory) GPIO group number. + bitNum = 8; // (Mandatory) Number of GPIO pins in each group. + regBase = 0x120d0000; // (Mandatory) Physical base address. + regStep = 0x1000; // (Mandatory) Register offset step. + irqStart = 48; // (Mandatory) Enable interrupts. + irqShare = 0; // (Mandatory) Whether to share interrupt. The value 1 means to share interrupt; the value 0 means the opposite. + } + ... + } } } - } ``` -3. Initialize the **GpioCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Init** and **Release**) to instantiate **GpioMethod** in **GpioCntlr** (so that the underlying driver functions can be called). - - Defining a custom structure + After the **gpio_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // Relative path of the gpio_config.hcs file. + ``` + +3. Instantiate the GPIO controller object. + + Initialize the **GpioCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **GpioMethod** in **GpioCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **gpio_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the GPIO group number and the number of pins, to the **GpioCntlr** object at the core layer. - - ``` - struct Pl061GpioCntlr { - struct GpioCntlr cntlr;// (Mandatory) Control object at the core layer. The details are as follows: - volatile unsigned char *regBase;// (Mandatory) Register base address. - uint32_t phyBase; // (Mandatory) Physical base address. - uint32_t regStep; // (Mandatory) Register offset step. - uint32_t irqStart; // (Mandatory) Enable interrupts. - uint16_t groupNum; // (Mandatory) Parameter of the GPIO port number. - uint16_t bitNum; // (Mandatory) Parameter of the GPIO port number. - uint8_t irqShare; // (Mandatory) Whether to share an interrupt. - struct Pl061GpioGroup *groups; // (Optional) Set based on the actual requirements. + ```c + // Define the GPIO group information. + struct Pl061GpioGroup { + struct GpioCntlr cntlr // (Mandatory) Control object of the core layer. + volatile unsigned char *regBase; // (Mandatory) Register base address. + unsigned int index; + unsigned int irq; + OsalIRQHandle irqFunc; + OsalSpinlock lock; + uint32_t irqSave; + bool irqShare; + struct PlatformDumper *dumper; + char *dumperName; }; - struct Pl061GpioGroup { // Register address, IRQ number and function, and lock. - volatile unsigned char *regBase; - unsigned int index; - unsigned int irq; - OsalIRQHandle irqFunc; - OsalSpinlock lock; + + struct Pl061GpioData { + volatile unsigned char *regBase; // (Mandatory) Register base address. + uint32_t phyBase; // (Mandatory) Physical base address. + uint32_t regStep;; // (Mandatory) Register offset step. + uint32_t irqStart; // (Mandatory) Enable interrupts. + uint16_t groupNum; // (Mandatory) Parameter of the GPIO port number. + uint16_t bitNum; // (Mandatory) Parameter of the GPIO port number. + uint8_t irqShare; // (Mandatory) Whether to share interrupt. + struct Pl061GpioGroup *groups; // (Optional) Set as required. + struct GpioInfo *gpioInfo; + void *priv; }; + struct GpioInfo { + struct GpioCntlr *cntlr; + char name[GPIO_NAME_LEN]; + OsalSpinlock spin; + uint32_t irqSave; + struct GpioIrqRecord *irqRecord; + }; // GpioCntlr is the controller structure at the core layer. The Init function assigns values to the members of GpioCntlr. struct GpioCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct GpioMethod *ops; - struct DListHead list; - OsalSpinlock spin; - uint16_t start; - uint16_t count; - struct GpioInfo *ginfos; - void *priv; + struct PlatformDevice device; + struct GpioMethod *ops; + uint16_t start; + uint16_t count; + struct GpioInfo *ginfos; + bool isAutoAlloced; + void *priv; }; ``` - - Instantiating the **GpioMethod** structure in **GpioCntlr** (other members are initialized by **Init**) - - ``` - // The members of the GpioMethod structure are all callbacks. You need to implement the functions listed in Table 1. + - Instantiate the **GpioMethod** structure in **GpioCntlr**. + + ```c + // The members of the GpioMethod structure are hook functions. You need to implement them by referring to Table 1. static struct GpioMethod g_method = { .request = NULL, .release = NULL, - .write = Pl061GpioWrite, // Write the pin level. - .read = Pl061GpioRead, // Read the pin level. - .setDir = Pl061GpioSetDir, // Set the pin direction. - .getDir = Pl061GpioGetDir, // Obtain the pin direction. - .toIrq = NULL, - .setIrq = Pl061GpioSetIrq, // Set an IRQ for a pin. Skip it if this capability is not available. - .unsetIrq = Pl061GpioUnsetIrq, // Cancel the IRQ settings for a pin. Skip it if this capability is not available. - .enableIrq = Pl061GpioEnableIrq, // Enable interrupts for a pin. Skip it if this capability is not available. - .disableIrq = Pl061GpioDisableIrq,// Disable interrupts for a pin. Skip it if this capability is not available. + .write = Pl061GpioWrite, // Write the pin level. + .read = Pl061GpioRead, // Read the pin level. + .setDir = Pl061GpioSetDir, // Set the pin direction. + .getDir = Pl061GpioGetDir, // Obtain the pin direction. + .toIrq = NULL, + .setIrq = Pl061GpioSetIrq, // Set an IRQ function for a pin. Skip it if this capability is not available. + .unsetIrq = Pl061GpioUnsetIrq, // Cancel the IRQ function for a pin. Skip it if this capability is not available. + .enableIrq = Pl061GpioEnableIrq, // Enable interrupts for a pin. Skip it if this capability is not available. + .disableIrq = Pl061GpioDisableIrq, // Disable interrupts for a pin. Skip it if this capability is not available. }; ``` - - **Init** function + - Implement the **Init** function. - **Input parameter**: + Input parameters: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS - **Table 2** HDF_STATUS - | Status| Description| | -------- | -------- | | HDF_ERR_INVALID_OBJECT | Invalid controller object.| @@ -220,76 +259,185 @@ The following uses **gpio_hi35xx.c** as an example to present the information re | HDF_SUCCESS | Initialization successful.| | HDF_FAILURE | Initialization failed.| - **Function description**: + Function description: - Initializes the custom structure object and **GpioCntlr**, calls the **GpioCntlrAdd** function at the core layer, and (optional) connects to the virtual file system (VFS). + Initializes the custom structure object and **GpioCntlr**, calls **GpioCntlrAdd()** at the core layer, and (optional) accesses the virtual file system (VFS). + ```c + static struct Pl061GpioData g_pl061 = { + .groups = NULL, + .groupNum = PL061_GROUP_MAX, + .bitNum = PL061_BIT_MAX, + }; + + static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061) + { + int32_t ret; + uint16_t i; + struct Pl061GpioGroup *groups = NULL; + + if (pl061 == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum); + if (groups == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + pl061->groups = groups; + + for (i = 0; i < pl061->groupNum; i++) { + // Initialize related information. + groups[i].index = i; + groups[i].regBase = pl061->regBase + i * pl061->regStep; + groups[i].irq = pl061->irqStart + i; + groups[i].irqShare = pl061->irqShare; + groups[i].cntlr.start = i * pl061->bitNum; + groups[i].cntlr.count = pl061->bitNum; + groups[i].cntlr.ops = &g_method; + groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum]; + + if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) { + goto ERR_EXIT; + } + + ret = GpioCntlrAdd(&groups[i].cntlr); // Add related information to the HDF core. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__, + groups[i].cntlr.start, groups[i].cntlr.count, ret); + (void)OsalSpinDestroy(&groups[i].lock); + goto ERR_EXIT; + } + } + return HDF_SUCCESS; + + ERR_EXIT: + while (i-- > 0) { + GpioCntlrRemove(&groups[i].cntlr); + (void)OsalSpinDestroy(&groups[i].lock); + } + pl061->groups = NULL; + OsalMemFree(groups); + return ret; + } - ``` static int32_t Pl061GpioInit(struct HdfDeviceObject *device) { - ... - struct Pl061GpioCntlr *pl061 = &g_pl061;// Use static global variables to complete initialization. - // static struct Pl061GpioCntlr g_pl061 = { - // .groups = NULL, - // .groupNum = PL061_GROUP_MAX, - // .bitNum = PL061_BIT_MAX, - //}; - ret = Pl061GpioReadDrs(pl061, device->property);// Use the attribute values read from the gpio_config.hcs file to initialize the members of the custom structure object. - ... - pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);// Create address mapping. - ... - ret = Pl061GpioInitCntlrMem(pl061); // Allocate memory. - ... - pl061->cntlr.count = pl061->groupNum * pl061->bitNum;// (Mandatory) Calculate the number of pins. - pl061->cntlr.priv = (void *)device->property; // (Mandatory) Store device attributes. - pl061->cntlr.ops = &g_method; // (Mandatory) Attach the GpioMethod instance. - pl061->cntlr.device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and GpioCntlr. - ret = GpioCntlrAdd(&pl061->cntlr); // (Mandatory) Call this function to fill the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. - ... - Pl061GpioDebugCntlr(pl061); - #ifdef PL061_GPIO_USER_SUPPORT // (Optional) Access the user-level VFS if it is supported. - if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { - HDF_LOGE("%s: add vfs fail!", __func__); - } - #endif - ... + int32_t ret; + struct Pl061GpioData *pl061 = &g_pl061; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property null!", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM); + if (pl061->gpioInfo == NULL) { + HDF_LOGE("%s: failed to calloc gpioInfo!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + ret = Pl061GpioReadDrs(pl061, device->property);// Use the attribute values read from the gpio_config.hcs file to initialize the members of the custom structure object. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: failed to read drs:%d", __func__, ret); + return ret; + } + + if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 || + pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) { + HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum); + return HDF_ERR_INVALID_PARAM; + } + + pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);// Create address mapping. + if (pl061->regBase == NULL) { + HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase); + return HDF_ERR_IO; + } + + ret = Pl061GpioInitGroups(pl061); // Initialize the group information and add it to the HDF core layer. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err init groups:%d", __func__, ret); + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; + return ret; + } + pl061->priv = (void *)device->property; + device->priv = (void *)pl061; + Pl061GpioDebug(pl061); + + #ifdef PL061_GPIO_USER_SUPPORT + if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { + HDF_LOGE("%s: add vfs fail!", __func__); + } + #endif + HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device)); + return HDF_SUCCESS; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. + + Input parameters: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: No value is returned. - **Function description**: + Function description: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. - - ``` + ```c + static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061) + { + uint16_t i; + struct Pl061GpioGroup *group = NULL; + + for (i = 0; i < pl061->groupNum; i++) { + group = &pl061->groups[i]; + GpioDumperDestroy(&pl061->groups[i]); + GpioCntlrRemove(&group->cntlr); // Remove from the HDF core layer. + } + + OsalMemFree(pl061->groups); + pl061->groups = NULL; + } + static void Pl061GpioRelease(struct HdfDeviceObject *device) { - struct GpioCntlr *cntlr = NULL; - struct Pl061GpioCntlr *pl061 = NULL; - ... - cntlr = GpioCntlrFromDevice(device);// (Mandatory) Obtain the control object at the core layer through forced conversion. - // return (device == NULL) ? NULL : (struct GpioCntlr *)device->service; - ... - #ifdef PL061_GPIO_USER_SUPPORT - GpioRemoveVfs();// Reverse operation of GpioAddVfs in Init. - #endif - GpioCntlrRemove(cntlr); // (Mandatory) Remove the device information and services from the core layer. - pl061 = ToPl061GpioCntlr(cntlr); // return (struct Pl061GpioCntlr *)cntlr; - Pl061GpioRleaseCntlrMem(pl061); // (Mandatory) Release the lock and memory. - OsalIoUnmap((void *)pl061->regBase);// (Mandatory) Unmap the addresses. - pl061->regBase = NULL; + struct Pl061GpioData *pl061 = NULL; + + HDF_LOGI("%s: enter", __func__); + if (device == NULL) { + HDF_LOGE("%s: device is null!", __func__); + return; + } + + #ifdef PL061_GPIO_USER_SUPPORT + GpioRemoveVfs(); + #endif + + pl061 = (struct Pl061GpioData *)device->priv; + if (pl061 == NULL) { + HDF_LOGE("%s: device priv is null", __func__); + return; + } + + Pl061GpioUninitGroups(pl061); + OsalMemFree(pl061->gpioInfo); + pl061->gpioInfo = NULL; + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify the basic functions, such as the GPIO status control and response to interrupts. diff --git a/en/device-dev/driver/driver-platform-i2c-des.md b/en/device-dev/driver/driver-platform-i2c-des.md index 9ba71d09003c5598ca4636eed7fe718406a1f960..2373c1e253d494fd98019b652078905510e36d8c 100644 --- a/en/device-dev/driver/driver-platform-i2c-des.md +++ b/en/device-dev/driver/driver-platform-i2c-des.md @@ -1,115 +1,78 @@ -# I2C +# I2C -## Overview -The Inter-Integrated Circuit \(I2C\) is a simple, bidirectional, and synchronous serial bus that uses merely two wires. +## Overview -In an I2C communication, one controller communicates with one or more devices through the serial data line \(SDA\) and serial clock line \(SCL\), as shown in the figure below. +### Function -I2C data transfer must begin with a **START** condition and end with a **STOP** condition. Data is transmitted byte-by-byte from the most significant bit to the least significant bit. +The Inter-Integrated Circuit (I2C) is a simple, bidirectional, and synchronous serial bus that uses merely two wires. It is widely used in short-distance communication due to simple connection and low cost. + +### Working Principles + +In I2C communication, one controller communicates with one or more devices through the serial data line (SDA) and serial clock line (SCL), as shown in the figure below. + +I2C data transfer must begin with a **START** condition and end with a **STOP** condition. Data is transmitted byte-by-byte from the most significant bit to the least significant bit. Each I2C node is recognized by a unique address and can serve as either a controller or a device. When the controller needs to communicate with a device, it writes the device address to the bus through broadcast. A device matching this address sends a response to set up a data transfer channel. -The I2C APIs define a set of common functions for I2C data transfer, including: +The I2C module provides a set of APIs for I2C data transfer, including: +- Opening or closing an I2C controller +- Performing custom transfer via a message array + + **Figure 1** I2C physical connection -- I2C controller management: opening or closing an I2C controller -- I2C message transfer: custom transfer by using a message array + ![](figures/physical-connection-diagram-for-i2c.png "physical-connection-diagram-for-i2c") -**Figure 1** Physical connection diagram for I2C
![](figures/physical-connection-diagram-for-i2c.png "physical-connection-diagram-for-i2c") +## Usage Guidelines +### When to Use -## Available APIs +The I2C is used in communication with the sensors, executors, and input/output devices that support the I2C protocol. -**Table 1** APIs available for the I2C driver +### Available APIs - - - - - - - - - - - - - - - - - - -

Capability

-

Function

-

Description

-

I2C controller management

-

I2cOpen

-

Opens an I2C controller.

-

I2cClose

-

Closes an I2C controller.

-

I2C message transfer

-

I2cTransfer

-

Performs a custom transfer.

-
+The following table describes the APIs provided by the I2C module. For more information about the APIs, see **//drivers/hdf_core/framework/include/platform/i2c_if.h**. ->![](../public_sys-resources/icon-note.gif) **NOTE**
->All functions provided in this document can be called only in kernel mode. +**Table 1** I2C driver APIs -## Usage Guidelines +| API | Description| +| -------- | -------- | +| DevHandle I2cOpen(int16_t number) | Opens an I2C controller.| +| void I2cClose(DevHandle handle) | Closes an I2C controller.| +| int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count) | Transfers data.| -### How to Use +### How to Use -The figure below illustrates how to use the APIs. +The following figure illustrates how to use I2C APIs. -**Figure 2** Using I2C driver APIs +**Figure 2** Process of using I2C APIs ![](figures/using-I2C-process.png "process-of-using-an-i2c-device") -### Opening an I2C Controller - -Call the **I2cOpen()** function to open an I2C controller. - -DevHandle I2cOpen\(int16\_t number\); - -**Table 2** Description of I2cOpen - - - - - - - - - - - - - - - - - - - -

Parameter

-

Description

-

number

-

I2C controller ID.

-

Return Value

-

Description

-

NULL

-

Failed to open the I2C controller.

-

Device handle

-

Handle of the I2C controller.

-
- -This example assumes that the system has eight I2C controllers \(numbered from 0 to 7\) and I2C controller 3 is to open. +#### Opening an I2C Controller + +Call **I2cOpen()** to open an I2C controller. + +```c +DevHandle I2cOpen(int16_t number); ``` -DevHandle i2cHandle = NULL; /* I2C controller handle */ -/* Open an I2C controller. */ + **Table 2** Description of I2cOpen + +| **Parameter**| **Description**| +| -------- | -------- | +| number | I2C controller number.| +| Return Value| **Description**| +| NULL | The operation fails.| +| Device handle| The operation is successful. The handle of the I2C controller opened is returned.| + +Example: Open controller 3 of the eight I2C controllers (numbered 0 and 7) in the system. + +```c +DevHandle i2cHandle = NULL; /* I2C controller handle. */ + +/* Open I2C controller 3. */ i2cHandle = I2cOpen(3); if (i2cHandle == NULL) { HDF_LOGE("I2cOpen: failed\n"); @@ -117,70 +80,45 @@ if (i2cHandle == NULL) { } ``` -### Performing I2C Communication - -Call the **I2cTransfer()** function to transfer messages. - -int32\_t I2cTransfer\(DevHandle handle, struct I2cMsg \*msgs, int16\_t count\); - -**Table 3** Description of I2cTransfer - - - - - - - - - - - - - - - - - - - - - - - - - -

Parameter

-

Description

-

handle

-

Handle of an I2C controller.

-

msgs

-

Message array of the data to transfer.

-

count

-

Length of the message array.

-

Return Value

-

Description

-

Positive integer

-

Number of message structures that are successfully transmitted.

-

Negative value

-

Failed to perform the message transfer.

-
- -The type of an I2C message transfer is defined by **I2cMsg**. Each message structure indicates a read or write operation. Multiple read or write operations can be performed by using a message array. +#### Performing I2C Communication + +Call **I2cTransfer()** to transfer data. + +```c +int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count); ``` + + **Table 3** Description of I2cTransfer + +| **Parameter**| **Description**| +| -------- | -------- | +| handle | Handle of the I2C controller.| +| msgs | Pointer to the message array to transfer.| +| count | Number of messages in the message array to transfer.| +| Return Value| **Description**| +| Positive integer| The operation is successful. The number of messages that are successfully transferred is returned.| +| Negative value| The operation fails.| + +The I2C message type is defined by **I2cMsg**. Each message structure indicates a read or write operation. A message array specifies multiple read and write operations to perform. + +Example of read and write operations: + + +```c int32_t ret; uint8_t wbuff[2] = { 0x12, 0x13 }; uint8_t rbuff[2] = { 0 }; -struct I2cMsg msgs[2]; /* Custom message array for transfer */ -msgs[0].buf = wbuff; /* Data to write */ -msgs[0].len = 2; /* The length of the data to write is 2. */ -msgs[0].addr = 0x5A; /* The address of the device to write the data is 0x5A. */ -msgs[0].flags = 0; /* The flag is 0, indicating the write operation. */ -msgs[1].buf = rbuff; /* Data to read */ -msgs[1].len = 2; /* The length of the data to read is 2. */ -msgs[1].addr = 0x5A; /* The address of the device to read is 0x5A. */ -msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ is configured, indicating the read operation. */ -/* Perform a custom transfer to transfer two messages. */ +struct I2cMsg msgs[2]; /* Custom message array to transfer. */ +msgs[0].buf = wbuff; /* Data to write. */ +msgs[0].len = 2; /* The length of the data to write is 2. */ +msgs[0].addr = 0x5A; /* The address of the device to write the data is 0x5A. */ +msgs[0].flags = 0; /* The flag 0 indicates a write operation. */ +msgs[1].buf = rbuff; /* Data to read. */ +msgs[1].len = 2; /* The length of the data to read is 2. */ +msgs[1].addr = 0x5A; /* The address of the device to read is 0x5A. */ +msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ is set. */ +/* Transfer two messages. */ ret = I2cTransfer(i2cHandle, msgs, 2); if (ret != 2) { HDF_LOGE("I2cTransfer: failed, ret %d\n", ret); @@ -188,78 +126,74 @@ if (ret != 2) { } ``` ->![](../public_sys-resources/icon-caution.gif) **CAUTION**
->- The device address in the **I2cMsg** structure does not contain the read/write flag bit. The read/write information is transferred by the read/write control bit in the member variable **flags**. ->- The **I2cTransfer** function does not limit the number of message structures and the data length of each message structure, which are determined by the I2C controller. ->- The **I2cTransfer** function may cause the system to sleep and therefore cannot be called in the interrupt context. +> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> - The device address in the **I2cMsg** structure does not contain the read/write flag bit. The read/write information is passed by the read/write control bit in **flags**. +> +> - The I2C controller determines the maximum number of messages to be transferred at a time and the maximum length of each message to transfer. +> +> - The **I2cTransfer** function may cause the system to sleep and therefore cannot be called in the interrupt context. -### Closing an I2C Controller -Call the **I2cClose()** function to close the I2C controller after the communication is complete. +#### Closing an I2C Controller -void I2cClose\(DevHandle \*handle\); +Call **I2cClose()** to close the I2C controller after the communication is complete. -**Table 4** Description of I2cClose +```c +void I2cClose(DevHandle handle); +``` - - - - - - - - - -

Parameter

-

Description

-

handle

-

Handle of the I2C controller to close.

-
+ **Table 4** Description of I2cClose +| Parameter| Description| +| -------- | -------- | +| handle | Handle of the I2C controller to close.| -``` -I2cClose(i2cHandle); /* Close the I2C controller. */ +Example: + +```c +I2cClose(i2cHandle); /* Close an I2C controller. */ ``` -## Usage Example -This example describes how to use I2C APIs with an I2C device on a development board. +### Example -This example shows a simple register read/write operation on TouchPad on a Hi3516D V300 development board. The basic hardware information is as follows: +The following example describes how to use I2C APIs to implement simple read/write operations on TouchPad from a Hi3516D V300 development board. -- SoC: hi3516dv300 +The basic hardware information is as follows: -- Touch IC: The I2C address is 0x38, and the bit width of Touch IC's internal register is 1 byte. +- SoC: Hi3516D V300 -- Schematic diagram: TouchPad is mounted to I2C controller 3. The reset pin of Touch IC is GPIO3. +- Touch IC: The I2C address is 0x38, and the bit width of touch IC internal register is 1 byte. -In this example, first we reset Touch IC. \(The development board supplies power to Touch IC by default after being powered on, and this use case does not consider the power supply\). Then, we perform a read/write operation on an internal register to test whether the I2C channel is normal. +- Hardware connection: The TouchPad is connected to I2C controller 3. The reset pin of the touch IC is GPIO 3. ->![](../public_sys-resources/icon-note.gif) **NOTE**
->The example focuses on I2C device access and verifies the I2C channel. The read and write values of the device register are not concerned. The behavior caused by the read and write operations on the register is determined by the device itself. +In this example, reset the touch IC (the development board supplies power to the touch IC by default after being powered on) and perform read/write operations on the internal register to test whether the I2C channel is functioning. -Example: +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
+> This example focuses on access to the I2C device and verifies the I2C channel, rather than the specific data read from or written to the device register and the result caused by the read and write operations on the register. -``` -#include "i2c_if.h" /* Header file of I2C APIs */ -#include "gpio_if.h" /* Header file of GPIO APIs */ -#include "hdf_log.h" /* Header file for log APIs */ -#include "osal_io.h" /* Header file of I/O read and write APIs */ -#include "osal_time.h" /* Header file of delay and sleep APIs */ +The sample code is as follows: + +```c +#include "i2c_if.h" /* Header file of I2C APIs. */ +#include "gpio_if.h" /* Header file of GPIO APIs. */ +#include "hdf_log.h" /* Header file of log APIs. */ +#include "osal_io.h" /* Header file of I/O read and write APIs. */ +#include "osal_time.h" /* Header file of delay and sleep APIs. */ /* Define a TP device structure to store I2C and GPIO hardware information. */ struct TpI2cDevice { - uint16_t rstGpio; /* Reset pin */ - uint16_t busId; /* I2C bus ID */ - uint16_t addr; /* I2C device address */ - uint16_t regLen; /* Register bit width */ - DevHandle i2cHandle; /* I2C controller handle */ + uint16_t rstGpio; /* Reset pin. */ + uint16_t busId; /* I2C bus number. */ + uint16_t addr; /* I2C device address. */ + uint16_t regLen; /* Register bit width. */ + DevHandle i2cHandle; /* I2C controller handle. */ }; /* I2C pin I/O configuration. For details, see the SoC register manual. */ -#define I2C3_DATA_REG_ADDR 0x112f008c /* Address of the SDA pin configuration register of I2C controller 3 -#define I2C3_CLK_REG_ADDR 0x112f0090 /* Address of the SCL pin configuration register of I2C controller 3 -#define I2C_REG_CFG 0x5f1 /* Configuration values of SDA and SCL pins of I2C controller 3 +#define I2C3_DATA_REG_ADDR 0x112f008c /* Address of the SDA pin configuration register of I2C controller 3. */ +#define I2C3_CLK_REG_ADDR 0x112f0090 /* Address of the SCL pin configuration register of I2C controller 3. */ +#define I2C_REG_CFG 0x5f1 /* Configuration values of SDA and SCL pins of I2C controller 3. */ static void TpSocIoCfg(void) { @@ -268,7 +202,7 @@ static void TpSocIoCfg(void) OSAL_WRITEL(I2C_REG_CFG, IO_DEVICE_ADDR(I2C3_CLK_REG_ADDR)); } -/* Initialize the reset pin of the TP. Pull up the pin for 20 ms, pull down the pin for 50 ms, and then pull up the pin for 20 ms to complete the resetting. */ +/* Initialize the reset pin of the TP. Pull up the pin for 20 ms, pull down the pin for 50 ms, and then pull up the pin for 20 ms to complete the reset. */ static int32_t TestCaseGpioInit(struct TpI2cDevice *tpDevice) { int32_t ret; @@ -322,7 +256,7 @@ static int TpI2cReadWrite(struct TpI2cDevice *tpDevice, unsigned int regAddr, /* Fill in the I2cMsg message structure. */ msgs[0].addr = tpDevice->addr; - msgs[0].flags = 0; /* The flag is 0, indicating the write operation. */ + msgs[0].flags = 0; /* The flag 0 indicates a write operation. */ msgs[0].len = tpDevice->regLen; msgs[0].buf = regBuf; @@ -338,14 +272,14 @@ static int TpI2cReadWrite(struct TpI2cDevice *tpDevice, unsigned int regAddr, return HDF_SUCCESS; } -/* TP register read function */ +/* TP register read function. */ static inline int TpI2cReadReg(struct TpI2cDevice *tpDevice, unsigned int regAddr, unsigned char *regData, unsigned int dataLen) { return TpI2cReadWrite(tpDevice, regAddr, regData, dataLen, 1); } -/* TP register write function */ +/* TP register write function. */ static inline int TpI2cWriteReg(struct TpI2cDevice *tpDevice, unsigned int regAddr, unsigned char *regData, unsigned int dataLen) { @@ -361,7 +295,7 @@ static int32_t TestCaseI2c(void) unsigned char bufRead[7] = {0}; static struct TpI2cDevice tpDevice; - /* I/O pin function configuration */ + /* Configuration of I/O pin functions. */ TpSocIoCfg(); /* Initialize TP device information. */ @@ -371,7 +305,7 @@ static int32_t TestCaseI2c(void) tpDevice.regLen = 1; tpDevice.i2cHandle = NULL; - /* Initialize the GPIO pin. */ + /* Initialize the GPIO pin. */ ret = TestCaseGpioInit(&tpDevice); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: gpio init fail!:%d", __func__, ret); @@ -412,4 +346,3 @@ static int32_t TestCaseI2c(void) return ret; } ``` - diff --git a/en/device-dev/driver/driver-platform-i2c-develop.md b/en/device-dev/driver/driver-platform-i2c-develop.md index 9366b4b5505a63f1fd9c8cd9cd4ab8fb1a63c3bb..2b36f75b55de2408eccea73bc6329ef954c630c7 100644 --- a/en/device-dev/driver/driver-platform-i2c-develop.md +++ b/en/device-dev/driver/driver-platform-i2c-develop.md @@ -1,195 +1,255 @@ # I2C - ## Overview -The Inter-Integrated Circuit (I2C) bus is a simple and bidirectional two-wire synchronous serial bus developed by Philips. In the Hardware Driver Foundation (HDF), the I2C module uses the unified service mode for API adaptation. In this mode, a device service is used as the I2C manager to handle external access requests in a unified manner, which is reflected in the configuration file. The unified service mode applies to the scenario where there are many device objects of the same type, for example, when the I2C module has more than 10 controllers. If the independent service mode is used, more device nodes need to be configured and memory resources will be consumed by services. +### Function + +The Inter-Integrated Circuit (I2C) is a simple, bidirectional, and synchronous serial bus that uses merely two wires. It is widely used in short-distance communication due to simple connection and low cost. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the I2C module uses the unified service mode for API adaptation. In this mode, a device service is used as the I2C manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure illustrates the unified service mode. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. + +The I2C module is divided into the following layers: + +- Interface layer: provides the capabilities of opening and closing a device and transferring data. +- Core layer: binds services, initializes and releases the PlatformManager, and provides the capabilities of adding, deleting, and obtaining controllers. +- Adaptation layer: implements hardware-related functions, such as controller initialization. **Figure 1** Unified service mode - ![image](figures/unified-service-mode.png "I2C Unified Service Mode") +![image](figures/unified-service-mode.png "I2C Unified Service Mode") -## Available APIs +## Usage Guidelines -**I2cMethod** and **I2cLockMethod** +### When to Use +The I2C is used in communication with the sensors, executors, and input/output devices that support the I2C protocol. Before using I2C devices with OpenHarmony, you need to adapt the I2C driver to OpenHarmony. The following describes how to do it. -``` +### Available APIs + +To enable the upper layer to successfully operate the hardware by calling the I2C APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/i2c/i2c_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**I2cMethod** and **I2cLockMethod**: + +```c struct I2cMethod { -int32_t (*transfer)(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int16_t count); + int32_t (*transfer)(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int16_t count); +}; + +struct I2cLockMethod {// Structure for the lock operation. + int32_t (*lock)(struct I2cCntlr *cntlr); + void (*unlock)(struct I2cCntlr *cntlr); }; -struct I2cLockMethod {// Lock mechanism operation structure - int32_t (*lock)(struct I2cCntlr *cntlr); // Add a lock. - void (*unlock)(struct I2cCntlr *cntlr); // Release the lock. +``` + +At the adaptation layer, **I2cMethod** must be implemented, and **I2cLockMethod** can be implemented based on service requirements. The core layer provides the default **I2cLockMethod**, in which a mutex is used to protect the critical section. + +```c +static int32_t I2cCntlrLockDefault(struct I2cCntlr *cntlr) +{ + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + return OsalMutexLock(&cntlr->lock); +} + +static void I2cCntlrUnlockDefault(struct I2cCntlr *cntlr) +{ + if (cntlr == NULL) { + return; + } + (void)OsalMutexUnlock(&cntlr->lock); +} + +static const struct I2cLockMethod g_i2cLockOpsDefault = { + .lock = I2cCntlrLockDefault, + .unlock = I2cCntlrUnlockDefault, }; ``` - **Table 1** Description of the callback functions in I2cMethod +If a mutex cannot be used (for example, an I2C API is called in the interrupt context, which does not allow sleep, but a mutex may cause sleep), you can use another type of lock to implement **I2cLockMethod**. The implemented **I2cLockMethod** will replace the default **I2cLockMethod**. + + **Table 2** Function in **I2cMethod** | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| transfer | **cntlr**: structure pointer to the I2C controller at the core layer.

**msgs**: structure pointer to the messages to transfer.
**count**: number of messages. The value is of the uint16_t type.| –| HDF_STATUS| Transfers user messages.| +| transfer | **cntlr**: structure pointer to the I2C controller at the core layer.
**msgs**: structure pointer to the messages to transfer.
**count**: number of messages. The value is of the uint16_t type.| –| HDF_STATUS| Transfers user messages.| + + **Table 2** Functions in **I2cLockMethod** +| Function| Input Parameter| Output Parameter| Return Value| Description| +| -------- | -------- | -------- | -------- | -------- | +| lock | **cntlr**: structure pointer to the I2C controller at the core layer.| –| HDF_STATUS| Acquires the critical section lock.| +| unlock | **cntlr**: structure pointer to the I2C controller at the core layer.| –| HDF_STATUS| Releases the critical section lock.| -## How to Develop +### How to Develop The I2C module adaptation involves the following steps: 1. Instantiate the driver entry. + - Instantiate the **HdfDriverEntry** structure. - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. 2. Configure attribute files. + - Add the **deviceNode** information to the **device_info.hcs** file. - (Optional) Add the **i2c_config.hcs** file. 3. Instantiate the I2C controller object. + - Initialize **I2cCntlr**. - Instantiate **I2cMethod** and **I2cLockMethod** in **I2cCntlr**. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** - > + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> For details, see [Available APIs](#available-apis). - -4. Debug the driver.
- (Optional) For new drivers, verify basic functions, for example, check whether data is successfully transmitted and the information returned after the virtual file system (VFS) is mounted. +4. Debug the driver. + + (Optional) For new drivers, verify basic functions, for example, check whether data is successfully transferred and the information returned after the virtual file system (VFS) is mounted. -## Development Example +### Example -The following uses **i2c_hi35xx.c** as an example to present the information required for implementing device functions. +The following uses the Hi3516D V300 driver **//device/soc/hisilicon/common/platform/i2c/i2c_hi35xx.c** as an example to describe how to perform the I2C driver adaptation. 1. Instantiate the driver entry. - + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. - + + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. + I2C driver entry example: - - An I2C controller may be connected with multiple devices. You need to create a manager object in the HDF and publish a manager service to handle external access requests in a unified manner. Before a device is used, the manager service must be obtained first. Then, the manager service locates the target device based on the specified parameters. - - The driver of the I2C manager is implemented by the core layer. Vendors do not need to care about the implementation of this part. However, the **Init** function must call the **I2cCntlrAdd** function of the core layer to implement corresponding features. - - ``` - struct HdfDriverEntry g_i2cDriverEntry = { + + Multiple devices may connect to the I2C controller. In the HDF, a manager object needs to be created for this type of devices, and a manager service is published to handle external access requests uniformly. When a device needs to be started, the manager service locates the target device based on the specified parameters. + + You do not need to implement the driver of the I2C manager, which is implemented by the core layer. However, the **I2cCntlrAdd** function of the core layer must be invoked in the **Init** function to implement the related features. + + ```c + struct HdfDriverEntry g_i2cDriverEntry = { .moduleVersion = 1, .Init = Hi35xxI2cInit, .Release = Hi35xxI2cRelease, .moduleName = "hi35xx_i2c_driver", // (Mandatory) The value must be the same as that in the config.hcs file. - }; - HDF_INIT(g_i2cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. - - // Driver entry of the i2c_core.c manager service at the core layer - struct HdfDriverEntry g_i2cManagerEntry = { + }; + HDF_INIT(g_i2cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + + /* Driver entry of the manager service i2c_core.c at the core layer. */ + struct HdfDriverEntry g_i2cManagerEntry = { .moduleVersion = 1, .Bind = I2cManagerBind, .Init = I2cManagerInit, .Release = I2cManagerRelease, - .moduleName = "HDF_PLATFORM_I2C_MANAGER", // This parameter corresponds to device0 in the device_info file. - }; - HDF_INIT(g_i2cManagerEntry); - ``` - - + .moduleName = "HDF_PLATFORM_I2C_MANAGER", // The value must be the same as that of device0 in the device_info.hcs file. + }; + HDF_INIT(g_i2cManagerEntry); + ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **i2c_config.hcs** file. +2. Add the **deviceNode** information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file and configure the device attributes in **i2c_config.hcs**. - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **I2cCntlr** members at the core layer. + The **deviceNode** information is related to the driver entry registration. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **I2cCntlr** members at the core layer. - In the unified service mode, the first device node in the **device_info** file must be the I2C manager. The table below lists the settings of its parameters. - - **Table 2** Settings of the I2C manager + In the unified service mode, the first device node in the **device_info.hcs** file must be the I2C manager. The table below lists the settings of its parameters. + **Table 3** Settings of the I2C manager + | Parameter| Value| | -------- | -------- | | moduleName | **HDF_PLATFORM_I2C_MANAGER**| | serviceName | **HDF_PLATFORM_I2C_MANAGER**| | policy | **1** or **2**, depending on whether the service is published to the user mode.| - | deviceMatchAttr | Reserved| - - Configure I2C controller information from the second node. This node specifies a type of I2C controllers rather than a specific I2C controller. The controllers are distinguishes by **busID** and **reg_pbase**, which can be seen in the **i2c_config** file. - - - **device_info.hcs** configuration example - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - device_i2c :: device { - device0 :: deviceNode { - policy = 2; - priority = 50; - permission = 0644; - moduleName = "HDF_PLATFORM_I2C_MANAGER"; - serviceName = "HDF_PLATFORM_I2C_MANAGER"; - deviceMatchAttr = "hdf_platform_i2c_manager"; - } - device1 :: deviceNode { - policy = 0; // The value 0 indicates that no service needs to be published. - priority = 55; // Driver startup priority. - permission = 0644; // Permission for the driver to create a device node. - moduleName = "hi35xx_i2c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HI35XX_I2C_DRIVER"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_i2c"; // (Mandatory) Used to configure the private data of the controller. The value must be the same as the controller information in i2c_config.hcs. - //The specific controller information is stored in i2c_config.hcs. - } - } - } - } - ``` - - - **i2c_config.hcs** configuration example - - - ``` - root { - platform { - i2c_config { - match_attr = "hisilicon_hi35xx_i2c"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - template i2c_controller { // Template configuration. In the template, you can configure the common parameters shared by service nodes. - bus = 0; // (Mandatory) I2C identifier. - reg_pbase = 0x120b0000; // (Mandatory) Physical base address. - reg_size = 0xd1; // (Mandatory) Register bit width. - irq = 0; // (Optional) Configured based on your requirements. - freq = 400000; // (Optional) Configured based on your requirements. - clk = 50000000; // (Optional) Configured based on your requirements. - } - controller_0x120b0000 :: i2c_controller { - bus = 0; - } - controller_0x120b1000 :: i2c_controller { - bus = 1; - reg_pbase = 0x120b1000; - } - ... - } - } - } - ``` + | deviceMatchAttr | This parameter is reserved.| + + Configure I2C controller information from the second node. This node specifies a type of I2C controllers rather than a specific I2C controller. The controllers are distinguished by **busID** and **reg_pbase**, which can be seen in the **i2c_config.hcs** file. + + - **device_info.hcs** example + + ```c + root { + device_info { + match_attr = "hdf_manager"; + device_i2c :: device { + device0 :: deviceNode { + policy = 2; + priority = 50; + permission = 0644; + moduleName = "HDF_PLATFORM_I2C_MANAGER"; + serviceName = "HDF_PLATFORM_I2C_MANAGER"; + deviceMatchAttr = "hdf_platform_i2c_manager"; + } + device1 :: deviceNode { + policy = 0; // The value 0 indicates that no service is published. + priority = 55; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi35xx_i2c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HI35XX_I2C_DRIVER"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hisilicon_hi35xx_i2c"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in i2c_config.hcs. + //The specific controller information is in i2c_config.hcs. + } + } + } + } + ``` + + - **i2c_config.hcs** example + + ```c + root { + platform { + i2c_config { + match_attr = "hisilicon_hi35xx_i2c"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + template i2c_controller { // Template configuration. In the template, you can configure the common parameters shared by service nodes. + bus = 0; // (Mandatory) I2C identifier. + reg_pbase = 0x120b0000; // (Mandatory) Physical base address. + reg_size = 0xd1; // (Mandatory) Register bit width. + irq = 0; // (Optional) Interrupt request (IRQ) number. The interrupt feature of the controller determines whether an IRQ number is required. + freq = 400000; // (Optional) Frequency used in hardware controller initialization. + clk = 50000000; // (Optional) Controller clock. The controller clock initialization process determines whether a controller clock is required. + } + controller_0x120b0000 :: i2c_controller { + bus = 0; + } + controller_0x120b1000 :: i2c_controller { + bus = 1; + reg_pbase = 0x120b1000; + } + ... + } + } + } + ``` + + After the **i2c_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + For example, if the path of **i2c_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs**, add the following statement to **hdf.hcs** of the product: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // Relative path of the file. + ``` 3. Initialize the **I2cCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **I2cMethod** in **I2cCntlr** (so that the underlying driver functions can be called). - - Defining a custom structure + + - Define a custom structure. To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **i2c_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **I2cCntlr** object at the core layer. - - ``` - // Custom structure + ```c + /* Custom structure. */ struct Hi35xxI2cCntlr { struct I2cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description. - OsalSpinlock spin; // (Mandatory) You need to implement lock() and unlock() based on this variable. - volatile unsigned char *regBase; // (Mandatory) Register base address. + OsalSpinlock spin; // (Mandatory) Lock or unlock an I2C operation function. + volatile unsigned char *regBase;// (Mandatory) Register base address. uint16_t regSize; // (Mandatory) Register bit width. int16_t bus; // (Mandatory) The value can be read from the i2c_config.hcs file. - uint32_t clk; // (Optional) Customized. - uint32_t freq; // (Optional) Customized. - uint32_t irq; // (Optional) Customized. - uint32_t regBasePhy; // (Mandatory) Physical base address of the register. + uint32_t clk; // (Optional) Set it as required. + uint32_t freq; // (Optional) Set it as required. + uint32_t irq; // (Optional) Set it as required. + uint32_t regBasePhy // (Mandatory) Physical base address of the register. }; - // I2cCntlr is a controller structure at the core layer. The Init function assigns values to the members of I2cCntlr. + /* I2cCntlr is the core layer controller structure. The **Init()** function assigns values to the members of I2cCntlr. */ struct I2cCntlr { struct OsalMutex lock; void *owner; @@ -199,31 +259,32 @@ The following uses **i2c_hi35xx.c** as an example to present the information req const struct I2cLockMethod *lockOps; }; ``` - - Instantiating **I2cMethod** and **I2cLockMethod** (other members are initialized by **Init**) - - ``` - // Example in i2c_hi35xx.c + - Instantiate **I2cMethod** and **I2cLockMethod**. Other members are initialized by **Init**. + + ```c + /* Example in i2c_hi35xx.c */ static const struct I2cMethod g_method = { .transfer = Hi35xxI2cTransfer, }; static const struct I2cLockMethod g_lockOps = { - .lock = Hi35xxI2cLock, // Lock function - .unlock = Hi35xxI2cUnlock, // Unlock function + .lock = Hi35xxI2cLock, // Acquires the lock. + .unlock = Hi35xxI2cUnlock, // Release the lock. }; ``` - - **Init** function - **Input parameter**: + - Implement the **Init** function. + + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. - **Table 3** HDF_STATUS + **Table 4** HDF_STATUS | Status| Description| | -------- | -------- | @@ -234,16 +295,15 @@ The following uses **i2c_hi35xx.c** as an example to present the information req | HDF_SUCCESS | Transmission successful.| | HDF_FAILURE | Transmission failed.| - **Function description**: + Function description: - Initializes the custom structure object and **I2cCntlr**, calls the **I2cCntlrAdd** function at the core layer, and mounts the VFS (optional). + Initialize the custom structure object and **I2cCntlr**, call **I2cCntlrAdd()** at the core layer, and connect to the VFS (optional). - - ``` + ```c static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device) { ... - // Traverse and parse all nodes in i2c_config.hcs and call Hi35xxI2cParseAndInit to initialize the devices separately. + /* Traverse and parse all nodes in i2c_config.hcs and call Hi35xxI2cParseAndInit to initialize the devices separately. */ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { ret = Hi35xxI2cParseAndInit(device, childNode);// The function is defined as follows. ... @@ -254,25 +314,25 @@ The following uses **i2c_hi35xx.c** as an example to present the information req static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) { struct Hi35xxI2cCntlr *hi35xx = NULL; - ... + ... // Check whether the input parameter is null. hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // Allocate memory. - ... + ... // Verify the return value. hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // Address mapping. - ... - Hi35xxI2cCntlrInit(hi35xx); // (Mandatory) Initialize the I2C device. + ... // Verify the return value. + Hi35xxI2cCntlrInit(hi35xx); // (Mandatory) Initialize the I2C device. - hi35xx->cntlr.priv = (void *)node; // (Mandatory) Device attributes. - hi35xx->cntlr.busId = hi35xx->bus; // (Mandatory) Initialize busId in I2cCntlr. - hi35xx->cntlr.ops = &g_method; // (Mandatory) Hook the I2cMethod instance. - hi35xx->cntlr.lockOps = &g_lockOps; // (Mandatory) Hook the I2cLockMethod instance. - (void)OsalSpinInit(&hi35xx->spin); // (Mandatory) Initialize the lock. - ret = I2cCntlrAdd(&hi35xx->cntlr); // (Mandatory) Call this function to set the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. + hi35xx->cntlr.priv = (void *)node; // (Mandatory) Device attributes. + hi35xx->cntlr.busId = hi35xx->bus; // (Mandatory) Initialize busId in I2cCntlr. + hi35xx->cntlr.ops = &g_method; // (Mandatory) Hook the I2cMethod instance. + hi35xx->cntlr.lockOps = &g_lockOps; // (Mandatory) Hook the I2cLockMethod instance. + (void)OsalSpinInit(&hi35xx->spin); // (Mandatory) Initialize the lock. + ret = I2cCntlrAdd(&hi35xx->cntlr); // (Mandatory) Call this function to add the controller object to the core layer of the platform. The driver can access the core layer of the platform only after a success signal is returned. ... #ifdef USER_VFS_SUPPORT (void)I2cAddVfsById(hi35xx->cntlr.busId);// (Optional) Mount the user-level VFS if required. #endif return HDF_SUCCESS; - __ERR__: // If the operation fails, execute the initialization functions reversely. + __ERR__: // If the operation fails, roll back the operations that have been performed in the function (such as unmapping I/O and releasing memory) and return an error code. if (hi35xx != NULL) { if (hi35xx->regBase != NULL) { OsalIoUnmap((void *)hi35xx->regBase); @@ -284,26 +344,26 @@ The following uses **i2c_hi35xx.c** as an example to present the information req return ret; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. + + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. - **Return value**: + Return value: No value is returned. - **Function description**: + Function description: Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. - - ``` + ```c static void Hi35xxI2cRelease(struct HdfDeviceObject *device) { ... - // Release each node separately, like Hi35xxI2cInit. + /* Release each node separately, like Hi35xxI2cInit. */ DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { Hi35xxI2cRemoveByNode(childNode);// The function is defined as follows: } @@ -312,12 +372,12 @@ The following uses **i2c_hi35xx.c** as an example to present the information req static void Hi35xxI2cRemoveByNode(const struct DeviceResourceNode *node) { ... - // (Mandatory) Call the I2cCntlrGet function to obtain the I2cCntlr object based on bus ID of the device, and call the I2cCntlrRemove function to release the I2cCntlr object. + /* (Mandatory) Call I2cCntlrGet() to obtain the pointer to the I2cCntlr object based on the bus number of the device, and call I2cCntlrRemove() to remove the I2cCntlr object from the core layer of the platform. */ cntlr = I2cCntlrGet(bus); if (cntlr != NULL && cntlr->priv == node) { ... I2cCntlrRemove(cntlr); - // (Mandatory) Unmap the addresses and release the lock and memory. + /* (Mandatory) Unmap the register address and release the lock and memory. */ hi35xx = (struct Hi35xxI2cCntlr *)cntlr; OsalIoUnmap((void *)hi35xx->regBase); (void)OsalSpinDestroy(&hi35xx->spin); diff --git a/en/device-dev/driver/driver-platform-i3c-des.md b/en/device-dev/driver/driver-platform-i3c-des.md index 1108e71375a1baa49cf7e649909a392ce612d9de..fa2dcf1cbd69f21250e0afc6672717bd1b547b4e 100644 --- a/en/device-dev/driver/driver-platform-i3c-des.md +++ b/en/device-dev/driver/driver-platform-i3c-des.md @@ -8,7 +8,7 @@ Improved Inter-Integrated Circuit (I3C) is a simple and cost-efficient two-wire I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target devices and controlled by only one I3C controller at a time. It is backward compatible with Inter-Integrated circuit (I2C) target devices, but features higher speed and lower power consumption. Moreover, I3C supports in-band interrupts (IBIs), hot-joins of target devices, and controller switchover. The IBIs over the serial bus eliminates the need for an extra interrupt line to complete interrupts in I2C. I2C devices, I3C target devices, and the I3C secondary controller can co-exist on the same I3C bus. -The I3C driver APIs provide a set of common functions for I3C transfer, including: +The I3C module provides a set of common APIs for I3C transfer, including: - Opening and closing an I3C controller - Obtaining and setting I3C controller parameters - Performing custom I3C message transfer by using a message array @@ -17,17 +17,14 @@ The I3C driver APIs provide a set of common functions for I3C transfer, includin ### Basic Concepts - IBI - + When there is no start signal on the serial clock (SCL) line, the I3C target device can pull down the serial data (SDA) line to make the controller send an SCL start signal, which initiates an IBI request. If multiple target devices send interrupt requests at the same time, the I3C controller arbitrates the requests based on the target device addresses. The request with a lower address is responded first. - + - Dynamic Address Assignment (DAA) The I3C controller can dynamically allocate addresses to target devices to avoid address conflicts. Before addresses are allocated, each I3C device connected to a I3C bus must be uniquely identified in either of the following ways: - - - The device has an I2C compliant static address that can be used by the host. - - The device has a 48-bit temporary ID. - - The host must use a 48-bit temporary ID unless the device has a static IP address. + 1) The device has an I2C compliant static address that can be used by the host. + 2) The device has a 48-bit temporary ID. The host must use a 48-bit temporary ID unless the device has a static IP address. - Common Command Code (CCC) @@ -43,49 +40,54 @@ The I3C driver APIs provide a set of common functions for I3C transfer, includin ### Working Principles -In the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than 10 I3C controllers. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. +In the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than 10 I3C controllers. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. -Multiple devices, such as I2C target device, I3C target device, and I3C secondary controller, can be connected to an I3C bus. However, the I3C bus must have only one controller. +Compared with I2C, I3C features higher speed and lower power consumption, supports IBIs, hot-joins of target devices, and controller switchover. I3C is also backward compatible with I2C target devices. Multiple devices, such as I2C target device, I3C target device, and I3C secondary controller, can be connected to an I3C bus. However, the I3C bus must have only one controller. + +**Figure 1** I3C physical connection -**Figure 1** I3C physical connection ![](figures/I3C_physical_connection.png "I3C_physical_connection") ### Constraints -Currently, the I3C module supports only the kernels (LiteOS) of mini and small systems. +The I3C module supports only the kernel (LiteOS-A) for mini and small systems and cannot be used in user mode. ## Usage Guidelines ### When to Use I3C can connect to one or more I3C or I2C target devices. It is used to: + - Communicate with sensors, such as gyroscopes, barometers, and image sensors that support the I3C protocol. - Communicate with devices with other ports (such as UART serial ports) through software or hardware protocols. ### Available APIs -**Table 1** I3C driver APIs +The following table describes the APIs provided by the I3C module. For more information about the APIs, see **//drivers/hdf_core/framework/include/platform/i3c_if.h**. +**Table 1** I3C driver APIs -| API | Description | +| API | Description | | ------------- | ----------------- | -| I3cOpen | Opens an I3C controller. | -| I3cClose | Closes an I3C controller. | -| I3cTransfer | Performs custom transfer. | -| I3cSetConfig | Sets the I3C controller. | -| I3cGetConfig | Obtains the I3C controller configuration. | -| I3cRequestIbi | Requests an IBI. | -| I3cFreeIbi | Releases an IBI. | - ->![](../public_sys-resources/icon-note.gif) **NOTE**
+| DevHandle I3cOpen(int16_t number) | Opens an I3C controller. | +| void I3cClose(DevHandle handle) | Closes an I3C controller. | +| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | Performs custom transfer. | +| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | Sets the I3C controller. | +| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | Obtains I3C controller configuration.| +| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | Requests an IBI. | +| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | Releases an IBI. | + +>![](../public_sys-resources/icon-note.gif) **NOTE** +> >All APIs described in this document can be called only in kernel mode. ### How to Develop -The figure below illustrates the use of I3C driver APIs. +The following figure illustrates how to use the I3C APIs. + +**Figure 2** Process of using I3C driver APIs -**Figure 2** Process of using I3C driver APIs -![](figures/I3C_usage_flowchart.png "I3C_usage_flowchart") +![](figures/using-I3C-process.png) #### Opening an I3C Controller @@ -98,10 +100,10 @@ DevHandle I3cOpen(int16_t number); | Name | Description | | ---------- | ------------------- | -| number | I3C controller number. | +| number | I3C controller number. | | **Return Value**| **Description** | -| NULL | The operation failed. | -| Controller handle| The operation is successful. The handle of the I3C controller opened is returned. | +| NULL | The operation fails. | +| Controller handle| The operation is successful. The handle of the I3C controller opened is returned.| Example: Open I3C controller 1 of the eight I3C controllers numbered from 0 to 7 in the system. @@ -116,64 +118,13 @@ if (i3cHandle == NULL) { } ``` -#### Performing I3C Communication - -Call **I3cTransfer()** to transfer messages. -```c -int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode); -``` - -**Table 3** Description of I3cTransfer - - -| Name | Description | -| ---------- | -------------------------------------------- | -| handle | I3C controller handle. | -| msgs | Pointer to the message array of the data to transfer. | -| count | Length of the message array. | -| mode | Transmission mode. The value **0** indicates I2C mode, **1** indicates I3C mode, and **2** indicates CCC transmission. | -| **Return Value**| **Description** | -| Positive integer | The operation is successful. The number of message structures that are successfully transmitted is returned. | -| Negative value | The operation failed. | - -The I3C messages are of the I3cMsg type. Each message structure indicates a read or write operation. A message array can be used to perform multiple read or write operations. - -```c -int32_t ret; -uint8_t wbuff[2] = { 0x12, 0x13 }; -uint8_t rbuff[2] = { 0 }; -struct I3cMsg msgs[2]; /* Custom message array for transfer. */ -msgs[0].buf = wbuff; /* Data to write. */ -msgs[0].len = 2; /* Length of the data to write. */ -msgs[0].addr = 0x3F; /* Address of the device to which the data is written. */ -msgs[0].flags = 0; /* Transfer flag. A write operation is performed by default. */ -msgs[1].buf = rbuff; /* Data to read. */ -msgs[1].len = 2; /* Length of the data to read. */ -msgs[1].addr = 0x3F; /* Address of the device from which the data is read. */ -msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ is set. */ -/* Transfer two messages in I2C mode. */ -ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE); -if (ret != 2) { - HDF_LOGE("I3cTransfer: failed, ret %d\n", ret); - return; -} -``` - ->![](./public_sys-resources/icon-caution.gif) **Caution**
->- The device address in the **I3cMsg** structure does not contain the read/write flag bit. The read/write information is passed by the read/write control bit in the member variable **flags**. ->- The **I3cTransfer()** function does not limit the number of message structures or the length of data in each message structure. The I3C controller determines these two limits. ->- Using **I3cTransfer()** may cause the system to sleep. Do not call it in the interrupt context. - #### Obtaining the I3C Controller Configuration -Call **I3cGetConfig()** to obtain the configuration of an I3C controller. - ```c int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config); ``` -**Table 4** Description of I3cGetConfig - +**Table 3** Description of I3cGetConfig | Name | Description | | ---------- | -------------- | @@ -181,7 +132,7 @@ int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config); | config | Pointer to the I3C controller configuration. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | The following is an example of obtaining the I3C controller configuration: @@ -197,14 +148,11 @@ if (ret != HDF_SUCCESS) { #### Setting an I3C Controller -Call **I3cSetConfig()** to set an I3C controller. - ```c int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config); ``` -**Table 5** Description of I3cSetConfig - +**Table 4** Description of I3cSetConfig | Name | Description | | ---------- | -------------- | @@ -212,7 +160,7 @@ int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config); | config | Pointer to the I3C controller configuration. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | The following is an example of setting an I3C controller: @@ -228,9 +176,54 @@ if (ret != HDF_SUCCESS) { } ``` -#### Requesting an IBI +#### Performing I3C Communication + +Call **I3cTransfer()** to transfer messages. +```c +int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode); +``` + +**Table 5** Description of I3cTransfer + +| Name | Description | +| ---------- | -------------------------------------------- | +| handle | I3C controller handle. | +| msgs | Pointer to the message array of the data to transfer. | +| count | Length of the message array. | +| mode | Transmission mode. The value **0** indicates I2C mode, **1** indicates I3C mode, and **2** indicates CCC transmission.| +| **Return Value**| **Description** | +| Positive integer | The operation is successful. The number of message structures that are successfully transmitted is returned. | +| Negative value | The operation fails. | + +The I3C messages are of the I3cMsg type. Each message structure indicates a read or write operation. A message array can be used to perform multiple read or write operations. + +```c +int32_t ret; +uint8_t wbuff[2] = { 0x12, 0x13 }; +uint8_t rbuff[2] = { 0 }; +struct I3cMsg msgs[2]; /* Custom message array for transfer. */ +msgs[0].buf = wbuff; /* Data to write. */ +msgs[0].len = 2; /* Length of the data to write. */ +msgs[0].addr = 0x3F; /* Address of the device to which the data is written. */ +msgs[0].flags = 0; /* Transfer flag. A write operation is performed by default. */ +msgs[1].buf = rbuff; /* Data to read. */ +msgs[1].len = 2; /* Length of the data to read. */ +msgs[1].addr = 0x3F; /* Address of the device from which the data is read. */ +msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ is set. */ +/* Transfer two messages in I2C mode. */ +ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE); +if (ret != 2) { + HDF_LOGE("I3cTransfer: failed, ret %d\n", ret); + return; +} +``` + +>![](./public_sys-resources/icon-caution.gif) **Caution**
+>- The device address in the **I3cMsg** structure does not contain the read/write flag bit. The read/write information is passed by the read/write control bit in **flags**. +>- The I3C controller determines the maximum number of messages to transfer at a time and the maximum length of each message. +>- Using **I3cTransfer()** may cause the system to sleep. Do not call it in the interrupt context. -Call **I3cRequestIbi()** to request an IBI. +#### Requesting an IBI ```c int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload); @@ -238,7 +231,6 @@ int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t **Table 6** Description of I3cRequestIbi - | Name | Description | | ---------- | -------------- | | handle | I3C controller handle. | @@ -247,7 +239,7 @@ int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t | payload | IBI payload. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | The following is an example: @@ -287,22 +279,19 @@ int32_t I3cTestRequestIbi(void) #### Releasing an IBI -Call **I3cFreeIbi()** to release an IBI. - ```c int32_t I3cFreeIbi(DevHandle handle, uint16_t addr); ``` **Table 7** Description of I3cFreeIbi - | Name | Description | | ---------- | -------------- | | handle | I3C controller handle. | | addr | I3C device address. | | **Return Value**| **Description**| | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | The following is an example: @@ -319,7 +308,6 @@ void I3cClose(DevHandle handle); **Table 8** Description of I3cClose - | Name | Description | | ---------- | -------------- | | handle | I3C controller handle. | @@ -330,17 +318,15 @@ The following is an example: I3cClose(i3cHandle); /* Close the I3C controller. */ ``` -## Development Example - -This following example shows how to use I3C APIs to manage an I3C device on a Hi3516D V300 development board. +## Example -Because the Hi3516D V300 SoC has no I3C controller, this example describes how to perform simple transfer operations on a virtual driver on a Hi3516D V300. The basic information is as follows: +The following example presents how to use I3C APIs to manage an I3C device on a Hi3516D V300 development board.
The basic hardware information is as follows: - SoC: Hi3516D V300 -- Virtual: The I3C address is 0x3f, and the register bit width is 1 byte. +- Virtual I3C device: The I3C address is 0x3f, and the register bit width is 1 byte. -- The virtual I3C devices are connected to virtual I3C controllers 18 and 19. +- The virtual I3C device is connected to I3C controllers 18 and 19. Perform simple I3C transfer to test whether the I3C channels are normal. @@ -349,7 +335,7 @@ The sample code is as follows: ```c #include "i3c_if.h" /* Header file for I3C standard APIs */ #include "hdf_log.h" /* Header file for log APIs */ -##include "osal_io.h" /* Header file for I/O read and write APIs */ +##include "osal_io.h" /* Header file for I/O read and write APIs */ #include "osal_time.h" /* Header file for delay and sleep APIs */ /* Define a device structure to hold information. */ diff --git a/en/device-dev/driver/driver-platform-i3c-develop.md b/en/device-dev/driver/driver-platform-i3c-develop.md index 41119331f16f48872aabe0aade37c267e3d21220..4df734722da3a7a669a214c050797e81ae3d60cf 100644 --- a/en/device-dev/driver/driver-platform-i3c-develop.md +++ b/en/device-dev/driver/driver-platform-i3c-develop.md @@ -6,7 +6,9 @@ Improved Inter-Integrated Circuit (I3C) is a simple and cost-efficient two-wire bidirectional synchronous serial bus protocol developed by the Mobile Industry Processor Interface (MIPI) Alliance. -I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target devices and controlled by only one I3C controller at a time. It is backward compatible with Inter-Integrated circuit (I2C) target devices, but features higher speed and lower power consumption and supports in-band interrupts (IBIs), hot-joins of target devices, and controller switchover. +I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target devices and controlled by only one I3C controller at a time. It is backward compatible with Inter-Integrated circuit (I2C) target devices, but features higher speed and lower power consumption and supports in-band interrupts (IBIs), hot-joins of target devices, and controller switchover. + +The IBIs over the serial bus eliminates the need for an extra interrupt line to complete interrupts in I2C. I2C devices, I3C target devices, and the I3C secondary controller can co-exist on the same I3C bus. ### Basic Concepts @@ -16,16 +18,16 @@ I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target - Dynamic Address Assignment (DAA) - The I3C controller can dynamically allocate addresses to target devices to avoid address conflicts. Before addresses are allocated, each I3C device connected to a I3C bus must be uniquely identified in either of the following ways: + The I3C controller can dynamically allocate addresses to target devices to avoid address conflicts. Before addresses are allocated, each I3C device connected to an I3C bus must be uniquely identified in either of the following ways: - The device has an I2C compliant static address that can be used by the host. - - The device has a 48-bit temporary ID. + - The device has a 48-bit temporary ID. The host must use a 48-bit temporary ID unless the device has a static IP address. - Common Command Code (CCC) - All I3C devices support CCC. The CCC can be sent to a specific I3C target device or all I3C target devices. + All I3C devices support CCC. The CCC can be sent to an I3C target device or all I3C target devices. - Bus Characteristic Register (BCR) @@ -38,21 +40,23 @@ I3C is a two-wire bidirectional serial bus, optimized for multiple sensor target ### Working Principles -In the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than ten I3C controllers. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The figure below illustrates the unified service mode. +In the Hardware Driver Foundation (HDF), the I3C module uses the unified service mode for API adaptation. In this mode, a service is used as the I3C manager to handle external access requests in a unified manner. The unified service mode applies when the system has multiple device objects of the same type, for example, when there are more than 10 I3C controllers. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure illustrtes the unified service mode. The I3C module is divided into the following layers: -- Interface layer: provides APIs for opening or closing a controller, transmitting messages, and obtaining and setting controller parameters. -- Core layer: provides the capabilities of binding, initializing, and releasing devices. -- Adaptation layer: implements other functions. +- Interface layer: provides the capabilities of opening a device, writing data, and closing a device. +- Core layer: binds services, initializes and releases the PlatformManager, and provides the capabilities of adding, deleting, and obtaining controllers. The core layer also provides capabilities of adding, deleting, and obtaining the devices connected to the I3C bus and interrupt callbacks. +- Adaptation layer: implements hardware-related functions, such as controller initialization. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. - **Figure 1** Unified service mode + **Figure 1** Unified service mode ![image1](figures/unified-service-mode.png) ### Constraints -Currently, the I3C module supports only the kernels (LiteOS) of mini and small systems. +The I3C module supports only the kernel (LiteOS-A) for mini and small systems. ## Development Guidelines @@ -63,12 +67,13 @@ I3C can connect to one or more I3C or I2C target devices. It is used to: - Communicate with sensors, such as gyroscopes, barometers, and image sensors that support the I3C protocol. - Communicate with devices with other ports (such as UART serial ports) through software or hardware protocols. -### Available APIs - -**I3cMethod**: +Before using I3C devices with OpenHarmony, you need to adapt the I3C driver to OpenHarmony. The following describes how to do it. +### Available APIs +To enable the upper layer to successfully operate the hardware by calling the I3C APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. +**I3cMethod**: ```c struct I3cMethod { int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc); @@ -81,16 +86,17 @@ struct I3cMethod { }; ``` -**Table 1** Description of the callback functions in I3cMethod +**Table 1** Hook functions in **I3cMethod** |Function|Input Parameter|Output Parameter|Return Value|Description| |-|-|-|-|-| |sendCccCmd| **cntlr**: structure pointer to an I3C controller at the core layer.
**ccc**: pointer to the CCC to send.| **ccc**: pointer to the CCC sent.| HDF_STATUS|Sends a CCC.| -|Transfer| **cntlr**: structure pointer to an I3C controller at the core layer.
**msgs**: structure pointer to the messages to transfer.
**count**: length of the message array, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS|Transfers user messages in I3C mode.| -|i2cTransfer| **cntlr**: structure pointer to an I3C controller at the core layer.
**msgs**: structure pointer to the messages to transfer.
**count**: length of the message array, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS|Transfers user messages in I2C mode.| -|setConfig| **cntlr**: structure pointer to an I3C controller at the core layer.
**config**: pointer to the controller configuration.| –| HDF_STATUS|Sets an I3C controller.| -|getConfig| **cntlr**: structure pointer to an I3C controller at the core layer.| **config**: pointer to the controller configuration.| HDF_STATUS|Obtains the I3C controller configuration.| -|requestIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS|Requests an IBI for an I3C device.| -|freeIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS|Releases the IBI for an I3C device.| +|Transfer | **cntlr**: structure pointer to an I3C controller at the core layer.
**msgs**: structure pointer to the messages to transfer.
**count**: number of messages to transfer, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS| Transfers user messages in I3C mode.| +|i2cTransfer | **cntlr**: structure pointer to an I3C controller at the core layer.
**msgs**: structure pointer to the messages to transfer.
**count**: number of messages to transfer, which is of the int16_t type.| **msgs**: structure pointer to the messages transferred.| HDF_STATUS| Transfers user messages in I2C mode.| +|setConfig| **cntlr**: structure pointer to an I3C controller at the core layer.
**config**: pointer to the controller configuration.| –| HDF_STATUS| Sets an I3C controller.| +|getConfig| **cntlr**: structure pointer to an I3C controller at the core layer.| **config**: pointer to the controller configuration.| HDF_STATUS| Obtains the I3C controller configuration.| +|requestIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS| Requests an IBI for an I3C device.| +|freeIbi| **device**: structure pointer to an I3C device at the core layer.| –| HDF_STATUS| Releases the IBI for an I3C device.| + ### How to Develop The I3C module adaptation involves the following steps: @@ -108,65 +114,65 @@ The I3C module adaptation involves the following steps: 3. Instantiate the I3C controller object. - Initialize **I3cCntlr**. - - Instantiate **I3cMethod** in **I3cCntlr**. For details, see [Available APIs](#available-apis). + - Instantiate **I3cMethod** in **I3cCntlr**. For details, see the description of **I3cMethod** below. 4. Register an interrupt handler. Registers an interrupt handler for the controller to implement the device hot-join and IBI features. - - -## Development Example +### Example 1. Instantiate the driver entry. - The driver entry must be a global variable of the **HdfDriverEntry** type (which is defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **//drivers/hdf_core/framework/include/core/hdf_device_desc.h**), and the module name must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - + I3C driver entry example: - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > The system may have multiple I3C controllers. Therefore, you need to create a manager object in the HDF and publish a manager service to uniformly handle external access requests. Then, the manager service locates the controller to open based on the specified parameters. - > - > The core layer implements the driver of the I3C manager service. You do not need to care about the implementation. However, the **I3cCntlrAdd()** function at the core layer must be called in the implementation of **Init()** to implement related features. - + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > Multiple devices may connect to the I3C controller. In the HDF, a manager object needs to be created for this type of devices, and a manager service is published to handle external access requests uniformly. When a device needs to be started, the manager service locates the target device based on the specified parameters. + > + > You do not need to implement the driver of the I3C manager, which is implemented by the core layer. However, the **I3cCntlrAdd** function of the core layer must be invoked in the **Init** function to implement the related features. + ```c static struct HdfDriverEntry g_virtualI3cDriverEntry = { .moduleVersion = 1, .Init = VirtualI3cInit, .Release = VirtualI3cRelease, - .moduleName = "virtual_i3c_driver",// (Mandatory) The value must be the same as that in the .hcs file. + .moduleName = "virtual_i3c_driver", // (Mandatory) The value must be the same as that in the .hcs file. }; - HDF_INIT(g_virtualI3cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + HDF_INIT(g_virtualI3cDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. - /* Driver entry of the i3c_core.c manager service at the core layer */ + /* Driver entry of the i3c_core.c manager service at the core layer. */ struct HdfDriverEntry g_i3cManagerEntry = { .moduleVersion = 1, .Init = I3cManagerInit, .Release = I3cManagerRelease, - .moduleName = "HDF_PLATFORM_I3C_MANAGER",// Correspond to device0 in the device_info file. + .moduleName = "HDF_PLATFORM_I3C_MANAGER", // The value must be the same as that of device0 in the device_info.hcs file. }; HDF_INIT(g_i3cManagerEntry); ``` 2. Configure attribute files. - Add **deviceNode** to the **device_info.hcs** file, and configure the device attributes in the **i3c_config.hcs** file. The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the driver implementation and the default values or restriction ranges of the **I3cCntlr** members at the core layer. + Add the **deviceNode** information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file and configure the device attributes in **i3c_config.hcs**. - In the unified service mode, the first device node in the **device_info** file must be the I3C manager. The I3C manager parameters must be set as follows. + The **deviceNode** information is related to the driver entry registration. The device attribute values are closely related to the driver implementation and the default values or value ranges of the **I3cCntlr** members at the core layer. + + In the unified service mode, the first device node in the **device_info.hcs** file must be the I3C manager. The I3C manager parameters must be set as follows: |Parameter|Value| |-|-| |moduleName |HDF_PLATFORM_I3C_MANAGER| - |serviceName|Reserved| + |serviceName|Reserved.| |policy|0| - |cntlrMatchAttr| Reserved | + |cntlrMatchAttr| Reserved.| - Configure I3C controller information from the second node. This node specifies a type of I3C controllers rather than a specific I3C controller. In this example, there is only one I3C controller. If there are multiple I3C controllers, add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **i3c_config** file for each controller. + Configure I3C controller information from the second node. This node specifies a type of I3C controllers rather than a specific I3C controller. In this example, there is only one I3C controller. If there are multiple I3C controllers, add the **deviceNode** information to the **device_info.hcs** file and add the corresponding device attributes to the **i3c_config** file for each controller. - - **device_info.hcs** configuration example + - **device_info.hcs** example ```c root { @@ -180,17 +186,17 @@ The I3C module adaptation involves the following steps: } } i3c_virtual :: deviceNode { - policy = 0; // The value 0 indicates that no service is published. - priority = 56; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "virtual_i3c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "VIRTUAL_I3C_DRIVER"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "virtual_i3c"; // (Mandatory) Controller private data, which must be same as that of the corresponding controller in i3c_config.hcs. - } // The specific controller information is in i3c_config.hcs. + policy = 0; // The value 0 indicates that no service is published. + priority = 56; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "virtual_i3c_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "VIRTUAL_I3C_DRIVER"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "virtual_i3c"; // (Mandatory) Controller private data, which must be same as that of the controller in i3c_config.hcs. + } // The specific controller information is in i3c_config.hcs. } ``` - - i3c_config.hcs configuration example + - i3c_config.hcs example ```c root { @@ -216,208 +222,245 @@ The I3C module adaptation involves the following steps: } } ``` + After the **i3c_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + For example, if the path of **i3c_config.hcs** is **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs**, add the following statement to **hdf.hcs** of the product: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs" // Relative path of the file. + ``` + + 3. Instantiate the I3C controller object. - Initialize the **I3cCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **I3cMethod** in **I3cCntlr** (so that the underlying driver functions can be called). - - Instantiate **I3cMethod** in **I3cCntlr**. The **I3cLockMethod** callback structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in **Init()**. - - - Defining a custom structure - - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **i3c_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **I3cCntlr** object at the core layer. - - ```c - struct VirtualI3cCntlr { - struct I3cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description. - volatile unsigned char *regBase; //(Mandatory) Register base address. - uint32_t regBasePhy; // (Mandatory) Physical base address of the register. - uint32_t regSize; // (Mandatory) Bit width of the register. - uint16_t busId; // (Mandatory) Bus number. - uint16_t busMode; - uint16_t IrqNum; - uint32_t i3cMaxRate; - uint32_t i3cRate; - uint32_t i2cFmRate; - uint32_t i2cFmPlusRate; - }; - - /* I3cCntlr is the controller structure at the core layer. The Init function assigns values to the members of I3cCntlr. - struct I3cCntlr { - OsalSpinlock lock; - void *owner; - int16_t busId; - struct I3cConfig config; - uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16]; - struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX]; - const struct I3cMethod *ops; - const struct I3cLockMethod *lockOps; - void *priv; - }; - ``` - - -- **Init** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. - - **Return value**: - - **HDF_STATUS**
The table below lists some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. - - | Status| Description| - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | Invalid controller object.| - | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.| - | HDF_ERR_INVALID_PARAM | Invalid parameter.| - | HDF_ERR_IO | I/O error.| - | HDF_SUCCESS | Transmission successful.| - | HDF_FAILURE | Transmission failed.| - - **Function description**: - - Initializes the custom structure object and **I3cCntlr**, and calls the **I3cCntlrAdd** function to add the I3C controller to the core layer. - - ```c - static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) - { - int32_t ret; - struct VirtualI3cCntlr *virtual = NULL; // (Mandatory) Custom structure object. - (void)device; - - virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // (Mandatory) Allocate memory. - if (virtual == NULL) { - HDF_LOGE("%s: Malloc virtual fail!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - - ret = VirtualI3cReadDrs(virtual, node); // (Mandatory) Fill the default values defined in the i3c_config file to the structure. - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - goto __ERR__; - } - ... - virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);// (Mandatory) Address mapping. - ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); // (Mandatory) Register an interrupt handler. - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: register irq failed!", __func__); - return ret; - } - ... - VirtualI3cCntlrInit(virtual); // (Mandatory) Initialize the I3C device. - virtual->cntlr.priv = (void *)node; // (Mandatory) Set the storage device attributes. - virtual->cntlr.busId = virtual->busId; // (Mandatory) Initialize I3cCntlr. - virtual->cntlr.ops = &g_method; // (Mandatory) Attach the I3cMethod instance. - (void)OsalSpinInit(&virtual->spin); - ret = I3cCntlrAdd(&virtual->cntlr); // (Mandatory) Call this function to add the controller to the core layer. If a success signal is returned, the driver is completely connected to the core layer. - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret); - (void)OsalSpinDestroy(&virtual->spin); - goto __ERR__; - } - - return HDF_SUCCESS; - __ERR__: // If the controller fails to be added, deinitialize related functions. - if (virtual != NULL) { - OsalMemFree(virtual); - virtual = NULL; - } - - return ret; - } - - static int32_t VirtualI3cInit(struct HdfDeviceObject *device) - { - int32_t ret; - const struct DeviceResourceNode *childNode = NULL; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = VirtualI3cParseAndInit(device, childNode); - if (ret != HDF_SUCCESS) { - break; - } - } - - return ret; - } - ``` - - - -- **Release** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: + Initialize the **I3cCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate I3cMethod in I3cCntlr (so that the underlying driver functions can be called). - No value is returned. - - **Function description**: + Instantiate **I3cMethod** in **I3cCntlr**.
The **I3cLockMethod** hook function structure is not implemented in this example. To instantiate the structure, refer to the I2C driver development. Other members are initialized in **Init()**. - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + - Define a custom structure. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **i3c_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number and bus number, to the **I3cCntlr** object at the core layer. - ```c - static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node) - { - int32_t ret; - int16_t busId; - struct I3cCntlr *cntlr = NULL; - struct VirtualI3cCntlr *virtual = NULL; - struct DeviceResourceIface *drsOps = NULL; - - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return; - } - - ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read busId fail!", __func__); - return; - } - ... - /* Call I3cCntlrGet() to obtain the I3cCntlr object based on the cntlrNum of the device, and then call I3cCntlrRemove() to release the I3cCntlr object. */ - cntlr = I3cCntlrGet(busId); - if (cntlr != NULL && cntlr->priv == node) { - I3cCntlrPut(cntlr); - I3cCntlrRemove(cntlr); // (Mandatory) Remove the I3cCntlr object from the manager driver. - virtual = (struct VirtualI3cCntlr *)cntlr; // (Mandatory) Obtain the custom object through a forced conversion and perform the release operation. - (void)OsalSpinDestroy(&virtual->spin); - OsalMemFree(virtual); - } - return; - } - - static void VirtualI3cRelease(struct HdfDeviceObject *device) - { - const struct DeviceResourceNode *childNode = NULL; - - HDF_LOGI("%s: enter", __func__); - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is NULL", __func__); + ```c + struct VirtualI3cCntlr { + struct I3cCntlr cntlr; // (Mandatory) Control object at the core layer. For details, see the following description. + volatile unsigned char *regBase; // (Mandatory) Register base address. + uint32_t regBasePhy // (Mandatory) Physical base address of the register. + uint32_t regSize; // (Mandatory) Register bit width. + uint16_t busId; // (Mandatory) Bus number. + uint16_t busMode; + uint16_t IrqNum; + uint32_t i3cMaxRate; + uint32_t i3cRate; + uint32_t i2cFmRate; + uint32_t i2cFmPlusRate; + }; + + /* I3cCntlr is the controller structure at the core layer. The Init function assigns values to the members of I3cCntlr. */ + struct I3cCntlr { + OsalSpinlock lock; + void *owner; + int16_t busId; + struct I3cConfig config; + uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16]; + struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX]; + const struct I3cMethod *ops; + const struct I3cLockMethod *lockOps; + void *priv; + }; + ``` + + - Implement the **Init** function. + + **Input parameter**: + + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. + + **Return value**: + + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + + |Status|Description| + |:-|:-:| + |HDF_ERR_INVALID_OBJECT|Invalid controller object.| + |HDF_ERR_INVALID_PARAM |Invalid parameter.| + |HDF_ERR_MALLOC_FAIL |Failed to allocate the memory.| + |HDF_ERR_IO |I/O error.| + |HDF_SUCCESS |Transmission successful.| + |HDF_FAILURE |Transmission failed.| + + **Function description**: + + Initializes the custom structure object and **I3cCntlr**, and calls the **I3cCntlrAdd** function to add the I3C controller to the core layer. + + ```c + static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) + { + int32_t ret; + struct VirtualI3cCntlr *virtual = NULL; // (Mandatory) Custom structure object. + (void)device; + + virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // (Mandatory) Allocate memory. + if (virtual == NULL) { + HDF_LOGE("%s: Malloc virtual fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + ret = VirtualI3cReadDrs(virtual, node); // (Mandatory) Use the default values in the i3c_config file to fill in the structure. For details about the function definition, see the following. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); + goto __ERR__; + } + ... + virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);// (Mandatory) Address mapping. + ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); // (Mandatory) Register an interrupt handler. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: register irq failed!", __func__); + return ret; + } + ... + VirtualI3cCntlrInit(virtual); // (Mandatory) Initialize the I3C device. + virtual->cntlr.priv = (void *)node; // (Mandatory) Set the storage device attributes. + virtual->cntlr.busId = virtual->busId; // (Mandatory) Initialize I3cCntlr. + virtual->cntlr.ops = &g_method; // (Mandatory) Attach the I3cMethod instance. + (void)OsalSpinInit(&virtual->spin); + ret = I3cCntlrAdd(&virtual->cntlr); // (Mandatory) Call this function to add the controller to the core layer. The driver can access the platform core layer only when a success signal is returned. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret); + (void)OsalSpinDestroy(&virtual->spin); + goto __ERR__; + } + + return HDF_SUCCESS; + __ERR__: // If the controller fails to be added, deinitialize related functions. + if (virtual != NULL) { + OsalMemFree(virtual); + virtual = NULL; + } + + return ret; + } + + static int32_t VirtualI3cInit(struct HdfDeviceObject *device) + { + int32_t ret; + const struct DeviceResourceNode *childNode = NULL; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is NULL", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + ret = VirtualI3cParseAndInit(device, childNode); + if (ret != HDF_SUCCESS) { + break; + } + } + + return ret; + } + + static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node) + { + struct DeviceResourceIface *drsOps = NULL; + + /* Obtain the drsOps method. */ + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: Invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + /* Read the configuration parameters in sequence and fill them in the structure. */ + if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read busId fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read busMode fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read IrqNum fail!", __func__); + return HDF_ERR_IO; + } + ··· + return HDF_SUCCESS; + } + ``` + + - Implement the **Release** function. + + **Input parameter**: + + **HdfDeviceObject**, an interface parameter provided by the driver, contains the .hcs information. + + **Return value**: + + No value is returned. + + **Function description**: + + Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. + + ```c + static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node) + { + int32_t ret; + int16_t busId; + struct I3cCntlr *cntlr = NULL; + struct VirtualI3cCntlr *virtual = NULL; + struct DeviceResourceIface *drsOps = NULL; + + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return; + } + + ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read busId fail!", __func__); + return; + } + ... + /* Call I3cCntlrGet() to obtain the I3cCntlr object based on the cntlrNum of the device, and then call I3cCntlrRemove() to release the I3cCntlr object. */ + cntlr = I3cCntlrGet(busId); + if (cntlr != NULL && cntlr->priv == node) { + I3cCntlrPut(cntlr); + I3cCntlrRemove(cntlr); // (Mandatory) Remove the I3cCntlr object from the manager driver. + virtual = (struct VirtualI3cCntlr *)cntlr; // (Mandatory) Obtain the custom object through a forced conversion and perform the release operation. + (void)OsalSpinDestroy(&virtual->spin); + OsalMemFree(virtual); + } return; } - ... - // Traverse and parse all nodes in i3c_config.hcs and perform the release operation on each node. - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - VirtualI3cRemoveByNode(childNode); // See the description of VirtualI3cRemoveByNode for more details. + + static void VirtualI3cRelease(struct HdfDeviceObject *device) + { + const struct DeviceResourceNode *childNode = NULL; + + HDF_LOGI("%s: enter", __func__); + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is NULL", __func__); + return; + } + ... + /* Traverse and parse all nodes in i3c_config.hcs and perform the release operation on each node. */ + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + VirtualI3cRemoveByNode(childNode); // See the description of VirtualI3cRemoveByNode for more details. + } } - } - ``` + ``` 4. Register an interrupt handler. @@ -445,12 +488,10 @@ The I3C module adaptation involves the following steps: HDF_LOGD("%s: Reserved address which is not supported!", __func__); break; } - + return HDF_SUCCESS; } - ``` - - ```c + static int32_t I3cIbiHandle(uint32_t irq, void *data) { struct VirtualI3cCntlr *virtual = NULL; diff --git a/en/device-dev/driver/driver-platform-mmc-develop.md b/en/device-dev/driver/driver-platform-mmc-develop.md index 6b9f16227b2c0968dccf04732d54852270fd0b69..e07e4a4e5313f70ead3cd7a5442b8b41e15817e0 100644 --- a/en/device-dev/driver/driver-platform-mmc-develop.md +++ b/en/device-dev/driver/driver-platform-mmc-develop.md @@ -1,50 +1,86 @@ # MMC - ## Overview -In the Hardware Driver Foundation (HDF), the MultiMedia Card (MMC) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +A multimedia card (MMC) is a small-sized and large-capacity flash memory card used for solid-state non-volatile storage. - ![image](figures/independent-service-mode.png "MMC independent service mode") +Nowadays, MMC refers to a standard driver interface to solid-state storage cards. Memory devices that comply with this standard can be called MMCs. An MMC consists of the MMC controller, MMC bus, and memory card, which can be an MMC, Secure Digital (SD) card, Secure Digital Input Output (SDIO) card, or TransFlash (TF) card. +The MMC, SD, and SDIO buses have similar bus specifications, which have evolved from the MMC bus specifications. The MMC features multimedia storage; the SD focuses on security and data protection; the SDIO, evolving from the SD, provides the interface regardless of the specific form of the peer end (Wi-Fi, Bluetooth, or GPS device). -## Available APIs +### Basic Concepts -**MmcCntlrOps**: +- SD card + Introduced as an improvement over the MMC, the SD cards can protect their contents from erasure or modification, prevent unauthorized access, and protect copyrighted content using digital rights management. The size of a standard SD card is 24 mm x 32 mm x 2.1 mm, which is a little thicker than an MMC card. The SD cards are forward compatible with MMC cards, that is, all devices that support SD cards also support MMC cards. -``` +- SDIO + + SDIO is an interface designed as an extension for the SD card standard. It introduces the low-speed transfer standard, which supports low-speed I/O with the minimum hardware overhead. The SDIO interface is compatible with the SD cards. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the MMC uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file must be **1** or **2**, but not **0**. + +The MMC module is divided into the following layers: + +- Interface layer: provides APIs for opening an MMC device, checking whether the MMC controller has devices, and closing an MMC device. +- Core layer: provides the capabilities of adding or removing an MMC controller, performing device management, and providing common controller services. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![img1](figures/independent-service-mode.png) + +## Development Guidelines + +### When to Use + +The MMC is used to store multimedia files. Before using your MMC device with OpenHarmony, you need to perform MMC driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the MMC controller by calling the MMC APIs, hook functions are defined in **//drivers/hdf_core/framework/model/storage/include/mmc/mmc_corex.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**MmcCntlrOps**: + +```c struct MmcCntlrOps { - int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd); - int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock); - int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode); - int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width); - int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing); - int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable); - int32_t (*hardwareReset)(struct MmcCntlr *cntlr); - int32_t (*systemInit)(struct MmcCntlr *cntlr); - int32_t (*setEnhanceStrobe)(struct MmcCntlr *cntlr, bool enable); - int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt); - bool (*devReadOnly)(struct MmcCntlr *cntlr); - bool (*devPlugged)(struct MmcCntlr *cntlr); - bool (*devBusy)(struct MmcCntlr *cntlr); - int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode); - int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr); + int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd); + int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock); + int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode); + int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width); + int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing); + int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable); + int32_t (*hardwareReset)(struct MmcCntlr *cntlr); + int32_t (*systemInit)(struct MmcCntlr *cntlr); + int32_t (*setEnhanceStrobe)(struct MmcCntlr *cntlr, bool enable); + int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt); + bool (*devReadOnly)(struct MmcCntlr *cntlr); + bool (*devPlugged)(struct MmcCntlr *cntlr); + bool (*devBusy)(struct MmcCntlr *cntlr); + int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode); + int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr); }; ``` - **Table 1** Description of callback functions in MmcCntlrOps +**Table 1** Hook functions in **MmcCntlrOps** | Function| Input Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | | doRequest | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmd**: structure pointer to the command to execute.| HDF_STATUS| Processes the request.| | setClock | **cntlr**: structure pointer to the MMC controller at the core layer.
**clock**: clock frequency to set.| HDF_STATUS| Sets the clock frequency.| -| setPowerMode | **cntlr**: structure pointer to the MMC controller at the core layer.
**mode**: power consumption mode, which is an enumerated value.| HDF_STATUS| Sets the power consumption mode.| -| setBusWidth | **cntlr**: structure pointer to the MMC controller at the core layer.
**width**: bus width, which is an enumerated value.| HDF_STATUS| Sets the bus width.| -| setBusTiming | **cntlr**: structure pointer to the MMC controller at the core layer.
**timing**: bus timing, which is an enumerated value.| HDF_STATUS| Sets the bus timing.| -| setSdioIrq | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable Secure Digital Input Output (SDIO) interrupts.| HDF_STATUS| Enables or disables SDIO interrupts.| +| setPowerMode | **cntlr**: structure pointer to the MMC controller at the core layer.
**mode**: power consumption mode. For details, see **MmcPowerMode**.| HDF_STATUS| Sets the power consumption mode.| +| setBusWidth | **cntlr**: structure pointer to the MMC controller at the core layer.
**width**: bus width. For details, see **MmcBusWidth**.| HDF_STATUS| Sets the bus width.| +| setBusTiming | **cntlr**: structure pointer to the MMC controller at the core layer.
**timing**: bus timing. For details, see **MmcBusTiming**.| HDF_STATUS| Sets the bus timing.| +| setSdioIrq | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable SDIO interrupts.| HDF_STATUS| Enables or disables SDIO interrupts.| | hardwareReset | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Resets hardware.| | systemInit | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Performs system initialization.| | setEnhanceStrobe | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable the enhanced strobe feature.| HDF_STATUS| Sets the enhanced strobe feature.| @@ -52,170 +88,161 @@ struct MmcCntlrOps { | devReadOnly | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is read-only.| | cardPlugged | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is removed.| | devBusy | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is being used.| -| tune | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmdCode**: command code of the uint32_t type.| HDF_STATUS| Tunes the oscillator circuit frequency.| +| tune | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmdCode**: command code, which is of the uint32_t type.| HDF_STATUS| Tunes the oscillator circuit frequency.| | rescanSdioDev | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Scans and adds an SDIO device.| +### How to Develop -## How to Develop - -The MMC module adaptation involves the following steps: +The MMC module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **mmc_config.hcs** file. - 3. Instantiate the MMC controller object. - - Initialize **MmcCntlr**. - - Instantiate **MmcCntlrOps** in the **MmcCntlr** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **MmcCntlrOps**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, for example, check the information returned after the **MmcCntlrOps** instance is attached and whether the device starts successfully. - - -## Development Example +### Example -The following uses **himci.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.c** driver of the Hi3516D V300 development board as an example to describe the driver adaptation. 1. Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - MMC driver entry example: - - ``` + MMC driver entry example: + + ```c struct HdfDriverEntry g_mmcDriverEntry = { .moduleVersion = 1, - .Bind = HimciMmcBind, // See the Bind function. - .Init = HimciMmcInit, // See the Init function. - .Release = HimciMmcRelease, // See the Release function. - .moduleName = "hi3516_mmc_driver",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HimciMmcBind, // See the Bind function. + .Init = HimciMmcInit, // See the Init function. + .Release = HimciMmcRelease, // See the Release function. + .moduleName = "hi3516_mmc_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. }; - HDF_INIT(g_mmcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + HDF_INIT(g_mmcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **mmc_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **MmcCntlr** members at the core layer. - - If there are multiple devices, you need to add the **deviceNode** information to the **device_info** file and add the device attributes to the **mmc_config** file for each device. - - - **device_info.hcs** configuration example - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_mmc:: device { - device0 :: deviceNode { - policy = 2; - priority = 10; - permission = 0644; - moduleName = "hi3516_mmc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HDF_PLATFORM_MMC_0"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hi3516_mmc_emmc";// (Mandatory) Private data of the controller. The value must be the same as the controller information in mmc_config.hcs. - } - device1 :: deviceNode { - policy = 1; - priority = 20; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_1"; - deviceMatchAttr = "hi3516_mmc_sd"; // The MMC is an SD card. - } - device2 :: deviceNode { - policy = 1; - priority = 30; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_2"; - deviceMatchAttr = "hi3516_mmc_sdio";// The MMC is an SDIO card. - } - } - } - } - } - ``` - - - **mmc_config.hcs** configuration example - - - ``` - root { - platform { - mmc_config { - template mmc_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes. - match_attr = ""; - voltDef = 0; // 3.3V - freqMin = 50000; // (Mandatory) Minimum frequency - freqMax = 100000000; // (Mandatory) Maximum frequency - freqDef = 400000; // (Mandatory) Default frequency - maxBlkNum = 2048; // (Mandatory) Maximum block number - maxBlkSize = 512; // (Mandatory) Maximum number of blocks - ocrDef = 0x300000; // (Mandatory) Working voltage. - caps2 = 0; // (Mandatory) Attribute register. For details, see MmcCaps2 in mmc_caps.h. - regSize = 0x118; // (Mandatory) Register bit width - hostId = 0; // (Mandatory) Host ID - regBasePhy = 0x10020000;// (Mandatory) Physical base address of the register - irqNum = 63; // (Mandatory) Interrupt number - devType = 2; // (Mandatory) Device type, which can be eMMC, SD, SDIO, or COMBO. - caps = 0x0001e045; // (Mandatory) Attribute register. For details, see MmcCaps in mmc_caps.h. - } - controller_0x10100000 :: mmc_controller { - match_attr = "hi3516_mmc_emmc";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - hostId = 0; - regBasePhy = 0x10100000; - irqNum = 96; - devType = 0; // The MMC is an eMMC card. - caps = 0xd001e045; - caps2 = 0x60; - } - controller_0x100f0000 :: mmc_controller { - match_attr = "hi3516_mmc_sd"; - hostId = 1; - regBasePhy = 0x100f0000; - irqNum = 62; - devType = 1; // The MMC is an SD card. - caps = 0xd001e005; - } - controller_0x10020000 :: mmc_controller { - match_attr = "hi3516_mmc_sdio"; - hostId = 2; - regBasePhy = 0x10020000; - irqNum = 63; - devType = 2; // The MMC is an SDIO card. - caps = 0x0001e04d; - } - } - } - } - ``` - -3. Initialize the **MmcCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **MmcCntlrOps** in **MmcCntlr** (so that the underlying driver functions can be called). - - - Defining a custom structure +2. Configure attribute files. - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **mmc_config.hcs** file to initialize the members in the custom structure and passes important parameters to the **MmcCntlr** object at the core layer. + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses three MMC controllers as an example. If there are more MMC controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **mmc_config.hcs** are closely related to the default values or value ranges of the **MmcCntlr** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_mmc:: device { + device0 :: deviceNode { // DeviceNode of the driver. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi3516_mmc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HDF_PLATFORM_MMC_0"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hi3516_mmc_emmc"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in mmc_config.hcs. + } + device1 :: deviceNode { + policy = 1; + priority = 20; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_1"; + deviceMatchAttr = "hi3516_mmc_sd"; // The MMC is an SD card. + } + device2 :: deviceNode { + policy = 1; + priority = 30; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_2"; + deviceMatchAttr = "hi3516_mmc_sdio"; // The MMC is an SDIO card. + } + ... + } + } + } + } + ``` - + - **mmc_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + mmc_config { + template mmc_controller { // Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + match_attr = ""; + voltDef = 0; // MMC default voltage. The value 0 stands for 3.3 V, 1 for 1.8 V, and 2 for 1.2 V. + freqMin = 50000; // (Mandatory) Minimum frequency. + freqMax = 100000000; // (Mandatory) Maximum frequency. + freqDef = 400000; // (Mandatory) Default frequency. + maxBlkNum = 2048; // (Mandatory) Maximum block number. + maxBlkSize = 512; // (Mandatory) Maximum block size. + ocrDef = 0x300000; // (Mandatory) working voltage. + caps2 = 0; // (Mandatory) Attribute register. For details, see MmcCaps2 in mmc_caps.h. + regSize = 0x118; // (Mandatory) Register size. + hostId = 0; // (Mandatory) Host number. + regBasePhy = 0x10020000; // (Mandatory) Physical base address of the register. + irqNum = 63; // (Mandatory) IRQ number. + devType = 2; // (Mandatory) Device type, which can be eMMC, SD, SDIO, or COMBO. + caps = 0x0001e045; // (Mandatory) Attribute register. For details, see MmcCaps in mmc_caps.h. + } + controller_0x10100000 :: mmc_controller { + match_attr = "hi3516_mmc_emmc"; // (Mandatory) The value must be the same as deviceMatchAttr in device_info.hcs. + hostId = 0; + regBasePhy = 0x10100000; + irqNum = 96; + devType = 0; // The device is an eMMC card. + caps = 0xd001e045; + caps2 = 0x60; + } + controller_0x100f0000 :: mmc_controller { + match_attr = "hi3516_mmc_sd"; + hostId = 1; + regBasePhy = 0x100f0000; + irqNum = 62; + devType = 1; // The device is an SD card. + caps = 0xd001e005; + } + controller_0x10020000 :: mmc_controller { + match_attr = "hi3516_mmc_sdio"; + hostId = 2; + regBasePhy = 0x10020000; + irqNum = 63; + devType = 2; // The device is an SDIO card. + caps = 0x0001e04d; + } + } + } + } + ``` + + After the **mmc_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // Relative path of the file. ``` + +3. Instantiate the MMC controller object. + + Initialize the **MmcCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **MmcCntlrOps** in **MmcCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **mmc_config.hcs** file to initialize the members in the custom structure and passes important parameters to the **MmcCntlr** object at the core layer. + + ```c struct HimciHost { - struct MmcCntlr *mmc;// (Mandatory) Core layer structure - struct MmcCmd *cmd; // (Mandatory) Core layer structure used to pass commands. For details about related commands, see MmcCmdCode. - //(Optional) Set parameters based on actual requirements. - void *base; + struct MmcCntlr *mmc; // (Mandatory) Core layer control object. + struct MmcCmd *cmd // (Mandatory) Core layer structure used to pass commands. For details about related commands, see MmcCmdCode. + void *base; // Register base address used for address mapping. enum HimciPowerStatus powerStatus; uint8_t *alignedBuff; uint32_t buffLen; @@ -260,37 +287,39 @@ The following uses **himci.c** as an example to present the information required }; ``` - - Instantiating **MmcCntlrOps** in **MmcCntlr** (other members are initialized by **Bind**) + - Instantiate **MmcCntlrOps** in **MmcCntlr**. - - ``` + ```c static struct MmcCntlrOps g_himciHostOps = { - .request = HimciDoRequest, - .setClock = HimciSetClock, - .setPowerMode = HimciSetPowerMode, - .setBusWidth = HimciSetBusWidth, - .setBusTiming = HimciSetBusTiming, - .setSdioIrq = HimciSetSdioIrq, - .hardwareReset = HimciHardwareReset, - .systemInit = HimciSystemInit, - .setEnhanceStrobe= HimciSetEnhanceStrobe, - .switchVoltage = HimciSwitchVoltage, - .devReadOnly = HimciDevReadOnly, - .devPlugged = HimciCardPlugged, - .devBusy = HimciDevBusy, - .tune = HimciTune, - .rescanSdioDev = HimciRescanSdioDev, + .request = HimciDoRequest, + .setClock = HimciSetClock, + .setPowerMode = HimciSetPowerMode, + .setBusWidth = HimciSetBusWidth, + .setBusTiming = HimciSetBusTiming, + .setSdioIrq = HimciSetSdioIrq, + .hardwareReset = HimciHardwareReset, + .systemInit = HimciSystemInit, + .setEnhanceStrobe = HimciSetEnhanceStrobe, + .switchVoltage = HimciSwitchVoltage, + .devReadOnly = HimciDevReadOnly, + .devPlugged = HimciCardPlugged, + .devBusy = HimciDevBusy, + .tune = HimciTune, + .rescanSdioDev = HimciRescanSdioDev, }; ``` - - **Bind** function + + - Implement the **Bind** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS description | Status| Description| | -------- | -------- | @@ -303,10 +332,9 @@ The following uses **himci.c** as an example to present the information required **Function description**: - Initializes the custom structure **HimciHost** object and **MmcCntlr**, and calls the **MmcCntlrAdd** function at the core layer. **MmcCntlr**, **HimciHost**, and **HdfDeviceObject** assign values with each other so that other functions can be converted successfully. + Initializes the **HimciHost** object and **MmcCntlr**, and calls the **MmcCntlrAdd** function at the core layer to add the MMC controllers. - - ``` + ```c static int32_t HimciMmcBind(struct HdfDeviceObject *obj) { struct MmcCntlr *cntlr = NULL; @@ -315,34 +343,34 @@ The following uses **himci.c** as an example to present the information required cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr)); host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost)); - host->mmc = cntlr; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. - cntlr->priv = (void *)host; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. - cntlr->ops = &g_himciHostOps; // (Mandatory) Attach the MmcCntlrOps instance. - cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. - obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. - ret = MmcCntlrParse(cntlr, obj); // (Mandatory) Initialize MmcCntlr. If the initialization fails, execute goto _ERR. - ... - ret = HimciHostParse(host, obj); // (Mandatory) Initialize HimciHost. If the initialization fails, execute goto _ERR. + host->mmc = cntlr; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. + cntlr->priv = (void *)host; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. + cntlr->ops = &g_himciHostOps; // (Mandatory) Attach the MmcCntlrOps instance to MmcCntlr. + cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. + obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. + ret = MmcCntlrParse(cntlr, obj); // (Mandatory) Initialize MmcCntlr. If the initialization fails, execute goto _ERR. + ... + ret = HimciHostParse(host, obj); // (Mandatory) Initialize HimciHost. If the initialization fails, execute goto _ERR. ... - ret = HimciHostInit(host, cntlr); // Customized initialization. If the initialization fails, execute goto _ERR. + ret = HimciHostInit(host, cntlr); // Customized initizlization. If the initialization fails, goto _ERR. ... - ret = MmcCntlrAdd(cntlr); // Call the functions at the core layer. If the operation fails, execute goto _ERR. + ret = MmcCntlrAdd(cntlr); // Call MmcCntlrAdd at the core layer. If the operation fails, execute goto _ERR. ... - (void)MmcCntlrAddDetectMsgToQueue(cntlr);// Add the card detection message to the queue. + (void)MmcCntlrAddDetectMsgToQueue(cntlr); // Add the card detection message to the queue. HDF_LOGD("HimciMmcBind: success."); return HDF_SUCCESS; - _ERR: + ERR: HimciDeleteHost(host); HDF_LOGD("HimciMmcBind: fail, err = %d.", ret); return ret; } ``` - - **Init** function + - Implement the **Init** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -352,8 +380,7 @@ The following uses **himci.c** as an example to present the information required Implements **ProcMciInit**. - - ``` + ```c static int32_t HimciMmcInit(struct HdfDeviceObject *obj) { static bool procInit = false; @@ -368,11 +395,12 @@ The following uses **himci.c** as an example to present the information required return HDF_SUCCESS; } ``` - - **Release** function + + - Implement the **Release** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -380,19 +408,22 @@ The following uses **himci.c** as an example to present the information required **Function description**: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - static void HimciMmcRelease(struct HdfDeviceObject *obj) + ```c + static void HimciMmcRelease(struct HdfDeviceObject *obj) { struct MmcCntlr *cntlr = NULL; ... - cntlr = (struct MmcCntlr *)obj->service; // Forcibly convert HdfDeviceObject to MmcCntlr by using service. For details about the value assignment, see the Bind function. + cntlr = (struct MmcCntlr *)obj->service; // Forcibly convert HdfDeviceObject to MmcCntlr by using service. For details about the value assignment, see the Bind function. ... - HimciDeleteHost((struct HimciHost *)cntlr->priv);// Customized memory release function. A forced conversion from MmcCntlr to HimciHost is involved in the process. + HimciDeleteHost((struct HimciHost *)cntlr->priv); // Memory release function customized. Forced conversion between MmcCntlr and HimciHost is involved. } + ``` +4. Debug the driver. + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-pin-des.md b/en/device-dev/driver/driver-platform-pin-des.md index 607807085a5334b9b975e840c44406ad3992ad06..fc520123f52559e08bea90ec8659553c21f4976d 100644 --- a/en/device-dev/driver/driver-platform-pin-des.md +++ b/en/device-dev/driver/driver-platform-pin-des.md @@ -1,103 +1,106 @@ -# Pin +# Pin +## Overview -## Overview +### Function -### Pin +The pin module, also called pin controller, manages pin resources of the system on a chip (SoC) and implements the pin multiplexing function. -The pin module, also called pin controller, manages pin resources of system on a chip (SoC) vendors and provides the pin multiplexing function. -The module defines a set of common methods for managing pins, including: - - Obtaining or releasing the pin description handle: The kernel compares the pin name passed in with the pin names of each controller in the linked list. If a match is found, a pin description handle is obtained. After the operation on the pin is complete, the pin description handle will be released. -- Setting or obtaining the pull type of a pin: The pull type can be pull-up, pull-down, or floating. -- Setting or obtaining the pull strength of a pin: You can set the pull strength as required. -- Setting or obtaining the functions of a pin to implement pin multiplexing +The pin module provides a set of APIs for pin management, including: -### Basic Concepts -Pin, as a software concept, provides APIs for uniformly managing the pins from different SoC vendors, providing the pin multiplexing function, and configuring the electrical features of pins. +- Obtaining or releasing a pin description handle +- Setting or obtaining the pull type (pull-up, pull-down, or floating) of a pin +- Setting or obtaining the pull strength of a pin +- Setting or obtaining the function of a pin to implement pin multiplexing + +### Basic Concepts + +Pin is a software concept designed to uniformly manage SoC pins, implement pin multiplexing, and set electrical features of pins. - SoC - An SOC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. + An SoC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. - Pin multiplexing - When the number of pins of a chip cannot handle the increasing connection requests, you can set the software registers to make the pins to work in different states. + When the number of pins of a chip cannot handle the increasing connection requests, you can set software registers to make the pins to work in different states. + +### Working Principles -### Working Principles +In the Hardware Driver Foundation (HDF), the pin module uses the unified service mode for API adaptation. In this mode, a device service is used as the pin manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. -In the HDF, the pin module does not support the user mode and therefore does not need to publish services. It uses the service-free mode in interface adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the OS that does not distinguish the user mode and the kernel mode. The **DevHandle**, a void pointer, directly points to the kernel mode address of the device object. +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. The pin module is divided into the following layers: -- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and functions of a pin, and releasing a pin. -- Core layer: provides the capabilities of matching pin resources and adding, removing, and managing pin controllers. The core layer interacts with the adaptation layer by using hooks. -- Adaptation layer: instantiates hooks to implement specific functions. -**Figure 1** Service-free mode -![](figures/service-free-mode.png "pin service-free mode") +- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and function of a pin, and releasing a pin. +- Core layer: provides the capabilities of matching pin resources, adding and removing a pin controller, and managing pins. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -### Constraints +**Figure 1** Unified service mode +![Unified service mode](figures/unified-service-mode.png) - Currently, the pin module supports only the kernels (LiteOS) of mini and small systems. +### Constraints - ## Usage Guidelines +The pin module supports only the LiteOS-A kernel of the small system. - ### When to Use +## Usage Guidelines - The pin module is a software concept and is used to manage pin resources. You can set the functions, pull type, and pull strength of pins to implement pin multiplexing. +### When to Use -### Available APIs +The pin module is a software concept used to manage pin resources. You can set the function, pull type, and pull strength of pins to implement pin multiplexing. -The table below describes the APIs of the pin module. For more details, see API Reference. +### Available APIs + +The following table describes the APIs of the pin module. **Table 1** Pin driver APIs - -| **API** | **Description** | +| **API** | **Description** | | ------------------------------------------------------------ | ---------------- | -| DevHandle PinGet(const char *pinName); | Obtains the pin description handle.| -| void PinPut(DevHandle handle); | Releases the pin description handle.| -| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); | Sets the pull type of a pin.| -| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); | Obtains the pull type of a pin.| -| int32_t PinSetStrength(DevHandle handle, uint32_t strength); | Sets the pull strength of a pin.| -| int32_t PinGetStrength(DevHandle handle, uint32_t *strength); | Obtains the pull strength of a pin.| -| int32_t PinSetFunc(DevHandle handle, const char *funcName); | Sets the pin function. | -| int32_t PinGetFunc(DevHandle handle, const char **funcName); | Obtains the pin functions. | +| DevHandle PinGet(const char *pinName) | Obtains a pin description handle.| +| void PinPut(DevHandle handle) | Releases the pin description handle.| +| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType) | Sets the pull type of a pin.| +| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType) | Obtains the pull type of a pin.| +| int32_t PinSetStrength(DevHandle handle, uint32_t strength) | Sets the pull strength of a pin.| +| int32_t PinGetStrength(DevHandle handle, uint32_t *strength) | Obtains the pull strength of a pin.| +| int32_t PinSetFunc(DevHandle handle, const char *funcName) | Sets the pin function.| +| int32_t PinGetFunc(DevHandle handle, const char **funcName) | Obtains the pin function. | ->![](../public_sys-resources/icon-note.gif) **NOTE**
->All APIs described in this document can be called only in the kernel space. +>![](../public_sys-resources/icon-note.gif) **NOTE** +> +>All the pin APIs described in this document can be used in kernel mode and user mode. -### How to Develop +### How to Develop -The figure below illustrates how to use the APIs. +The following figure illustrates how to use pin driver APIs. - **Figure 2** Using the pin driver APIs - + **Figure 2** Using pin driver APIs
![](figures/using-pin-process.png "Process of using the pin module") -#### Obtaining the Pin Description Handle +#### Obtaining a Pin Description Handle -Before performing an operation on a pin, call **PinGet** to obtain the pin description handle. This API returns the pin description handle that matches the input pin name. +Before performing an operation on a pin, use **PinGet()** to obtain the pin description handle based on the pin name. -``` +```c DevHandle PinGet(const char *pinName); ``` **Table 2** Description of PinGet - - | Parameter | Description | | ---------- | ----------------------- | | pinName | Pointer to the pin name. | -| **Return Value**| **Description** | -| NULL | Failed to obtain the pin description handle.| -| handle | Pin description handle obtained. | +| **Return Value**| **Description** | +| NULL | The operation fails.| +| handle | The operation is successful. The pin description handle obtained is returned. | -Example: Obtain the handle of P18. +Example: Obtain the pin description handle of P18. -``` -DevHandle handle = NULL; /* Pin description handle */ -char pinName = "P18"; /* Pin name. */ +```c +DevHandle handle = NULL; // Pin description handle. + +char pinName = "P18"; // Pin name. handle = PinGet(pinName); if (handle == NULL) { HDF_LOGE("PinGet: get handle failed!\n"); @@ -107,29 +110,28 @@ if (handle == NULL) { #### Setting the Pull Type of a Pin -Call **PinSetPull** to set the pull type of a pin. +Use **PinSetPull()** to set the pull type of a pin. -``` +```c int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); ``` **Table 3** Description of PinSetPull - - -| Parameter | Description | +| Parameter | Description | | ---------- | ----------------------- | | handle | Pin description handle. | | pullType | Pull type to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pull type to pull-up. -``` +```c int32_t ret; enum PinPullType pullTypeNum; + /* Set the pull type of a pin. */ pullTypeNum = 1; ret = PinSetPull(handle, pullTypeNum); @@ -141,29 +143,28 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pull Type of a Pin -Call **PinGetPull** to obtain the pull type of a pin. +Use **PinGetPull()** to obtain the pull type of a pin. -``` +```c int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); ``` **Table 4** Description of PinGetPull - - | Parameter | Description | | ---------- | ------------------------- | | handle | Pin description handle. | | pullType | Pointer to the pull type obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pull type of a pin. -``` +```c int32_t ret; enum PinPullType pullTypeNum; + /* Obtain the pull type of a pin. */ ret = PinGetPull(handle, &pullTypeNum); if (ret != HDF_SUCCESS) { @@ -174,27 +175,25 @@ if (ret != HDF_SUCCESS) { #### Setting the Pull Strength of a Pin -Call **PinSetStrength** to set the pull type of a pin. +Use **PinSetStrength()** to set the pull type of a pin. -``` +```c int32_t PinSetStrength(DevHandle handle, uint32_t strength); ``` **Table 5** Description of PinSetStrength - - | Parameter | Description | | ---------- | ----------------------- | | handle | Pin description handle. | | strength | Pull strength to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pull strength of the pin to 2. -``` +```c int32_t ret; uint32_t strengthNum; /* Set the pull strength of the pin. */ @@ -208,29 +207,28 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pull Strength of a Pin -Call **PinGetStrength** to obtain the pull strength set. +Use **PinGetStrength()** to obtain the pull strength of a pin. -``` +```c int32_t PinGetStrength(DevHandle handle, uint32_t *strength); ``` **Table 6** Description of PinGetStrength - - | Parameter | Description | | ---------- | ------------------------- | | handle | Pin description handle. | | strength | Pointer to the pull strength obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pull strength of a pin. -``` +```c int32_t ret; uint32_t strengthNum; + /* Obtain the pull strength of the pin. */ ret = PinGetStrength(handle, &strengthNum); if (ret != HDF_SUCCESS) { @@ -241,31 +239,30 @@ if (ret != HDF_SUCCESS) { #### Setting the Pin Function -The pin function refers to the pin multiplexing function. The function of each pin is different. For details about the pin functions, see [pin_config.hcs](https://gitee.com/openharmony/device_soc_hisilicon/blob/master/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs). +The pin function refers to the multiplexed pin function. The function of each pin is different. For details about the pin function name, see **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs**. -Call **PinSetFunc** to set the pin function. +Use **PinSetFunc()** to set the pin function. -``` +```c int32_t PinSetFunc(DevHandle handle, const char *funcName); ``` **Table 7** Description of PinSetFunc - - | Parameter | Description | | ---------- | ------------------- | | handle | Pin description handle. | | funcName | Pointer to the pin function to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pin function to LSADC_CH1 (ADC channel 1). -``` +```c int32_t ret; char funcName = "LSADC_CH1"; + /* Sets the pin function. */ ret = PinSetFunc(handle, funcName); if (ret != HDF_SUCCESS) { @@ -276,31 +273,30 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pin Function -Call **PinGetFunc** to obtain the pin function set. +Use **PinGetFunc()** to obtain the pin function. -``` +```c int32_t PinGetFunc(DevHandle handle, const char **funcName); ``` **Table 8** Description of PinGetFunc - - | Parameter | Description | | ---------- | --------------------- | | handle | Pin description handle. | | funcName | Pointer to the function name obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pin function. -``` +```c int32_t ret; -char *funcName; +char *funcName = NULL; + /* Obtain the pin function. */ -ret = PinGetFunc(handle,&funcName); +ret = PinGetFunc(handle, &funcName); if (ret != HDF_SUCCESS) { HDF_LOGE("PinGetFunc: failed, ret %d\n", ret); return ret; @@ -309,16 +305,14 @@ if (ret != HDF_SUCCESS) { #### Releasing a Pin Description Handle -After the operations on a pin are complete, call **PinPut** to release the pin description handle. +After the operations on a pin are complete, use **PinPut()** to release the pin description handle. -``` +```c void PinPut(DevHandle handle); ``` **Table 9** Description of PinPut - - | Parameter | Description | | ---------- | -------------- | | handle | Pin description handle. | @@ -327,13 +321,14 @@ void PinPut(DevHandle handle); Example: Release a pin description handle. -``` +```c PinPut(handle); ``` -## Development Example +## Example + +The following uses the Hi3516D V300 development board as an example. The procedure is as follows: -The procedure is as follows: 1. Pass in the pin name to obtain the pin description handle. 2. Set the pull type of the pin. If the operation fails, release the pin description handle. 3. Obtain the pull type of the pin. If the operation fails, release the pin description handle. @@ -343,9 +338,9 @@ The procedure is as follows: 6. Obtain the pin function. If the operation fails, release the pin description handle. 7. Release the pin description handle if no operation needs to be performed on the pin. -``` -#include "hdf_log.h" /* Header file for log APIs */ -#include "pin_if.h" /* Header file for standard pin APIs */ +```c +#include "hdf_log.h" /* Header file of the HDF log APIs. */ +#include "pin_if.h" /* Header file of standard pin APIs. */ int32_t PinTestSample(void) { @@ -361,7 +356,7 @@ int32_t PinTestSample(void) /* Obtain the pin description handle. */ handle = PinGet(pinName); if (handle == NULL) { - HDF_LOGE("PinGet: failed!\n"); + HDF_LOGE("PinGet: pin get failed!\n"); return; } /* Set the pull type to pull-up for the pin. */ @@ -407,4 +402,5 @@ ERR: /* Release the pin description handle. */ PinPut(handle); return ret; -} \ No newline at end of file +} +``` diff --git a/en/device-dev/driver/driver-platform-pin-develop.md b/en/device-dev/driver/driver-platform-pin-develop.md index 9417dc54ae6ca33ae46941bfe970d87fd7c5c3af..9f34c362f61978f8957e2a7e90d623ac60b3c4a6 100644 --- a/en/device-dev/driver/driver-platform-pin-develop.md +++ b/en/device-dev/driver/driver-platform-pin-develop.md @@ -1,49 +1,54 @@ # Pin - ## Overview -### Pin -The pin, also called pin controller, manages pin resources of system on a chip (SoC) vendors and provides the pin multiplexing function. +### Function + +The pin module, also called pin controller, manages pin resources of the system on a chip (SoC) and implements the pin multiplexing function. ### Basic Concepts -Pin, as a software concept, provides APIs for uniformly managing the pins from different SoC vendors, providing the pin multiplexing function, and configuring the electrical features of pins. +Pin is a software concept designed to uniformly manage SoC pins, implement pin multiplexing, and set electrical features of pins. - SoC - An SOC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. + An SoC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. - Pin multiplexing - When the number of pins of a chip cannot handle the increasing connection requests, you can set the software registers to make the pins to work in different states. + When the number of pins of a chip cannot handle the increasing connection requests, you can set software registers to make the pins to work in different states. ### Working Principles -In the HDF, the pin module does not support the user mode and therefore does not need to publish services. The pin module uses the service-free mode (as shown in Figure 1) in interface adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the OS that does not distinguish the user mode and the kernel mode. The **DevHandle**, a void pointer, directly points to the kernel mode address of the device object. +In the Hardware Driver Foundation (HDF), the pin module uses the unified service mode for API adaptation. In this mode, a device service is used as the pin manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. The pin module is divided into the following layers: -- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and functions of a pin, and releasing a pin. -- Core layer: provides the capabilities of matching pin resources and adding, removing, and managing pin controllers. The core layer interacts with the adaptation layer by using hooks. -- Adaptation layer: instantiates hooks to implement specific functions. -**Figure 1** Service-free mode +- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and function of a pin, and releasing a pin. +- Core layer: provides APIs for matching pin resources, adding and removing a pin controller, and managing pins. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -![](figures/service-free-mode.png) +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) ### Constraints -Currently, the pin module supports only the kernels (LiteOS) of mini and small systems. +The pin module supports only the LiteOS-A kernel of the small system. ## Development Guidelines ### When to Use -The pin module is used to manage pin resources. When the devices from SoC vendors interconnect with the HDF, the pin driver needs to be adapted. +The pin module is used to manage pin resources. Before using the SoC with the HDF, you need to perform PIN driver adaptation. ### Available APIs -The **PinCntlrMethod** structure defines callbacks to be invoked to call the functions of the pin driver. +To enable the upper layer to successfully operate pins by calling the pin driver APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/pin/pin_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**PinCntlrMethod**: ```c struct PinCntlrMethod { @@ -56,385 +61,410 @@ struct PinCntlrMethod { }; ``` -**Table 1** Callbacks in the PinCntlrMethod structure +**Table 1** Hook functions in PinCntlrMethod -| API | Input Parameter | Output Parameter | Return Value| Description| +| Function | Input Parameter | Output Parameter | Return Value| Description| | ------------ | ------------------------------------------- | ------ | ---- | ---- | -| SetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**pullType**: pull type of the pin. It is an enum constant.| -|HDF_STATUS|Sets the pull type of a pin.| -| GetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **pullType**: pointer to the pull type of the pin.| HDF_STATUS| Obtains the pull type of a pin.| -| SetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**strength**: pull strength of the pin. It is a uint32_t variable.| -| HDF_STATUS| Sets the pull strength of a pin.| -| GetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **strength**: pointer to the pull strength of the pin.| HDF_STATUS| Obtains the pull strength of a pin.| -| SetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**funcName**: char pointer to the pin function.| -| HDF_STATUS| Sets the pin function.| -| GetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **funcName**: char double pointer to the pin function.| HDF_STATUS| Obtains the pin function.| +| SetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**pullType**: pull type of the pin.| -|HDF_STATUS|Sets the pull type of a pin.| +| GetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **pullType**: pointer to the pull type obtained.| HDF_STATUS| Obtains the pull type of a pin.| +| SetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**strength**: pull type to set, which is a uint32_t variable.| -| HDF_STATUS| Sets the pull strength of a pin.| +| GetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **strength**: pointer to the pull strength obtained.| HDF_STATUS| Obtains the pull strength of a pin.| +| SetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**funcName**: pointer to the pin function to set. It is a character constant.| -| HDF_STATUS| Sets the pin function.| +| GetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **funcName**: double pointer to the PIN function obtained. It is a character constant.| HDF_STATUS| Obtains the pin function.| ### How to Develop The pin module adaptation procedure is as follows: -1. Instantiate the driver entry. -2. Configure attribute files. -3. Instantiate the core layer APIs. -4. Debug the driver. - -### Development Example +- Instantiate the driver entry. +- Configure attribute files. +- Instantiate the pin controller object. +- Debug the driver. -1. Instantiate the driver entry. +### Example - - Instantiate the **HdfDriverEntry** structure. +The following uses the **//device_soc_hisilicon/common/platform/pin/pin_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the pin driver adaptation. - Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. +1. Instantiate the driver entry. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. - Generally, the HDF calls the **Init** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + PIN driver entry example: - ```c - static struct HdfDriverEntry g_hi35xxPinDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxPinBind, - .Init = Hi35xxPinInit, - .Release = Hi35xxPinRelease, - .moduleName = "hi35xx_pin_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. - }; - HDF_INIT(g_hi35xxPinDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. - ``` + ```c + static struct HdfDriverEntry g_hi35xxPinDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxPinBind, + .Init = Hi35xxPinInit, + .Release = Hi35xxPinRelease, + .moduleName = "hi35xx_pin_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + }; + HDF_INIT(g_hi35xxPinDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + ``` 2. Configure attribute files. - - Add the device node description to the **vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. - - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pin :: device { - device0:: deviceNode { // Set an HDF device node for each pin controller. - policy = 0; // Policy for publishing services. - priority = 10; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - /* (Mandatory) Driver name, which must be the same as the moduleName in the driver entry. */ - moduleName = "hi35xx_pin_driver"; - /* (Mandatory) Set the controller private data, which must be same as that in pin_config.hcs. */ - deviceMatchAttr = "hisilicon_hi35xx_pin_0"; - } - device1 :: deviceNode { - policy = 0; - priority = 10; - permission = 0644; - moduleName = "hi35xx_pin_driver"; - deviceMatchAttr = "hisilicon_hi35xx_pin_1"; - } - ... - } - } - } - } - ``` - - Add the **pin_config.hcs** file. - - Configure the device attributes in the **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs** file. The parameters are set as follows: - ```c - root { - platform { - pin_config_hi35xx { - template pin_controller { // (Mandatory) Template configuration. In the template, you can configure the common parameters shared by device nodes. - number = 0; // (Mandatory) Controller ID. - regStartBasePhy = 0; // (Mandatory) Start physical base address of the register. - regSize = 0; // (Mandatory) Register bit width. - PinCount = 0; // (Mandatory) Number of pins. - match_attr = ""; - template pin_desc { - pinName = ""; // (Mandatory) Name of the pin. - init = 0; // (Mandatory) Default value of the register. - F0 = ""; // (Mandatory) Function 0. - F1 = ""; // Function 1. - F2 = ""; // Function 2. - F3 = ""; // Function 3. - F4 = ""; // Function 4. - F5 = ""; // Function 5. - } - } - controller_0 :: pin_controller { - number = 0; - regStartBasePhy = 0x10FF0000; - regSize = 0x48; - pinCount = 18; - match_attr = "hisilicon_hi35xx_pin_0"; - T1 :: pin_desc { - pinName = "T1"; - init = 0x0600; - F0 = "EMMC_CLK"; - F1 = "SFC_CLK"; - F2 = "SFC_BOOT_MODE"; - } - ... // Correspond to the pins of the pin controller. Add pins according to actual situation. - } - ... // Each pin controller corresponds to a controller node. If there are multiple pin controllers, add the corresponding controller nodes one by one. - } - } - } - ``` + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two pin controllers as an example. If there are more pin controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **pin_config.hcs** are closely related to the driver implementation and default values or value ranges of the **PinCntlr** members at the core layer. + + - **device_info.hcs** example + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pin :: device { + device0 :: deviceNode { // Used to manage pins and release services in a unified manner. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 8; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_PIN_MANAGER"; + serviceName = "HDF_PLATFORM_PIN_MANAGER"; + } + device1:: deviceNode { // Configure an HDF device node for each pin controller. + policy = 0; + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi35xx_pin_driver"; // (mandatory) Driver name, which must be the same as moduleName in the driver entry. + deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // (Mandatory) Controller private data, which must be the same as that of the controller in pin_config.hcs. + } + device2 :: deviceNode { + policy = 0; + priority = 10; + permission = 0644; + moduleName = "hi35xx_pin_driver"; + deviceMatchAttr = "hisilicon_hi35xx_pin_1"; + } + ... + } + } + } + } + ``` + + - **pin_config.hcs** example: + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + pin_config_hi35xx { + template pin_controller { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + number = 0; // (Mandatory) PIN controller number. + regStartBasePhy = 0; // (Mandatory) Start address of the physical base address of the register. + regSize = 0; // (Mandatory) Register size. + pinCount = 0; // (Mandatory) Number of pins. + match_attr = ""; + template pin_desc { + pinName = ""; // (Mandatory) Pin name. + init = 0; // (Mandatory) Default value of the register. + F0 = ""; // (Mandatory) Function 0. + F1 = ""; // Function 1. + F2 = ""; // Function 2. + F3 = ""; // Function 3. + F4 = ""; // Function 4. + F5 = ""; // Function 5. + } + } + controller_0 :: pin_controller { + number = 0; + regStartBasePhy = 0x10FF0000; + regSize = 0x48; + pinCount = 18; + match_attr = "hisilicon_hi35xx_pin_0"; + T1 :: pin_desc { + pinName = "T1"; + init = 0x0600; + F0 = "EMMC_CLK"; + F1 = "SFC_CLK"; + F2 = "SFC_BOOT_MODE"; + } + ... // Add each pin under the pin controller. + } + ... // Each pin controller corresponds to a controller node. If there are multiple pin controllers, add the controller nodes one by one. + } + } + } + ``` + + After the **pin_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // Relative path of the file. + ``` 3. Instantiate the pin controller object. - - Initialize the **PinCntlr** object. - - Call **Hi35xxPinCntlrInit** to initialize the **PinCntlr** members. - - ```c - struct Hi35xxPinDesc { - // Pin name. - const char *pinName; - // Initial value. - uint32_t init; - // Index of the pin. - uint32_t index; - // Pull type of the pin. - int32_t pullType; - // Pull strength of the pin. - int32_t strength; - // Array of pin function names. - const char *func[HI35XX_PIN_FUNC_MAX]; - }; - - struct Hi35xxPinCntlr { - // Pin controller. - struct PinCntlr cntlr; - // Pointer to the pin description structure. - struct Hi35xxPinDesc *desc; - // Register mapping address. - volatile unsigned char *regBase; - // ID of the pin controller. - uint16_t number; - // Start address of the register physical base addresses. - uint32_t regStartBasePhy; - // Register bit width. - uint32_t regSize; - // Number of pins. - uint32_t pinCount; - }; - - // PinCntlr is the controller structure at the core layer. The Init function assigns values to PinCntlr. - struct PinCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct PinCntlrMethod *method; - struct DListHead node; // Node in the linked list. - OsalSpinlock spin; // Spinlock. - uint16_t number; // ID of the pin controller. - uint16_t pinCount; // Number of pins. - struct PinDesc *pins; - void *priv; // Private data. - }; - - // Initialize PinCntlr. - static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) - { - struct DeviceResourceIface *drsOps = NULL; - int32_t ret; - // Read the pin controller attributes from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read number failed", __func__); - return ret; - } - - ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regStartBasePhy failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regSize failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinCount failed", __func__); - return ret; - } - // Assign the values read to the members of the pin controller to initialize the pin controller. - hi35xx->cntlr.pinCount = hi35xx->pinCount; - hi35xx->cntlr.number = hi35xx->number; - hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // Pin controller mapping. - if (hi35xx->regBase == NULL) { - HDF_LOGE("%s: remap Pin base failed", __func__); - return HDF_ERR_IO; - } - hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); - hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); - return HDF_SUCCESS; - } - ``` - - - Instantiate the callback structure **PinCntlrMethod** in **PinCntlr**. Other members are initialized by using the **Init** function. - - ```c - // The members of the PinCntlrMethod structure are all callbacks. Vendors need to implement the corresponding functions according to Table 1. - static struct PinCntlrMethod g_method = { - .SetPinPull = Hi35xxPinSetPull, // Set the pull type. - .GetPinPull = Hi35xxPinGetPull, // Obtain the pull type. - .SetPinStrength = Hi35xxPinSetStrength, // Set the pull strength. - .GetPinStrength = Hi35xxPinGetStrength, // Obtain the pull strength. - .SetPinFunc = Hi35xxPinSetFunc, // Set the pin functions. - .GetPinFunc = Hi35xxPinGetFunc, // Obtain the pin functions. - }; - ``` - - - **Init** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - **HDF\_STATUS**
The table below describes some status. For more details, see **HDF\_STATUS** in **/drivers/framework/include/utils/hdf\_base.h**. - - | **Status** | **Description** | - | ---------------------- | -------------- | - | HDF_ERR_INVALID_OBJECT | Invalid controller object.| - | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | - | HDF_ERR_INVALID_PARAM | Invalid parameter. | - | HDF_ERR_IO | I/O error. | - | HDF_SUCCESS | Initialization successful. | - | HDF_FAILURE | Initialization failed. | - - **Function description**: - - Initializes the custom structure object and **PinCntlr** members, and connects to the pin controller by calling the **PinCntlrAdd** function. - - ```c - static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) - { - int32_t ret; - uint32_t funcNum = 0; - // Read the pin function names of the pin controller child nodes from the .hcs file. - ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F0 failed", __func__); - return ret; - } - - funcNum++; - ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F1 failed", __func__); - return ret; - } - - funcNum++; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) - { - int32_t ret; - struct DeviceResourceIface *drsOps = NULL; - // Read the pin attributes of the pin controller child nodes from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinName failed", __func__); - return ret; - } - ... - ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:Pin read Func failed", __func__); - return ret; - } - hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; - hi35xx->cntlr.pins[index].priv = (void *)node; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) - { - ... - struct Hi35xxPinCntlr *hi35xx = NULL; - ... - ret = Hi35xxPinCntlrInit(device, hi35xx); // Initialize the pin controller. - ... - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // Traverses each child node of the pin controller. - ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // Parse child nodes. - ... - } - - hi35xx->cntlr.method = &g_method; // Instantiate method. - ret = PinCntlrAdd(&hi35xx->cntlr); // Connect to the controller. - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: add Pin cntlr: failed", __func__); - ret = HDF_FAILURE; - } - return HDF_SUCCESS; - } - ``` - - - **Release** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - No value is returned. - - **Function description**: - - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, **Release** can be called to release driver resources. - - ```c - static void Hi35xxPinRelease(struct HdfDeviceObject *device) - { - int32_t ret; - uint16_t number; - struct PinCntlr *cntlr = NULL; - struct Hi35xxPinCntlr *hi35xx = NULL; - struct DeviceResourceIface *drsOps = NULL; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is null", __func__); - return; - } - // Read the pin controller ID from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops", __func__); - return; - } - ret = drsOps->GetUint16(device->property, "number", &number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read cntlr number failed", __func__); - return; - } - - cntlr = PinCntlrGetByNumber(number); // Obtain the pin controller based on the controller ID. - PinCntlrRemove(cntlr); - hi35xx = (struct Hi35xxPinCntlr *)cntlr; - if (hi35xx != NULL) { - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - } - OsalMemFree(hi35xx); - } - } - ``` + Initialize the **PinCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **PinCntlrMethod** in **PinCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pin_config.hcs** file to initialize the members in the custom structure and passes important parameters to the object at the core layer. + + Initialize **PinCntlr** in **Hi35xxPinCntlrInit**. + + ```c + // Custom pin description structure. + struct Hi35xxPinDesc { + const char *pinName; // Pin name. + uint32_t init; // Initial value. + uint32_t index; // Pin index. + int32_t pullType; // Pull type of the pin. + int32_t strength; // Pull strength of the pin. + const char *func[HI35XX_PIN_FUNC_MAX]; // Array of pin function names. + }; + + /* Custom structure. + struct Hi35xxPinCntlr { + struct PinCntlr cntlr // Core layer control object. + struct Hi35xxPinDesc *desc; // Pointer to the pin description structure. + volatile unsigned char *regBase; // Register base address used for address mapping. + uint16_t number; // Pin controller number. + uint32_t regStartBasePhy; // Start address of the register physical base address. + uint32_t regSize; // Register size. + uint32_t pinCount; // Number of pins. + }; + + // PinCntlr is the controller structure at the core layer. The Init function assigns values to the members of PinCntlr. + struct PinCntlr { + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + struct PinCntlrMethod *method; // Hook functions. + struct DListHead node; // Linked list node. + OsalSpinlock spin; // Spinlock. + uint16_t number; // Pin controller number. + uint16_t pinCount; // Number of pins. + struct PinDesc *pins; // Pin resources. + void *priv; // Private data. + }; + + // Initialize the pin controller. + static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) + { + struct DeviceResourceIface *drsOps = NULL; + int32_t ret; + // Read the pin controller attributes from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read number failed", __func__); + return ret; + } + + if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regStartBasePhy failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regSize failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinCount failed", __func__); + return ret; + } + if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + // Assign the values read to the members of the pin controller to initialize the pin controller. + hi35xx->cntlr.pinCount = hi35xx->pinCount; + hi35xx->cntlr.number = hi35xx->number; + hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // Pin controller mapping. + if (hi35xx->regBase == NULL) { + HDF_LOGE("%s: remap Pin base failed", __func__); + return HDF_ERR_IO; + } + hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + return HDF_SUCCESS; + } + ``` + + - Instantiate the **PinCntlrMethod** structure in **PinCntlr**. + + ```c + static struct PinCntlrMethod g_method = { + .SetPinPull = Hi35xxPinSetPull, // Set the pull type. + .GetPinPull = Hi35xxPinGetPull, // Obtain the pull type. + .SetPinStrength = Hi35xxPinSetStrength, // Set the pull strength. + .GetPinStrength = Hi35xxPinGetStrength, // Obtain the pull strength. + .SetPinFunc = Hi35xxPinSetFunc, // Set the pin functions. + .GetPinFunc = Hi35xxPinGetFunc, // Obtain the pin functions. + }; + ``` + + - Implement the **Init** function. + + Input Parameter + + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: + + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + | **State** | **Description** | + | ---------------------- | -------------- | + | HDF_ERR_INVALID_OBJECT | Invalid controller object.| + | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | + | HDF_ERR_INVALID_PARAM | Invalid parameter. | + | HDF_ERR_IO | I/O error. | + | HDF_SUCCESS | Initialization successful. | + | HDF_FAILURE | Initialization failed. | + + Function description: + + Initializes the custom structure object and **PinCntlr** members, and calls **PinCntlrAdd()** to add the pin controller to the core layer. + + ```c + static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) + { + int32_t ret; + uint32_t funcNum = 0; + // Read the pin function names of the pin controller child nodes from the .hcs file. + ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F0 failed", __func__); + return ret; + } + + funcNum++; + ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F1 failed", __func__); + return ret; + } + + funcNum++; + ... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) + { + int32_t ret; + struct DeviceResourceIface *drsOps = NULL; + // Read the pin attributes of the pin controller child nodes from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinName failed", __func__); + return ret; + } + ... + ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s:Pin read Func failed", __func__); + return ret; + } + hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; + hi35xx->cntlr.pins[index].priv = (void *)node; + ... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) + { + ... + struct Hi35xxPinCntlr *hi35xx = NULL; + ... + ret = Hi35xxPinCntlrInit(device, hi35xx); // Initialize the pin controller. + ... + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // Traverse each child node of the pin controller. + ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // Parse child nodes. + ... + } + + hi35xx->cntlr.method = &g_method; // Attach the PinCntlrMethod instance. + ret = PinCntlrAdd(&hi35xx->cntlr); // Add the controller. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: add Pin cntlr: failed", __func__); + ret = HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + + - Implement the **Release** function. + + Input parameters: + + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: + + No value is returned. + + Function description: + + Releases the memory and deletes the controller. This function needs to assign values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. + + ```c + static void Hi35xxPinRelease(struct HdfDeviceObject *device) + { + int32_t ret; + uint16_t number; + struct PinCntlr *cntlr = NULL; + struct Hi35xxPinCntlr *hi35xx = NULL; + struct DeviceResourceIface *drsOps = NULL; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is null", __func__); + return; + } + // Read the pin controller number from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops", __func__); + return; + } + ret = drsOps->GetUint16(device->property, "number", &number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read cntlr number failed", __func__); + return; + } + + cntlr = PinCntlrGetByNumber(number); // Obtain the pin controller based on the controller number. + PinCntlrRemove(cntlr); + hi35xx = (struct Hi35xxPinCntlr *)cntlr; + if (hi35xx != NULL) { + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + } + OsalMemFree(hi35xx); + } + } + ``` + 4. Debug the driver. (Optional) Verify basic functionalities of new drivers. For example, verify the information returned when the driver is loaded and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-pwm-des.md b/en/device-dev/driver/driver-platform-pwm-des.md index 9ea0bedfd64114be9e5d646a52857f8d013406ed..0814751514e04ea888575106a6178a1edb5aa836 100644 --- a/en/device-dev/driver/driver-platform-pwm-des.md +++ b/en/device-dev/driver/driver-platform-pwm-des.md @@ -1,417 +1,409 @@ # PWM - ## Overview -Pulse width modulation (PWM) is a technology that digitally encodes analog signal levels and converts them into pulses. It can be used for motor control and backlight brightness adjustment. - -The PWM APIs provide a set of functions for operating a PWM device, including those for: -- Opening or closing a PWM device handle +### Function -- Setting the PWM period, signal ON-state time, and polarity +Pulse width modulation (PWM) is a technology that digitally encodes analog signal levels and converts them into pulses. -- Enabling and disabling a PWM device +The PWM module provides a set of APIs for operating a PWM device, including: +- Opening or closing a PWM device +- Setting the PWM period, signal ON-state time, and polarity +- Enabling or disabling a PWM device - Obtaining and setting PWM parameters +### Basic Concepts -### PwmConfig Structure +A pulse (electrical pulse) is a burst of current or voltage, characterized by sudden change and discontinuity. There are many types of pulses. Common pulses include triangular, sharp, rectangular, square, trapezoidal, and zigzag pulses. Main pulse parameters include the repetition period **T** (**T** = 1/**F**, where **F** is the pulse repetition frequency), pulse amplitude **U**, rise time **ts** at the leading edge, fall time **t** at the trailing edge, and pulse width **tk**. - **Table 1** PwmConfig structure +### Working Principles -| Parameter| Description| -| -------- | -------- | -| duty | Time that a signal is in the ON state, in ns.| -| period | Time for a signal to complete an on-and-off cycle, in ns.| -| number | Number of square waves to generate.
- Positive value: indicates the number of square waves to generate.
- **0**: indicates that square waves are generated continuously.| -| polarity | PWM signal polarity, which can be positive or reverse.| -| status | PWM device status, which can be enabled or disabled.| +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: -## Available APIs +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. - **Table 2** PWM driver APIs +The PWM module is divided into the following layers: -| Category| Description| -| -------- | -------- | -| Operating PWM handles| - **PwmOpen**: opens the device handle of a PWM device.
- **PwmClose**: closes the device handle of a PWM device.| -| Enabling or disabling PWM| - **PwmEnable**: enables a PWM device.
- **PwmDisable**: disables a PWM device.| -| Setting PWM| - **PwmSetPeriod**: sets the PWM period.
- **PwmSetDuty**: sets the signal ON-state time.
- **PwmSetPolarity**: sets the PWM signal polarity.| -| Setting or obtaining PWM configuration| - **PwmSetConfig**: sets PWM device parameters.
- **PwmGetConfig**: obtains PWM device parameters.| +- Interface layer: provides APIs for opening or closing a PWM device, setting the PWM period, signal ON-state time, PWM device polarity, or PWM device parameters, obtaining PWM device parameters, and enabling or disabling a PWM device +- Core layer: provides the capabilities of adding or removing a PWM controller and managing PWM devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -> ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in the kernel space. +**Figure 1** Independent service mode +![image1](figures/independent-service-mode.png "PWM independent service mode") ## Usage Guidelines +### When to Use + +The PWM module is used for controlling vibrators and adjusting backlight brightness in smart devices. -### How to Use +### Available APIs -The figure below illustrates how to use the APIs. +**Table 1** describes the **PwmConfig** structure, which defines the PWM device attributes. **Table 2** describes the APIs provided by the PWM module. -**Figure 1** Using the PWM driver APIs +**Table 1** PwmConfig structure -![](figures/using-PWM-process.png) +| Parameter| Description| +| -------- | -------- | +| duty | Time that a signal is in the ON state, in ns.| +| period | Time for a signal to complete an on-and-off cycle, in ns.| +| number | Number of square waves to generate.
- Positive value: indicates the number of square waves to generate.
- **0**: indicates that square waves are generated continuously.| +| polarity | PWM signal polarity, which can be normal or reverted.
A signal with normal polarity starts high for the duration of the duty cycle and goes low for the remaining of the period.
A signal with inverted polarity starts low for the duration of the duty cycle and goes high for the remaining of the period.| +| status | PWM device status, which can be enabled or disabled.| +**Table 2** PWM driver APIs -### Opening a PWM Device Handle +| API | | +| ------------------------------------------------------------ | ------------------- | +| DevHandle PwmOpen(uint32_t num) | Opens a PWM device. | +| void PwmClose(DevHandle handle) | Closes a PWM device. | +| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | Sets the PWM period. | +| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | Sets the signal ON-state time.| +| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | Sets the PWM signal polarity. | +| int32_t PwmEnable(DevHandle handle) | Enables a PWM device. | +| int32_t PwmDisable(DevHandle handle) | Disables a PWM device. | +| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config) | Sets PWM device parameters. | +| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config) | Obtains PWM device parameters. | -Before performing operations on a PWM device, call **PwmOpen** to open the device handle. +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All the PWM APIs described in this document can be used in kernel mode and user mode. +### How to Develop -``` +The following figure shows how to use PWM APIs. + +**Figure 2** Using PWM APIs + +![image2](figures/using-PWM-process.png) + +#### Opening a PWM Device + +Before performing operations on a PWM device, use **PwmOpen()** to obtain the device handle. + +```c DevHandle PwmOpen(uint32_t num); ``` - **Table 3** Description of PwmOpen +**Table 3** Description of PwmOpen | **Parameter**| **Description**| | -------- | -------- | | num | PWM device number. | | **Return Value** | **Description** | -| handle | The operation is successful. The handle of the PWM device obtained is returned.| -| NULL | The operation failed. | - -Example: Open the device handle of PWM device 0. +| handle | The operation is successful. The PWM device handle is returned.| +| NULL | The operation fails. | +Example: Open PWM device 0. -``` -uint32_t num = 0; /* PWM device number. */ +```c +uint32_t num = 0; // PWM device number. DevHandle handle = NULL; -/* Obtain the PWM device handle. */ -handle = PwmOpen(num); +handle = PwmOpen(num); // Open PWM device 0 and obtain the device handle. if (handle == NULL) { - /* Error handling. */ + HDF_LOGE("PwmOpen: open pwm_%u failed.\n", num); + return; } ``` +#### Closing a PWM Device -### Closing a PWM Device Handle - -Call **PwmClose()** to close a PWM device handle to release resources. +Use **PwmClose()** to close a PWM device to release resources. - -``` -voidPwmClose(DevHandle handle); +```c +void PwmClose(DevHandle handle); ``` - **Table 4** Description of PwmClose +**Table 4** Description of PwmClose | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | +| handle | Handle of the PWM device to close. | - -``` -/* Close a PWM device handle. */ -PwmClose(handle); +```c +PwmClose(handle); // Close the PWM device and destroy the PWM device handle. ``` +#### Enabling a PWM Device -### Enabling a PWM Device - -Call **PwmEnable()** to enable a PWM device. - - -``` +```c int32_t PwmEnable(DevHandle handle); ``` - **Table 5** Description of PwmEnable +**Table 5** Description of PwmEnable | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| **Return Value** | **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value** | **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Enable a PWM device. */ -ret = PwmEnable(handle); -if (ret != 0) { - /* Error handling. */ +ret = PwmEnable(handle); // Enable the PWM device. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmEnable: enable pwm failed, ret:%d\n", ret); + return ret; } ``` +#### Disabling a PWM Device -### Disabling a PWM Device - -Call **PwmDisable()** to disable a PWM device. - - -``` +```c int32_t PwmDisable(DevHandle handle); ``` - **Table 6** Description of PwmDisable +**Table 6** Description of PwmDisable | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| **Return Value** | **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value** | **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Disable a PWM device. */ -ret = PwmDisable(handle); -if (ret != 0) { - /* Error handling. */ +ret = PwmDisable(handle); // Disable the PWM device. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmDisable: disable pwm failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the PWM Period -### Setting the PWM Period - -Call **PwmSetPeriod()** to set the PWM period. - - -``` +```c int32_t PwmSetPeriod(DevHandle handle, uint32_t period); ``` - **Table 7** Description of PwmSetPeriod +**Table 7** Description of PwmSetPeriod | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | | period | PWM period to set, in ns.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | | Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Set the PWM period to 50000000 ns. */ -ret = PwmSetPeriod(handle, 50000000); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetPeriod(handle, 50000000); // Set the PWM period to 50,000,000 ns. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPeriod: pwm set period failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the Signal ON-State Time -### Setting the PWM Signal ON-State Time - -Call **PwmSetDuty()** to set the time that the PWM signal is in the ON state. - - -``` +```c int32_t PwmSetDuty(DevHandle handle, uint32_t duty); ``` - **Table 8** Description of PwmSetDuty +**Table 8** Description of PwmSetDuty | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| duty | Time that the signal is in the ON state, in ns.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| duty | Time that a signal is in the ON state, in ns.| +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; -/* Set the signal ON-state time to 25000000 ns. */ -ret = PwmSetDuty(handle, 25000000); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetDuty(handle, 25000000); // Set the signal ON-state time to 25,000,000 ns. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetDuty: pwm set duty failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the PWM Signal Polarity -### Setting the PWM Signal Polarity - -Call **PwmSetPolarity()** to set the signal polarity for a PWM device. - - -``` +```c int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity); ``` - **Table 9** Description of PwmSetPolarity +**Table 9** Description of PwmSetPolarity | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | | polarity | Polarity to set, which can be **PWM\_NORMAL\_POLARITY** or **PWM\_INVERTED\_POLARITY**.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; -/* Set the PWM polarity to PWM_INVERTED_POLARITY. */ -ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // Set the PWM signal polarity to inverted. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret:%d\n", ret); + return ret; } ``` +#### Setting PWM Device Parameters -### Setting PWM Device Parameters - -Call **PwmSetConfig()** to set PWM device parameters. - - -``` +```c int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config); ``` - **Table 10** Description of PwmSetConfig +**Table 10** Description of PwmSetConfig | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | -| \*config | Pointer to PWM parameters. | -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | - +| handle | PWM device handle. | +| \*config | Pointer to the PWM parameters to set. | +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; struct PwmConfig pcfg; -pcfg.duty = 25000000; /* Set the signal ON-state time to 25000000 ns. */ -pcfg.period = 50000000; /* Set the PWM period to 50000000 ns. */ -pcfg.number = 0; /* Generate square waves continuously. */ -pcfg.polarity = PWM_INVERTED_POLARITY; /* Set the PWM polarity to PWM_INVERTED_POLARITY. */ -pcfg.status = PWM_ENABLE_STATUS; /* Set the running status to Enabled. */ - -/* Set PWM device parameters. */ -ret = PwmSetConfig(handle, &pcfg); -if (ret != 0) { - /* Error handling. */ -} -``` +The pcfg.duty = 25000000; // Set the signal ON-state time to 25,000,000 ns. +pcfg.period = 50000000; // Set the PWM period to 50,000,000 ns. +pcfg.number = 0; // Generate square waves continuously. +pcfg.polarity = PWM_INVERTED_POLARITY; // Set the PWM signal polarity to inverted. +pcfg.status = PWM_ENABLE_STATUS; // Enable PWM. -### Obtaining PWM Device Parameters - -Call **PwmGetConfig()** to obtain PWM device parameters. +ret = PwmSetConfig(handle, &pcfg); // Set PWM device parameters. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetConfig: pwm set config failed, ret:%d\n", ret); + return ret; +} +``` +#### Obtaining PWM Device Parameters -``` +```c int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config); ``` - **Table 11** Description of PwmGetConfig +**Table 11** Description of PwmGetConfig | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | -| \*config | Pointer to PWM parameters. | -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | - +| handle | PWM device handle. | +| \*config | Pointer to the PWM parameters obtained. | +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; struct PwmConfig pcfg; -/* Obtain PWM device parameters. */ -ret = PwmGetConfig(handle, &pcfg); -if (ret != 0) { - /* Error handling. */ +ret = PwmGetConfig(handle, &pcfg); // Obtain PWM device parameters. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmGetConfig: pwm get config failed, ret:%d\n", ret); + return ret; } ``` +## Example -## Development Example +The following uses the Hi3516D V300 development board as an example to describe how to use the PWM. The procedure is as follows: -The following example shows how to use the APIs to implement a PWM driver and manage the PWM device. +1. Open a PWM device and obtain the PWM device handle. +2. Set the PWM device period. +3. Set the signal ON-state time for the PWM device. +4. Set the signal polarity for the PWM device. +5. Obtain the PWM device parameters. +6. Enable the PWM device. +7. Set the PWM device parameters. +8. Disable the PWM device. +9. Close the PWM device. +```c +#include "pwm_if.h" // Header file of PWM standard APIs. +#include "hdf_log.h" // Header file of the HDF log APIs. -``` -void PwmTestSample(void) +static int32_t PwmTestSample(void) { int32_t ret; uint32_t num; + uint32_t period DevHandle handle = NULL; struct PwmConfig pcfg; - pcfg.duty = 20000000; /* Set the signal ON-state time to 20000000 ns. */ - pcfg.period = 40000000; /* Set the PWM period to 40000000 ns. */ - pcfg.number = 100; /* Generate 100 square waves. */ - pcfg.polarity = PWM_NORMAL_POLARITY; /* Set the polarity to PWM_NORMAL_POLARITY. */ - pcfg.status = PWM_ENABLE_STATUS; /* Set the running status to Enabled. */ + pcfg.duty = 20000000; // Set the signal ON-state time to 20,000,000 ns. + pcfg.period = 40000000; // Set the PWM period to 40,000,000 ns. + pcfg.number = 100; // Generate 100 square waves continuously. + pcfg.polarity = PWM_NORMAL_POLARITY; // Set the PWM signal polarity to normal. + pcfg.status = PWM_ENABLE_STATUS; // Enable the PWM device. - /* Enter the PWM device number. */ - num = 1; + num = 1; // PWM device number. - /* Open the PWM device handle. */ - handle = PwmOpen(num); + handle = PwmOpen(num); // Open a PWM device. if (handle == NULL) { - HDF_LOGE("PwmOpen: failed!\n"); + HDF_LOGE("PwmOpen: open pwm_%u failed!\n", num); return; } - /* Set the PWM period to 50000000 ns.*/ - ret = PwmSetPeriod(handle, 50000000); - if (ret != 0) { - HDF_LOGE("PwmSetPeriod: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetPeriod(handle, 50000000); // Set the PWM period to 50,000,000 ns. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPeriod: pwm set period failed, ret %d\n", ret); + goto ERR; } - /* Set the signal ON-state time to 25000000 ns. */ - ret = PwmSetDuty(handle, 25000000); - if (ret != 0) { - HDF_LOGE("PwmSetDuty: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetDuty(handle, 25000000); // Set the signal ON-state time to 25,000,000 ns. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetDuty: pwm set duty failed, ret %d\n", ret); + goto ERR; } - /* Set the PWM polarity to PWM_INVERTED_POLARITY. */ - ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); - if (ret != 0) { - HDF_LOGE("PwmSetPolarity: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // Set the PWM signal polarity to inverted. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret %d\n", ret); + goto ERR; } - /* Obtain PWM device parameters. */ - ret = PwmGetConfig(handle, &pcfg); - if (ret != 0) { - HDF_LOGE("PwmGetConfig: failed, ret %d\n", ret); - goto _ERR; + ret = PwmGetConfig(handle, &pcfg); // Obtain PWM device parameters. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmGetConfig: get pwm config failed, ret %d\n", ret); + goto ERR; } - /* Enable the PWM device. */ - ret = PwmEnable(handle); - if (ret != 0) { - HDF_LOGE("PwmEnable: failed, ret %d\n", ret); - goto _ERR; + ret = PwmEnable(handle); // Enable the PWM device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmEnable: enable pwm failed, ret %d\n", ret); + goto ERR; } - /* Set PWM device parameters. */ - ret = PwmSetConfig(handle, &pcfg); - if (ret != 0) { - HDF_LOGE("PwmSetConfig: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetConfig(handle, &pcfg); // Set PWM device parameters. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetConfig: set pwm config failed, ret %d\n", ret); + goto ERR; } - /* Disable the PWM device. */ - ret = PwmDisable(handle); - if (ret != 0) { - HDF_LOGE("PwmDisable: failed, ret %d\n", ret); - goto _ERR; + ret = PwmDisable(handle); // Disable the PWM device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmDisable: disable pwm failed, ret %d\n", ret); + goto ERR; } -_ERR: - /* Close the PWM device handle. */ - PwmClose(handle); +ERR: + PwmClose(handle); // Close the PWM device. + return ret; } ``` diff --git a/en/device-dev/driver/driver-platform-pwm-develop.md b/en/device-dev/driver/driver-platform-pwm-develop.md index 73fe496394b614bf4dc8390dfaa8cc879c4ddb0d..c2d2fe59ab49dda27d2ed5093af80b7311e15e26 100644 --- a/en/device-dev/driver/driver-platform-pwm-develop.md +++ b/en/device-dev/driver/driver-platform-pwm-develop.md @@ -1,208 +1,224 @@ # PWM - ## Overview -In the Hardware Driver Foundation (HDF), the Pulse Width Modulator (PWM) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +Pulse Width Modulation (PWM) is a technology that performs digital coding on analog signal levels and converts them into pulses. It is widely used in fields, such as measurement, communication, and power control and conversion. The PWM module is used for controlling vibrators and adjusting backlight brightness in smart devices. - ![image](figures/independent-service-mode.png "PWM independent service mode") +### Basic Concepts +A pulse (electrical pulse) is a burst of current or voltage, characterized by sudden change and discontinuity. There are many types of pulses. Common pulses include triangular, sharp, rectangular, square, trapezoidal, and zigzag pulses. Main pulse parameters include the repetition period **T** (**T** = 1/**F**, where **F** is the pulse repetition frequency), pulse amplitude **U**, rise time **ts** at the leading edge, fall time **t** at the trailing edge, and pulse width **tk**. -## Available APIs +### Working Principles -**PwmMethod**: +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: -``` +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The PWM module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a PWM device, setting the PWM period, signal ON-state time, PWM device polarity, or PWM device parameters, obtaining PWM device parameters, and enabling or disabling a PWM device +- Core layer: provides the capabilities of adding or removing a PWM controller and managing PWM devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![image](figures/independent-service-mode.png "PWM independent service mode") + +## Development Guidelines + +### When to Use + +Before using your PWM device with OpenHarmony, you need to perform PWM driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the PWM controller by calling the PWM APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/pwm/pwm_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**PwmMethod**: + +```c struct PwmMethod { - int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config); - int32_t (*open)(struct PwmDev *pwm); - int32_t (*close)(struct PwmDev *pwm); + int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config); + int32_t (*open)(struct PwmDev *pwm); + int32_t (*close)(struct PwmDev *pwm); }; ``` - **Table 1** Description of callback functions in PwmMethod +**Table 1** Hook functions in **PwmMethod** | Function| Input Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -| setConfig | **pwm**: structure pointer to the PWM controller at the core layer.
**config**: structure pointer to the attributes to set.| HDF_STATUS| Sets attributes.| -| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a device.| -| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a device.| - +| setConfig | **pwm**: structure pointer to the PWM controller at the core layer.
**config**: structure pointer to the device attributes to set.| HDF_STATUS| Sets device attributes.| +| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a PWM device.| +| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a PWM device.| -## How to Develop +### How to Develop -The PWM module adaptation involves the following steps: +The PWM module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** description to the **device_info.hcs** file. - - (Optional) Add the **pwm_config.hcs** file. - 3. Instantiate the PWM controller object. - - Initialize **PwmDev**. - - Instantiate **PwmMethod** in the **PwmDev** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **PwmMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts. - +### Example -## Development Example - -The following uses **pwm_hi35xx.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/pwm/pwm_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the PWM driver adaptation. 1. Instantiate the driver entry. - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. - - In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. PWM driver entry example: - - ``` + + ```c struct HdfDriverEntry g_hdfPwm = { .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_PWM",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. - .Bind = HdfPwmBind, - .Init = HdfPwmInit, - .Release = HdfPwmRelease, + .moduleName = "HDF_PLATFORM_PWM", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HdfPwmBind, // See the Bind function. + .Init = HdfPwmInit, // See the Init function. + .Release = HdfPwmRelease, // See the Release function. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_hdfPwm); + HDF_INIT(g_hdfPwm); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **pwm_config.hcs** file. +2. Configure attribute files. + + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two PWM controllers as an example. If there are more PWM controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **pwm_config.hcs** are closely related to default values or value ranges of the **PwmDev** members at the core layer. - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **PwmDev** members at the core layer. If there are multiple devices, you need to add the **deviceNode** information to the **device_info** file and add the device attributes to the **pwm_config** file for each device. + - **device_info.hcs** example - - **device_info.hcs** configuration example + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. - - ``` + ```c root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pwm :: device { // Configure an HDF device node for each PWM controller. - device0 :: deviceNode { - policy = 1; // Publish services for kernel-mode processes. - priority = 80; // Driver startup priority. - permission = 0644; // Permission for the driver to create a device node. - moduleName = "HDF_PLATFORM_PWM"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HDF_PLATFORM_PWM_0"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_pwm_0";// (Mandatory) Used to configure the private data of the controller. - // The value must be the same as the controller information in pwm_config.hcs. + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pwm ::device { // Configure an HDF device node for each PWM controller. + device0 :: deviceNode { + policy = 1; // The value 1 means to publish services only to the kernel-mode processes. + priority = 80; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_PWM"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HDF_PLATFORM_PWM_0"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // Controller private data, which must be the same as that of the controller in pwm_config.hcs. + } + device1 :: deviceNode { + policy = 1; + priority = 80; + permission = 0644; + moduleName = "HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_1"; + deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; + } + ... + } } - device1 :: deviceNode { - policy = 1; - priority = 80; - permission = 0644; - moduleName = "HDF_PLATFORM_PWM"; - serviceName = "HDF_PLATFORM_PWM_1"; - deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; - } - } } - } } ``` - - **pwm_config.hcs** configuration example - - ``` + - **pwm_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs** file. The parameters are as follows: + + ```c root { - platform { - pwm_config { - template pwm_device { // (Mandatory) Template configuration. In the template, you can configure the common parameters shared by service nodes. - serviceName = ""; - match_attr = ""; - num = 0; // (Mandatory) Device number - base = 0x12070000; // (Mandatory) Used for address mapping - } - device_0x12070000 :: pwm_device { // Add the HDF node and device node information for each device. - match_attr = "hisilicon_hi35xx_pwm_0";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - } - device_0x12070020 :: pwm_device { - match_attr = "hisilicon_hi35xx_pwm_1"; - num = 1; - base = 0x12070020; // (Mandatory) Used for address mapping - } + platform { + pwm_config { + template pwm_device { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + serviceName = ""; + match_attr = ""; + num = 0; // (Mandatory) Device number. + base = 0x12070000; // (Mandatory) Base address used for address mapping. + } + device_0x12070000 :: pwm_device { // Add the HDF node and device node information for each device. + match_attr = "hisilicon_hi35xx_pwm_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + } + device_0x12070020 :: pwm_device { + match_attr = "hisilicon_hi35xx_pwm_1"; + num = 1; + base = 0x12070020; // (Mandatory) Base address used for address mapping. + } + } } - } } ``` -3. Initialize the **wmDev** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **PwmMethod** in **PwmDev** (so that the underlying driver functions can be called). + After the **pwm_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. - - Defining a custom structure + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // Relative path of the file. + ``` - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pwm_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number, to the object at the core layer. +3. Instantiate the PWM controller object. - - ``` + Initialize the **PwmDev** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **PwmMethod** in **PwmDev** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pwm_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the PWM device number, to the object at the core layer. + + ```c struct HiPwm { - struct PwmDev dev; // (Mandatory) Core layer structure - volatile unsigned char *base; - struct HiPwmRegs *reg; // Device attribute structure, which can be customized. - bool supportPolarity; + struct PwmDev dev; // (Mandatory) Control object at the core layer. + volatile unsigned char *base; // (Mandatory) Register base address used for address mapping. + struct HiPwmRegs *reg; // Device attribute structure, which can be customized. + bool supportPolarity; // Whether polarity is supported. }; - - // PwmDev is the controller structure at the core layer. The Init function assigns values to the members of PwmDev. - struct PwmDev { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct PwmConfig cfg; // Attribute structure. For details, see the description of PwmConfig. - struct PwmMethod *method; // Hook function template - bool busy; - uint32_t num; // Device number - OsalSpinlock lock; - void *priv; // Private data. Generally, the start address of the custom structure is stored to facilitate invoking of the structure. + + struct PwmDev { // PwmDev is the core layer controller structure. The Bind function assigns values to the members of PwmDev. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + struct PwmConfig cfg; // Device attribute structure. For details, see the following definition. + struct PwmMethod *method; // Hook functions. + bool busy; // Whether the device is busy. + uint32_t num; // Device number. + OsalSpinlock lock; // Spinlock. + void *priv; // Private data. }; - struct PwmConfig { - uint32_t duty // Time that a signal is in the ON state, in ns. - uint32_t period; // Time for a signal to complete an on-and-off cycle, in ns. - uint32_t number; // Number of square waves to generate. - uint8_t polarity; // Polarity - // ------------------- | -------------- - // PWM_NORMAL_POLARITY | Normal polarity - // PWM_INVERTED_POLARITY | Inverted polarity - // - uint8_t status; // Running status - // ------------------ | ----------------- - // PWM_DISABLE_STATUS | Disabled - // PWM_ENABLE_STATUS | Enabled + + struct PwmConfig { // PWM device attributes. + uint32_t duty; // Time that a signal is in the ON state, in ns. + uint32_t period; // Time for a signal to complete an on-and-off cycle, in ns. + uint32_t number; // Number of square waves to generate. + uint8_t polarity; // Polarity + // ------------------- | -------------- + // PWM_NORMAL_POLARITY | Normal polarity + // PWM_INVERTED_POLARITY | Inverted polarity + // + uint8_t status; // Running status. + // ------------------ | ----------------- + // PWM_DISABLE_STATUS | Disabled + // PWM_ENABLE_STATUS | Enabled }; ``` - - Instantiating **PwmMethod** in **PwmDev** (other members are initialized by **Init**) + - Instantiate the **PwmMethod** structure in **PwmDev**. - - ``` - // The following uses pwm_hi35xx.c as an example. Fill the hook function. - struct PwmMethod g_pwmOps = { - .setConfig = HiPwmSetConfig,// Set attributes. + ```c + struct PwmMethod g_pwmOps = { // Instantiate the hook functions in pwm_hi35xx.c. + .setConfig = HiPwmSetConfig, // Set device attributes. }; ``` - - **Init** function + + - Implement the **Init** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. | Status| Description| | -------- | -------- | @@ -215,58 +231,58 @@ The following uses **pwm_hi35xx.c** as an example to present the information req **Function description**: - Initializes the custom structure object and **PwmDev**, and calls the **PwmDeviceAdd** function at the core layer. + Initializes the custom structure object and **PwmDev** members, and calls **PwmDeviceAdd()** to add the PWM controller to the core layer. - - ``` - // The Bind function is empty. It can be combined with the Init function or implement related operations based on service requirements. + ```c + // In this example, Bind() is an empty function. You can add operations as required or implement related features in Init(). static int32_t HdfPwmBind(struct HdfDeviceObject *obj) { - (void)obj; - return HDF_SUCCESS; + (void)obj; + return HDF_SUCCESS; } - + static int32_t HdfPwmInit(struct HdfDeviceObject *obj) { - int ret; - struct HiPwm *hp = NULL; - ... - hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); - ... - ret = HiPwmProbe(hp, obj); // (Mandatory) The implementation is as follows: - ... - return ret; + int ret; + struct HiPwm *hp = NULL; + ... + hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); + ... + ret = HiPwmProbe(hp, obj); // (Mandatory) The implementation is as follows. + ... + return ret; } - + static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj) { uint32_t tmp; struct DeviceResourceIface *iface = NULL; - iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);// Initialize the custom structure HiPwm. + iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // Initialize the custom structure HiPwm. ... - hp->reg = (struct HiPwmRegs *)hp->base; // Initialize the custom structure HiPwm. - hp->supportPolarity = false; // Initialize the custom structure HiPwm. - hp->dev.method = &g_pwmOps; // Attach the PwmMethod instance. - hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // Initialize PwmDev. - hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // Initialize PwmDev. - hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // Initialize PwmDev. - hp->dev.cfg.status = PWM_DISABLE_STATUS; // Initialize PwmDev. - hp->dev.cfg.number = 0; // Initialize PwmDev. - hp->dev.busy = false; // Initialize PwmDev. - if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) { // Call the core layer function to initialize devices and services. + hp->reg = (struct HiPwmRegs *)hp->base; // Initialize the custom structure HiPwm. + hp->supportPolarity = false; // Initialize the custom structure HiPwm. + hp->dev.method = &g_pwmOps; // Attach the PwmMethod instance. + hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // Initialize PwmDev. + hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // Initialize PwmDev. + hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // Initialize PwmDev. + hp->dev.cfg.status = PWM_DISABLE_STATUS; // Initialize PwmDev. + hp->dev.cfg.number = 0; // Initialize PwmDev. + hp->dev.busy = false; // Initialize PwmDev. + if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) { // Call the core layer function to initialize devices and services. OsalIoUnmap((void *)hp->base); return HDF_FAILURE; } return HDF_SUCCESS; } ``` - - **Release** function + + - Implement the **Release** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -274,17 +290,20 @@ The following uses **pwm_hi35xx.c** as an example to present the information req **Function description**: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. - - ``` + ```c static void HdfPwmRelease(struct HdfDeviceObject *obj) { struct HiPwm *hp = NULL; ... - hp = (struct HiPwm *)obj->service;// A forced conversion from HdfDeviceObject to HiPwm is involved. - ... - PwmDeviceRemove(obj, &(hp->dev));// (Mandatory) Call the core layer functions to release PwmDev devices and services. A forced conversion from HiPwm to PwmDev is involved in the process. - HiPwmRemove(hp); // Release HiPwm. + hp = (struct HiPwm *)obj->service; // A forced conversion from HdfDeviceObject to HiPwm is involved. + ... + PwmDeviceRemove(obj, &(hp->dev)); // (Mandatory) Call the core layer functions to release PwmDev devices and services. A forced conversion from HiPwm to PwmDev is involved in the process. + HiPwmRemove(hp); // Release HiPwm. } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts. diff --git a/en/device-dev/driver/driver-platform-uart-des.md b/en/device-dev/driver/driver-platform-uart-des.md index 3ddb92b532569df4718931c9c3ca32b7e7e010c1..c73c19d71a903d9f73539b3704975fb15c374a94 100644 --- a/en/device-dev/driver/driver-platform-uart-des.md +++ b/en/device-dev/driver/driver-platform-uart-des.md @@ -1,328 +1,353 @@ # UART - ## Overview -The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. +### Function -UART is widely used to print information for debugging or to connect to various external modules such as GPS and Bluetooth. +The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. A UART is connected to other modules through two wires (as shown in Figure 1) or four wires (as shown in Figure 2). - - TX: TX pin of the transmitting UART. It is connected to the RX pin of the peer UART. - - RX: RX pin of the receiving UART. It is connected to the TX pin of the peer UART. - - RTS: Request to Send signal pin. It is connected to the CTS pin of the peer UART and is used to indicate whether the local UART is ready to receive data. - - CTS: Clear to Send signal pin. It is connected to the RTS pin of the peer UART and is used to indicate whether the local UART is allowed to send data to the peer end. - **Figure 1** Two-wire UART communication + - TX: UART transmitter. It is connected to the RX of the peer UART. + - RX: UART receiver. It is connected to the TX of the peer UART. + - RTS: Request to Send signal, indicating whether the local UART is ready to receive data. It is connected to the CTS of the peer UART. + - CTS: Clear to Send signal, indicating whether the local UART is allowed to send data to the peer end. It is connected to the RTS of the peer UART. - ![](figures/2-wire-uart-communication.png "2-wire-uart-communication") +**Figure 1** Two-wire UART communication - **Figure 2** Four-wire UART communication +![image1](figures/2-wire-uart-communication.png "2-wire-uart-communication") - ![](figures/4-wire-uart-communication.png "4-wire-uart-communication") +**Figure 2** Four-wire UART communication -- The transmitting and receiving UARTs must ensure that they have the same settings on particular attributes such as the baud rate and data format (start bit, data bit, parity bit, and stop bit) before they start to communicate. During data transmission, a UART sends data to the peer end over the TX pin and receives data from the peer end over the RX pin. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. + ![image2](figures/4-wire-uart-communication.png "4-wire-uart-communication") -- The UART interface defines a set of common functions for operating a UART port, including obtaining and releasing device handles, reading and writing data of a specified length, and obtaining and setting the baud rate, as well as the device attributes. +The UART transmitter and receiver must have the same settings on particular attributes, such as the baud rate and data format (start bit, data bits, parity bit, and stop bit) before they start to communicate. A UART sends data to the peer end over the TX and receives data from the peer end over the RX. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. +The UART module provides APIs for operating UART ports, including: -## Available APIs +- Opening or closing a UART device +- Reading or writing data +- Setting or obtaining the baud rate of a UART device +- Setting or obtaining UART device attributes - **Table 1** UART driver APIs +### Basic Concepts -| API| Description| -| -------- | -------- | -| UartOpen | Obtains a UART device handle.| -| UartClose | Releases a UART device handle.| -| UartRead | Reads data of the specified length from a UART device.| -| UartWrite | Writes data of the specified length to a UART device.| -| UartGetBaud | Obtains the UART baud rate.| -| UartSetBaud | Sets the UART baud rate.| -| UartGetAttribute | Obtains UART device attributes.| -| UartSetAttribute | Sets UART device attributes.| -| UartSetTransMode | Sets the UART transmission mode.| +- Asynchronous communication + + In asynchronous communication, data is transmitted in frames of characters or bytes. Frames are sent and received one by one through the transmission line. The transmitter and receiver have their own clocks to control data sending and receiving. The two clock sources are independent and not synchronized with each other. + + When data is sent one character at a time, the time interval between two characters is not fixed, but the time interval between two adjacent bits in a character frame is fixed. + +- Full-duplex transmission + + A duplex communication mode allows data to be transmitted in both directions at the same time. A duplex communication channel is equivalent to two simplex communication channels operating in opposite directions at the same time. In full-duplex mode, signals can be transmitted bidirectionally at the same time. + +### Working Principles -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in kernel mode. +In the Hardware Driver Foundation (HDF), the UART uses the independent service mode (see Figure 3) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The UART module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a UART device, reading or writing data of the specified length, setting or obtaining the baud rate or attributes of a UART device, and setting the transmission mode. +- Core layer: provides the capabilities of adding or removing a UART controller, and managing UART devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 3** Independent service mode + +![image3](figures/independent-service-mode.png) ## Usage Guidelines +### When to Use -### How to Use +The UART module is widely used to implement low-speed serial communication between devices, for example, output the printing information. It can also connect to a variety of external GPS and Bluetooth devices. -The figure below illustrates how to use the APIs. +### Available APIs - **Figure 3** Using UART driver APIs +**Table 1** UART driver APIs - ![](figures/using-UART-process.png) +| API| Description| +| -------- | -------- | +| DevHandle UartOpen(uint32_t port) | Opens a UART device.| +| void UartClose(DevHandle handle) | Closes a UART device.| +| int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) | Reads data of the specified length from a UART device.| +| int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) | Writes data of the specified length to a UART device.| +| int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) | Obtains the UART baud rate.| +| int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) | Sets the UART baud rate.| +| int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) | Obtains UART device attributes.| +| int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) | Sets UART device attributes.| +| int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) | Sets the UART transmission mode.| +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All the UART APIs described in this document can be used in kernel mode and user mode. -### Opening a UART Device Handle +### How to Develop -Before performing UART communication, call **UartOpen** to obtain a UART device handle. This function returns the pointer to the UART device handle with the specified port number. +The following figure illustrates how to use the UART APIs. - -``` +**Figure 4** Using UART driver APIs + +![image4](figures/using-UART-process.png) + + +#### Opening a UART Device + +Before performing UART communication, use **UartOpen()** to obtain a UART device handle based on the port number. + +```c DevHandle UartOpen(uint32_t port); ``` - **Table 2** Description of UartOpen +**Table 2** Description of UartOpen -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| port | UART port number.| -| **Return Value**| **Description**| -| NULL | The operation failed.| -| Device handle| The operation is successful. The obtained UART device handle is returned.| +| port | UART port number.| +| **Return Value**| **Description**| +| NULL | The operation fails.| +| Device handle| The operation is successful. The obtained UART device handle is returned.| + +Example: Obtain the device handle of UART port 1. + +```c +DevHandle handle = NULL; // UART device handle. +uint32_t port = 1; // UART device port number. - Example: Obtain the device handle of UART port 3. - -``` -DevHandle handle = NULL; /* UART device handle */ -uint32_t port = 3; /* UART port number */ handle = UartOpen(port); if (handle == NULL) { - HDF_LOGE("UartOpen: failed!\n"); + HDF_LOGE("UartOpen: open uart_%u failed!\n", port); return; } ``` +#### Setting the UART Baud Rate -### Setting the UART Baud Rate - -Call **UartSetBaud()** to set the UART baud rate. +Use **UartSetBaud()** to set the UART baud rate. - -``` +```c int32_t UartSetBaud(DevHandle handle, uint32_t baudRate); ``` - **Table 3** Description of UartSetBaud +**Table 3** Description of UartSetBaud -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| baudRate | Baud rate to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| baudRate | Baud rate to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set the UART baud rate to **9600**. - -``` +```c int32_t ret; -/* Set the UART baud rate to 9600. */ -ret = UartSetBaud(handle, 9600); -if (ret != 0) { + +ret = UartSetBaud(handle, 9600); // Set the UART baud rate. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetBaud: failed, ret %d\n", ret); + return ret; } ``` +#### Obtaining the UART Baud Rate -### Obtaining the UART Baud Rate - -Call **UartGetBaud()** to obtain the UART baud rate. +Use **UartGetBaud()** to obtain the UART baud rate. - -``` +```c int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate); ``` - **Table 4** Description of UartGetBaud +**Table 4** Description of UartGetBaud -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| baudRate | Pointer to the UART baud rate obtained.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| baudRate | Pointer to the UART baud rate obtained.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Obtain the UART baud rate. - -``` +```c int32_t ret; uint32_t baudRate; -/* Obtain the UART baud rate. */ -ret = UartGetBaud(handle, &baudRate); -if (ret != 0) { + +ret = UartGetBaud(handle, &baudRate); // Obtain the UART baud rate. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartGetBaud: failed, ret %d\n", ret); + return ret; } ``` +#### Setting UART Device Attributes -### Setting UART Device Attributes +Use **UartSetAttribute()** to set UART device attributes. -Call **UartSetAttribute()** to set UART device attributes. - - -``` +```c int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` - **Table 5** Description of UartSetAttribute +**Table 5** Description of UartSetAttribute -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| attribute | Pointer to the UART device attributes to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| attribute | Pointer to the UART device attributes to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set UART device attributes. - -``` +```c int32_t ret; struct UartAttribute attribute; -attribute.dataBits = UART_ATTR_DATABIT_7; /* Enable 7 bits to be transferred each time. */ -attribute.parity = UART_ATTR_PARITY_NONE; /* Disable parity check. */ -attribute.stopBits = UART_ATTR_STOPBIT_1; /* Set the stop bit to 1. */ -attribute.rts = UART_ATTR_RTS_DIS; /* Disable the RTS signal. */ -attribute.cts = UART_ATTR_CTS_DIS; /* Disable the CTS signal. */ -attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; /* Enable RX FIFO. */ -attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; /* Enable TX FIFO. */ -/* Set UART device attributes. */ -ret = UartSetAttribute(handle, &attribute); -if (ret != 0) { + +attribute.dataBits = UART_ATTR_DATABIT_7; // Transfer 7 bits each time. +attribute.parity = UART_ATTR_PARITY_NONE; // Disable parity check for the data to transfer. +attribute.stopBits = UART_ATTR_STOPBIT_1; // Set the stop bit to 1. +attribute.rts = UART_ATTR_RTS_DIS; // Disable RTS. +attribute.cts = UART_ATTR_CTS_DIS; // Disable CTS. +attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // Enable RX FIFO. +attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // Enable TX FIFO. + +ret = UartSetAttribute(handle, &attribute); // Set UART device attributes. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetAttribute: failed, ret %d\n", ret); +turn ret; } ``` +#### Obtaining UART Device Attributes -### Obtaining UART Device Attributes - -Call **UartGetAttribute()** to obtain the current UART device attributes. +Use **UartGetAttribute()** to obtain the UART device attributes. - -``` +```c int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` - **Table 6** Description of UartGetAttribute +**Table 6** Description of UartGetAttribute -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| attribute | Pointer to the UART device attributes obtained.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| attribute | Pointer to the UART device attributes obtained.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Obtain UART device attributes. - -``` +```c int32_t ret; struct UartAttribute attribute; -/* Obtain UART device attributes. */ -ret = UartGetAttribute(handle, &attribute); -if (ret != 0) { + +ret = UartGetAttribute(handle, &attribute); // Obtain the attributes of the UART device. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartGetAttribute: failed, ret %d\n", ret); + return ret; } ``` +#### Setting the UART Transmission Mode -### Setting the UART Transmission Mode +Use **UartSetTransMode()** to set the UART transmission mode. -Call **UartSetTransMode()** to set the UART transmission mode. - - -``` +```c int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode); ``` - **Table 7** Description of UartSetTransMode +**Table 7** Description of UartSetTransMode -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| mode | UART transmission mode to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| mode | UART transmission mode to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set the UART transmission mode to **UART_MODE_RD_BLOCK**. - -``` +```c int32_t ret; -/* Set the UART transmission mode. */ -ret = UartSetTransMode(handle, UART_MODE_RD_BLOCK); -if (ret != 0) { + +ret = UartSetTransMode(handle, UART_MODE_RD_BLOCK); // Sets the UART transmission mode. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetTransMode: failed, ret %d\n", ret); + return ret; } ``` +#### Writing Data to a UART Device -### Writing Data to a UART Device +Use **UartWrite()** to write data of the specified length to a UART device. -Call **UartWrite()** to write data of the specified length to a UART device. - - -``` +```c int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size); ``` - **Table 8** Description of UartWrite +**Table 8** Description of UartWrite -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| data | Pointer to the data to write.| -| size | Length of the data to write.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| data | Pointer to the data to write.| +| size | Length of the data to write.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Write data to a UART device. - -``` +```c int32_t ret; uint8_t wbuff[5] = {1, 2, 3, 4, 5}; -/* Write 5-byte data to the UART device. */ -ret = UartWrite(handle, wbuff, 5); -if (ret != 0) { + +ret = UartWrite(handle, wbuff, 5); // Write data of the specified length to the UART device. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartWrite: failed, ret %d\n", ret); + return ret; } ``` +#### Reading Data from a UART Device -### Reading Data from a UART Device - -Call **UartRead()** to read data of the specified length from a UART device. +Use **UartRead()** to read data of the specified length from a UART device. - -``` +```c int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size); ``` - **Table 9** Description of UartRead +**Table 9** Description of UartRead -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| data | Pointer to the buffer for receiving the data.| -| size | Length of the data to read.| -| **Return Value**| **Description**| -| Non-negative value| The operation is successful. The length of the data read is returned.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| data | Pointer to the buffer for receiving the data.| +| size | Length of the data to read.| +| **Return Value**| **Description**| +| Non-negative value| The operation is successful. The length of the data read is returned.| +| Negative value| The operation fails.| Example: Read data of the specified length from a UART device. - -``` +```c int32_t ret; uint8_t rbuff[5] = {0}; -/* Read 5-byte data from the UART device. */ -ret = UartRead(handle, rbuff, 5); + +ret = UartRead(handle, rbuff, 5); // Read data of the specified length from the UART device. if (ret < 0) { HDF_LOGE("UartRead: failed, ret %d\n", ret); + return ret; } ``` @@ -330,94 +355,115 @@ if (ret < 0) { > Data is successfully read from the UART device if a non-negative value is returned. If **0** is returned, no valid data can be read from the UART device. A value greater than **0** indicates the length of the data read from the UART device. The data length must be less than or equal to the value of **size** and cannot exceed the maximum length of the data to read at a time specified by the UART controller in use. -### Closing a UART Device Handle +#### Closing a UART Device -Call **UartClose()** to close a UART device handle. +Use **UartClose()** to close a UART device. - -``` +```c void UartClose(DevHandle handle); ``` This function releases the resources requested by **UartOpen**. - **Table 10** Description of UartClose +**Table 10** Description of UartClose -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle to close.| +| handle | UART device handle to close.| -Example: Close a UART device handle. +Example: Close a UART device. - +```c +UartClose(handle); // Close a UART device to release resources. ``` -UartClose(handle); /* Close the UART device handle. */ -``` - ## Example - The following example shows how to open a UART device handle, set the baud rate, device attributes, and transmission mode, read data from or write data into the UART device, and then close the UART device handle. - -``` +The following uses the Hi3516D V300 development board as an example to describe how to manage the UART device. The procedure is as follows: + +1. Open a UART device based on the port number. The handle of the UART device opened is returned. +2. Set the baud rate of the UART device. +3. Obtain the baud rate of the UART device. +4. Set the attributes of the UART device. +5. Obtain the attributes of the UART device. +6. Set the transmission mode of the UART device. +7. Transfer data of the specified length. +8. Receive data of the specified length. +9. Closes the UART device. + +```c #include "hdf_log.h" #include "uart_if.h" void UartTestSample(void) { int32_t ret; - uint32_t port; + uint32_t port; + uint32_t baud; DevHandle handle = NULL; uint8_t wbuff[5] = { 1, 2, 3, 4, 5 }; uint8_t rbuff[5] = { 0 }; struct UartAttribute attribute; - attribute.dataBits = UART_ATTR_DATABIT_7; /* Enable 7 bits to be transferred each time. */ - attribute.parity = UART_ATTR_PARITY_NONE; /* Disable parity check. */ - attribute.stopBits = UART_ATTR_STOPBIT_1; /* Set the stop bit to 1. */ - attribute.rts = UART_ATTR_RTS_DIS; /* Disable the RTS signal. */ - attribute.cts = UART_ATTR_CTS_DIS; /* Disable the CTS signal. */ - attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; /* Enable RX FIFO. */ - attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; /* Enable TX FIFO. */ - /* Enter the UART port number. */ - port = 1; - /* Open the UART device handle based on the port number. */ - handle = UartOpen(port); + + attribute.dataBits = UART_ATTR_DATABIT_7; // Transfer 7 bits each time. + attribute.parity = UART_ATTR_PARITY_NONE; // Disable parity check. + attribute.stopBits = UART_ATTR_STOPBIT_1; // Set the stop bit to 1. + attribute.rts = UART_ATTR_RTS_DIS; // Disable RTS. + attribute.cts = UART_ATTR_CTS_DIS; // Disable CTS. + attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // Enable RX FIFO. + attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // Enable TX FIFO. + + port = 1; // UART device port number. + + handle = UartOpen(port); // Open a UART device. if (handle == NULL) { - HDF_LOGE("UartOpen: failed!\n"); + HDF_LOGE("UartOpen: open uart_%u failed!\n", port); return; } - /* Set the UART baud rate to 9600. */ - ret = UartSetBaud(handle, 9600); - if (ret != 0) { - HDF_LOGE("UartSetBaud: failed, ret %d\n", ret); - goto _ERR; + + ret = UartSetBaud(handle, 9600); // Set the UART baud rate to 9600. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetBaud: set baud failed, ret %d\n", ret); + goto ERR; } - /* Set UART device attributes. */ - ret = UartSetAttribute(handle, &attribute); - if (ret != 0) { - HDF_LOGE("UartSetAttribute: failed, ret %d\n", ret); - goto _ERR; + + ret = UartGetBaud(handle, &baud); // Obtain the UART baud rate. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartGetBaud: get baud failed, ret %d\n", ret); + goto ERR; + } + + ret = UartSetAttribute(handle, &attribute); // Set the attributes of the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetAttribute: set attribute failed, ret %d\n", ret); + goto ERR; + } + + ret = UartGetAttribute(handle, &attribute); // Obtain the attributes of the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartGetAttribute: get attribute failed, ret %d\n", ret); + goto ERR; } - /* Set the UART transmission mode to non-blocking mode. */ - ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK); - if (ret != 0) { - HDF_LOGE("UartSetTransMode: failed, ret %d\n", ret); - goto _ERR; + + ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK); // Set the UART transmission mode to non-block mode. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetTransMode: set trans mode failed, ret %d\n", ret); + goto ERR; } - /* Write 5-byte data to the UART device. */ - ret = UartWrite(handle, wbuff, 5); - if (ret != 0) { - HDF_LOGE("UartWrite: failed, ret %d\n", ret); - goto _ERR; + + ret = UartWrite(handle, wbuff, 5); // Write 5-byte data to the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartWrite: write data failed, ret %d\n", ret); + goto ERR; } - /* Read 5-byte data from the UART device. */ - ret = UartRead(handle, rbuff, 5); + + ret = UartRead(handle, rbuff, 5); // Read 5-byte data from the UART device. if (ret < 0) { - HDF_LOGE("UartRead: failed, ret %d\n", ret); - goto _ERR; + HDF_LOGE("UartRead: read data failed, ret %d\n", ret); + goto ERR; } -_ERR: - /* Close the UART device handle. */ - UartClose(handle); +ERR: + UartClose(handle); // Close the UART device. + return ret; } ``` diff --git a/en/device-dev/driver/driver-platform-uart-develop.md b/en/device-dev/driver/driver-platform-uart-develop.md index cf3393d01505ceccb7c34b643a9ce218f148ce6c..eb08e87a54b68cae652c70e9018e9c0e73d26704 100644 --- a/en/device-dev/driver/driver-platform-uart-develop.md +++ b/en/device-dev/driver/driver-platform-uart-develop.md @@ -1,256 +1,293 @@ # UART - ## Overview -In the Hardware Driver Foundation (HDF), the Universal Asynchronous Receiver/Transmitter (UART) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. - ![image](figures/independent-service-mode.png) +A UART is connected to other modules through two wires (as shown in Figure 1) or four wires (as shown in Figure 2). + - TX: UART transmitter. It is connected to the RX of the peer UART. + - RX: UART receiver. It is connected to the TX of the peer UART. + - RTS: Request to Send signal, indicating whether the local UART is ready to receive data. It is connected to the CTS of the peer UART. + - CTS: Clear to Send signal, indicating whether the local UART is allowed to send data to the peer end. It is connected to the RTS of the peer UART. -## Available APIs +**Figure 1** Two-wire UART communication -**UartHostMethod**: +![image1](figures/2-wire-uart-communication.png "2-wire-uart-communication") +**Figure 2** Four-wire UART communication -``` + ![image2](figures/4-wire-uart-communication.png "4-wire-uart-communication") + +The UART transmitter and receiver must have the same settings on particular attributes, such as the baud rate and data format (start bit, data bits, parity bit, and stop bit) before they start to communicate. A UART sends data to the peer end over the TX and receives data from the peer end over the RX. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. + +### Basic Concepts + +- Asynchronous communication + + In asynchronous communication, data is transmitted in frames of characters or bytes. Frames are sent and received one by one through the transmission line. The transmitter and receiver have their own clocks to control data sending and receiving. The two clock sources are independent and not synchronized with each other. + + When data is sent one character at a time, the time interval between two characters is not fixed, but the time interval between two adjacent bits in a character frame is fixed. + +- Full-duplex transmission + + A duplex communication mode allows data to be transmitted in both directions at the same time. A duplex communication channel is equivalent to two simplex communication channels operating in opposite directions at the same time. In full-duplex mode, signals can be transmitted bidirectionally at the same time. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the UART uses the independent service mode (see Figure 3) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The UART module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a UART device, reading or writing data of the specified length, setting or obtaining the baud rate or attributes of a UART device, and setting the transmission mode. +- Core layer: provides the capabilities of adding or removing a UART controller, and managing UART devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 3** Independent service mode + +![image3](figures/independent-service-mode.png) + +## Development Guidelines + +### When to Use + +The UART module is widely used to implement low-speed serial communication between devices, for example, output the printing information. It can also connect to a variety of external GPS and Bluetooth devices. Before using your UART devices with OpenHarmony, you need to perform UART driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the PWM controller by calling the UART APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/uart/uart_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**UartHostMethod**: + +```c struct UartHostMethod { - int32_t (*Init)(struct UartHost *host); - int32_t (*Deinit)(struct UartHost *host); - int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size); - int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size); - int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate); - int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate); - int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute); - int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute); - int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode); - int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table); + int32_t (*Init)(struct UartHost *host); + int32_t (*Deinit)(struct UartHost *host); + int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size); + int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size); + int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate); + int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate); + int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute); + int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute); + int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode); + int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table); }; ``` - **Table 1** Description of the callback functions in UartHostMethod +**Table 1** Hook functions in UartHostMethod | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | | Init | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Initializes a UART device.| | Deinit | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Deinitializes a UART device.| -| Read | **host**: structure pointer to the UART controller at the core layer.
**size**: data size, which is of the uint32_t type.| **data**: pointer to the data read. The value is of the uint8_t type. | HDF_STATUS| Reads data.| -| Write | **host**: structure pointer to the UART controller at the core layer.
**data**: pointer to the data to write. The value is of the uint8_t type.
**size**: data size, which is of the uint32_t type. | –| HDF_STATUS| Writes data.| -| SetBaud | **host**: structure pointer to the UART controller at the core layer.
**baudRate**: pointer to the baud rate to set. The value is of the uint32_t type. | –| HDF_STATUS| Sets the baud rate.| -| GetBaud | **host**: structure pointer to the UART controller at the core layer.| **baudRate**: pointer to the baud rate obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the current baud rate.| -| GetAttribute | **host**: structure pointer to the UART controller at the core layer.| **attribute**: structure pointer to the attribute obtained. For details, see **UartAttribute** in **uart_if.h**. | HDF_STATUS| Obtains UART attributes.| -| SetAttribute | **host**: structure pointer to the UART controller at the core layer.
**attribute**: structure pointer to the attribute to set. | –| HDF_STATUS| Sets UART attributes.| -| SetTransMode | **host**: structure pointer to the UART controller at the core layer.
**mode**: transfer mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transfer mode.| -| PollEvent | **host**: structure pointer to the UART controller at the core layer.
**filep**: void pointer to a file.
**table**: void pointer to poll_table.| –| HDF_STATUS| Polls for pending events.| - +| Read | **host**: structure pointer to the UART controller at the core layer.
**size**: size of the data to read, which is of the uint32_t type.| **data**: pointer to the data read, which is of the uint8_t type. | HDF_STATUS| Reads data.| +| Write | **host**: structure pointer to the UART controller at the core layer.
**data**: pointer to the data to write, which is of the uint8_t type.
**size**: size of the data to write, which is of the uint32_t type.| –| HDF_STATUS| Writes data.| +| SetBaud | **host**: structure pointer to the UART controller at the core layer.
**baudRate**: baud rate to set, which is of the uint32_t type.| –| HDF_STATUS| Sets the baud rate.| +| GetBaud | **host**: structure pointer to the UART controller at the core layer.| **baudRate**: pointer to the baud rate obtained, which is of the uint32_t type.| HDF_STATUS| Obtains the baud rate.| +| GetAttribute | **host**: structure pointer to the UART controller at the core layer.| **attribute**: structure pointer to the attributes obtained. For details, see **UartAttribute** in **uart_if.h**.| HDF_STATUS| Obtains UART attributes.| +| SetAttribute | **host**: structure pointer to the UART controller at the core layer.
**attribute**: structure pointer to the attributes to set.| –| HDF_STATUS| Sets UART attributes.| +| SetTransMode | **host**: structure pointer to the UART controller at the core layer.
**mode**: transmission mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transmission mode.| +| PollEvent | **host**: structure pointer to the UART controller at the core layer.
**filep**: void pointer to a file.
**table**: void pointer to the poll_table.| –| HDF_STATUS| Polls for the pending events.| -## How to Develop +### How to Develop -The UART module adaptation involves the following steps: +The UART module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **uart_config.hcs** file. - 3. Instantiate the UART controller object. - - Initialize **UartHost**. - - Instantiate **UartHostMethod** in the **UartHost** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **UartHostMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, such as the UART status control and response to interrupts. +### Example - -## Development Example - -The following uses **uart_hi35xx.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/uart/uart_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the UART driver adaptation. 1. Instantiate the driver entry. - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. - - In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. UART driver entry example: - - ``` + + ```c struct HdfDriverEntry g_hdfUartDevice = { .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_UART", // (Mandatory) The value must be the same as that in the .hcs file. - .Bind = HdfUartDeviceBind, // See the Bind function. - .Init = HdfUartDeviceInit, // See the Init function. - .Release = HdfUartDeviceRelease, //See the Release function. + .moduleName = "HDF_PLATFORM_UART", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HdfUartDeviceBind, // See the Bind function. + .Init = HdfUartDeviceInit, // See the Init function. + .Release = HdfUartDeviceRelease, // See the Release function. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_hdfUartDevice); + HDF_INIT(g_hdfUartDevice); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **uart_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **UartHost** members at the core layer. - - In this example, there is only one UART controller. If there are multiple UART controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **uart_config** file for each controller. - - - **device_info.hcs** configuration example: - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_uart :: device { - device0 :: deviceNode { - policy = 1; // The driver publishes services only for kernel-mode processes. - priority = 40; // Driver startup priority. - permission = 0644; // Permission for the driver to create a device node. - moduleName = "HDF_PLATFORM_UART"; // Driver name, which must be the same as moduleName in the HdfDriverEntry structure. - serviceName = "HDF_PLATFORM_UART_0"; // Unique name of the service published by the driver. The name is in the HDF_PLATFORM_UART_X format. X indicates the UART controller number. - deviceMatchAttr = "hisilicon_hi35xx_uart_0"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. - } - device1 :: deviceNode { - policy = 2; // The driver publishes services for both kernel- and user-mode processes. - permission = 0644; - priority = 40; - moduleName = "HDF_PLATFORM_UART"; - serviceName = "HDF_PLATFORM_UART_1"; - deviceMatchAttr = "hisilicon_hi35xx_uart_1"; - } - ... - } - } - } - } - ``` - - - **uart_config.hcs** configuration example - - - ``` - root { - platform { - template uart_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes. - match_attr = ""; - num = 0; // (Mandatory) Device number. - baudrate = 115200; // (Mandatory) Baud rate. Set the value based on service requirements. - fifoRxEn = 1; // (Mandatory) Enable FIFOs to be received. - fifoTxEn = 1; // (Mandatory) Enable FIFOs to be transferred. - flags = 4; // (Mandatory) Flag signal. - regPbase = 0x120a0000; // (Mandatory) Used for address mapping. - interrupt = 38; // (Mandatory) Interrupt number. - iomemCount = 0x48; // (Mandatory) Used for address mapping. - } - controller_0x120a0000 :: uart_controller { - match_attr = "hisilicon_hi35xx_uart_0";// (Mandatory) The value must be the same as that of deviceMatchAttr of the corresponding device in device_info.hcs. - } - controller_0x120a1000 :: uart_controller { - num = 1; - baudrate = 9600; - regPbase = 0x120a1000; - interrupt = 39; - match_attr = "hisilicon_hi35xx_uart_1"; - } - ... - //(Optional) Add more controller data. The node information must have been added in the device_info.hcs file. - } - } - ``` - -3. Initialize the **UartHost** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **UartHostMethod** in **UartHost** (so that the underlying driver functions can be called). - - - Defining a custom structure - - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **uart_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number, to the **UartHost** object at the core layer. +2. Configure attribute files. - + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two UART controllers as an example. If there are more UART controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **uart_config.hcs** are closely related to default values or value ranges of the **UartHost** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_uart :: device { + device0 :: deviceNode { + policy = 1; // The value 1 means to publish services only to the kernel-mode processes. + priority = 40; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_UART"; // Driver name, which must be the same as moduleName in the HdfDriverEntry structure. + serviceName = "HDF_PLATFORM_UART_0"; // Unique name of the service published by the driver. The name is in the HDF_PLATFORM_UART_X format. X indicates the UART controller number. + deviceMatchAttr = "hisilicon_hi35xx_uart_0";// Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. + } + device1 :: deviceNode { + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + permission = 0644; + priority = 40; + moduleName = "HDF_PLATFORM_UART"; + serviceName = "HDF_PLATFORM_UART_1"; + deviceMatchAttr = "hisilicon_hi35xx_uart_1"; + } + ... + } + } + } + } + ``` + + - **uart_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + template uart_controller { // Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + match_attr = ""; + num = 0; // (Mandatory) Port number. + baudrate = 115200; // (Mandatory) Baud rate. + fifoRxEn = 1; // (Mandatory) Enable RX FIFO. + fifoTxEn = 1; // (Mandatory) Enable TX FIFO. + flags = 4; // (Mandatory) flag signal. + regPbase = 0x120a0000; // (Mandatory) Register physical base address used for address mapping. + interrupt = 38; // (Mandatory) Interrupt number. + iomemCount = 0x48; // (Mandatory) Used for address mapping. + } + controller_0x120a0000 :: uart_controller { + match_attr = "hisilicon_hi35xx_uart_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr of the device in device_info.hcs. + } + controller_0x120a1000 :: uart_controller { + num = 1; + baudrate = 9600; + regPbase = 0x120a1000; + interrupt = 39; + match_attr = "hisilicon_hi35xx_uart_1"; + } + ... // Add node information for more UART devices. + } + } + ``` + + After the **uart_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // Relative path of the file. ``` - struct UartPl011Port { // Interface structure - int32_t enable; - unsigned long physBase; // Physical address - uint32_t irqNum; // Interrupt number - uint32_t defaultBaudrate; // Default baud rate - uint32_t flags; // Flags related to the following three macros + +3. Instantiate the UART controller object. + + Initialize the **UartHost** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **UartHostMethod** in **UartHost** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **uart_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the UART port number, to the object at the core layer. + + ```c + struct UartPl011Port { // Pin description structure customized. + int32_t enable; + unsigned long physBase; // Physical base address. + uint32_t irqNum; // IRQ number. + uint32_t defaultBaudrate; // Default baud rate. + uint32_t flags; // Flag signals related to the following three macros. #define PL011_FLG_IRQ_REQUESTED (1 << 0) #define PL011_FLG_DMA_RX_REQUESTED (1 << 1) #define PL011_FLG_DMA_TX_REQUESTED (1 << 2) - struct UartDmaTransfer *rxUdt; // DMA transfer - struct UartDriverData *udd; // The data structure is defined as follows: + struct UartDmaTransfer *rxUdt; // DMA transfer. + struct UartDriverData *udd; }; - struct UartDriverData { // Structure related to data transfer - uint32_t num; - uint32_t baudrate; // Baud rate (configurable) - struct UartAttribute attr; // Attributes, such as the data bit and stop bit, related to data transfer. - struct UartTransfer *rxTransfer; // Buffer (FIFO structure) - wait_queue_head_t wait; // Queuing signal related to conditional variables - int32_t count; // Data count - int32_t state; // UART controller state + struct UartDriverData { // Structure related to data transfer + uint32_t num; // Port number. + uint32_t baudrate; // Baud rate (configurable). + struct UartAttribute attr; // Attributes, such as the data bits and stop bit of the data to transfer. + struct UartTransfer *rxTransfer; // Buffer structure (FIFO structure) + wait_queue_head_t wait; // Queuing signal related to conditional variables + int32_t count; // Data count. + int32_t state; // UART controller state. #define UART_STATE_NOT_OPENED 0 #define UART_STATE_OPENING 1 #define UART_STATE_USEABLE 2 #define UART_STATE_SUSPENDED 3 - uint32_t flags; // Status flags + uint32_t flags; // Status flags. #define UART_FLG_DMA_RX (1 << 0) #define UART_FLG_DMA_TX (1 << 1) #define UART_FLG_RD_BLOCK (1 << 2) - RecvNotify recv; // Pointer to the function that receives serial port data. - struct UartOps *ops; // Custom function pointer structure. For details, see device/hisilicon/drivers/uart/uart_pl011.c. - void *private; // It stores the pointer to the start address of UartPl011Port for easy invocation. + RecvNotify recv; // Pointer to the function that receives serial port data. + struct UartOps *ops; // Custom function pointer structure. + void *private; // Private data. }; // UartHost is the controller structure at the core layer. The Init function assigns values to the members of UartHost. struct UartHost { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - uint32_t num; - OsalAtomic atom; - void *priv; // It stores the pointer to the start address of the vendor's custom structure for easy invocation. - struct UartHostMethod *method; // Hook at the core layer. You need to implement and instantiate its member functions. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + uint32_t num; // Port number. + OsalAtomic atom; // Atomic quantity. + void *priv; // Private data. + struct UartHostMethod *method; // Callback functions. }; ``` - - Instantiating **UartHostMethod** in **UartHost** (other members are initialized by **Bind**) + - Instantiate **UartHostMethod** in **UartHost**. - - ``` - // Example in uart_hi35xx.c: instantiate the hook. + ```c + // Instantiate the hook functions in uart_hi35xx.c. struct UartHostMethod g_uartHostMethod = { - .Init = Hi35xxInit, - .Deinit = Hi35xxDeinit, - .Read = Hi35xxRead, - .Write = Hi35xxWrite, - .SetBaud = Hi35xxSetBaud, - .GetBaud = Hi35xxGetBaud, - .SetAttribute = Hi35xxSetAttribute, - .GetAttribute = Hi35xxGetAttribute, - .SetTransMode = Hi35xxSetTransMode, - .pollEvent = Hi35xxPollEvent, + .Init = Hi35xxInit, // Initialize the device. + .Deinit = Hi35xxDeinit, // Deinitialize the device. + .Read = Hi35xxRead, // Receive data. + .Write = Hi35xxWrite, // Write data. + .SetBaud = Hi35xxSetBaud, // Set the baud rate. + .GetBaud = Hi35xxGetBaud, // Obtain the baud rate. + .SetAttribute = Hi35xxSetAttribute, // Set device attributes. + .GetAttribute = Hi35xxGetAttribute, //Obtain device attributes. + .SetTransMode = Hi35xxSetTransMode, // Set the transmission mode. + .pollEvent = Hi35xxPollEvent, // Polling for pending events. }; ``` - - **Bind** function + - Implement the **Bind** function. - **Input parameter**: + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS - **Table 2** Description of HDF_STATUS - | Status| Description| | -------- | -------- | | HDF_ERR_INVALID_OBJECT | Invalid controller object.| @@ -260,89 +297,85 @@ The following uses **uart_hi35xx.c** as an example to present the information re | HDF_SUCCESS | Initialization successful.| | HDF_FAILURE | Initialization failed.| - **Function description**: + Function description: Initializes the custom structure object and **UartHost**. - - ``` + ```c //uart_hi35xx.c static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device) { ... - return (UartHostCreate(device) == NULL)? HDF_FAILURE: HDF_SUCCESS;// (Mandatory) Call UartHostCreate. + return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // (Mandatory) Call UartHostCreate. } + // Description of UartHostCreate() in uart_core.c struct UartHost *UartHostCreate(struct HdfDeviceObject *device) { - struct UartHost *host = NULL; // Create UartHost. - ... - host = (struct UartHost *)OsalMemCalloc(sizeof(*host));// Allocate memory. + struct UartHost *host = NULL // Create UartHost. + ... + host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // Allocate memory. ... - host->device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. - device->service = &(host->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. - host->device->service->Dispatch = UartIoDispatch; // Assign values to Dispatch of service. - OsalAtomicSet(&host->atom, 0); // Initialize or set the atomic services. + host->device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. + device->service = &(host->service); // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. + host->device->service->Dispatch = UartIoDispatch; // Assign values to Dispatch() of service. + OsalAtomicSet(&host->atom, 0); // Initialize or set the atomic service. host->priv = NULL; host->method = NULL; return host; } ``` - - **Init** function + - Implement the **Init** function. - **Input parameter**: + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS** + HDF_STATUS - **Function description**: + Function description: - Initializes the custom structure object and **UartHost**, calls the **artAddDev** function at the core layer, and connects to the VFS. + Initialize the custom structure and **UartHost**, calls **UartAddDev()** at the core layer to add the UART controller, and accesses the VFS. - - ``` + ```c int32_t HdfUartDeviceInit(struct HdfDeviceObject *device) { int32_t ret; struct UartHost *host = NULL; HDF_LOGI("%s: entry", __func__); ... - host = UartHostFromDevice(device);// Forcibly convert to UartHost by using service. The values are assigned by Bind(). - ... - ret = Hi35xxAttach(host, device); // Initialize the UartHost object. - ... - host->method = &g_uartHostMethod; // Attach the UartHostMethod instance. + host = UartHostFromDevice(device); // Forcibly convert to UartHost by using service. The values are assigned by Bind(). + ... + ret = Hi35xxAttach(host, device); // Initialize the UartHost object. + ... + host->method = &g_uartHostMethod; // Attach the UartHostMethod instance. return ret; } // Initialize UartHost. static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device) { int32_t ret; - // udd and port are customized structure objects. Implement the related functions as required. - struct UartDriverData *udd = NULL; + struct UartDriverData *udd = NULL; // udd and port are custom structure objects. You can implement features as required. struct UartPl011Port *port = NULL; ... - // Steps 1 to 7 instantiate and assign values to the udd object, and then assign values to UartHost. - udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd));// Step 1 + // Steps 1 to 7 assign values to the udd object and then UartHost. + udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // Step 1 ... - port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port));// Step 2 + port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // Step 2 ... - udd->ops = Pl011GetOps(); // Step 3 Hook the functions for starting or stopping a device, setting attributes, and sending data. - udd->recv = PL011UartRecvNotify;// Step 4 Hook the data receiving notification function (conditional lock mechanism). - udd->count = 0; // Step 5 - port->udd = udd; // Step 6 Enable conversion between UartPl011Port and UartDriverData. - ret = UartGetConfigFromHcs(port, device->property);// Pass the attributes of HdfDeviceObject to the custom structure. - // The sample code is as follows: + udd->ops = Pl011GetOps(); // Step 3 Hook the functions for opening or closing a device, setting device attributes, and sending data. + udd->recv = PL011UartRecvNotify; // Step 4 Hook the data receiving notification function (conditional lock mechanism). + udd->count = 0; // Step 5. + port->udd = udd; // Step 6 Prerequisites for conversion between UartPl011Port and UartDriverData. + ret = UartGetConfigFromHcs(port, device->property); // Pass the attributes of HdfDeviceObject to the custom structure to perform related operations. The sample code is as follows: ... - udd->private = port; // Step 7 - - host->priv = udd; // (Mandatory) Enable conversion between UartHost and UartDriverData. - host->num = udd->num; // (Mandatory) UART device number - UartAddDev(host); // (Mandatory) Function (in uart_dev.c) used to register a character device node to the VFS so that the UART can be accessed through the virtual file node in user mode. + udd->private = port; // Step 7 + host->priv = udd; // (Mandatory) Prerequisites for conversion between UartHost and UartDriverData. + host->num = udd->num; // (Mandatory) UART device number. + UartAddDev(host); // (Mandatory) Function in uart_dev.c at the core layer used to register a character device node to the VFS so that the UART can be accessed through the virtual file node in user mode. return HDF_SUCCESS; } @@ -352,7 +385,7 @@ The following uses **uart_hi35xx.c** as an example to present the information re struct UartDriverData *udd = port->udd; struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); ... - // Extract the values based on the request and assign the values to the custom structure. + // Extract the values based on the request and assign the values to the custom structures. if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) { HDF_LOGE("%s: read busNum fail", __func__); return HDF_FAILURE; @@ -361,36 +394,35 @@ The following uses **uart_hi35xx.c** as an example to present the information re return 0; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + Input parameter: - **Return value**: + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: No value is returned. - **Function description**: + Function description: - Releases the memory and deletes the controller. This function assigns values to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** - > > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - ``` + ```c void HdfUartDeviceRelease(struct HdfDeviceObject *device) { struct UartHost *host = NULL; ... - host = UartHostFromDevice(device); // Forcibly convert HdfDeviceObject to UartHost by using service. For details about the value assignment, see the Bind function. - ... - if (host->priv != NULL) { - Hi35xxDetach(host); // Customized memory release function. - } - UartHostDestroy(host); // Call the function of the core layer to release the host. + host = UartHostFromDevice(device); // Forcible conversion from HdfDeviceObject to UartHost through the service member. For details about the value assignment, see the Bind function. + ... + if (host->priv != NULL) { + Hi35xxDetach(host); // Customized memory release function. For details, see the following. + } + UartHostDestroy(host); // Call the core layer function to release the host. } static void Hi35xxDetach(struct UartHost *host) @@ -398,13 +430,13 @@ The following uses **uart_hi35xx.c** as an example to present the information re struct UartDriverData *udd = NULL; struct UartPl011Port *port = NULL; ... - udd = host->priv; // The conversion from UartHost to UartDriverData is involved. - ... - UartRemoveDev (host); // Remove the VFS. - port = udd->private; // The conversion from UartDriverData to UartPl011Port is involved. - if (port != NULL) { - if (port->physBase != 0) { - OsalIoUnmap((void *)port->physBase);// Unmap addresses. + udd = host->priv; // The conversion from UartHost to UartDriverData is involved. + ... + UartRemoveDev (host); // Remove the VFS. + port = udd->private; // The conversion from UartDriverData to UartPl011Port is involved. + if (port != NULL) { + if (port->physBase != 0) { + OsalIoUnmap((void *)port->physBase); // Unmap addresses. } OsalMemFree(port); udd->private = NULL; @@ -413,3 +445,7 @@ The following uses **uart_hi35xx.c** as an example to present the information re host->priv = NULL; } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-watchdog-des.md b/en/device-dev/driver/driver-platform-watchdog-des.md index aa00d54869576d625e05eead5cbe237a81d560e8..5482f8542dbf0a40e317f77f13c7ebee913c456a 100644 --- a/en/device-dev/driver/driver-platform-watchdog-des.md +++ b/en/device-dev/driver/driver-platform-watchdog-des.md @@ -1,49 +1,83 @@ # Watchdog +## Overview -## **Overview** +### Function -A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. +A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. Generally, it has an input to feed the watchdog and an output to the reset pin of the system. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. +The watchdog module provides APIs for watchdog operations, including: -## Available APIs +- Opening or closing a watchdog +- Starting or stopping a watchdog +- Setting or obtaining the watchdog timeout period +- Obtaining the watchdog status +- Feeding a watchdog -**Table 1** Watchdog APIs +### Basic Concepts -| API| Description| -| -------- | -------- | -| WatchdogOpen | Opens a watchdog.| -| WatchdogClose | Closes a watchdog.| -| WatchdogStart | Starts a watchdog.| -| WatchdogStop | Stops a watchdog.| -| WatchdogSetTimeout | Sets the watchdog timeout duration.| -| WatchdogGetTimeout | Obtains the watchdog timeout duration.| -| WatchdogGetStatus | Obtains the watchdog status.| -| WatchdogFeed | Feeds a watchdog or resets a watchdog timer.| - -> ![](../public_sys-resources/icon-note.gif) **NOTE** -> -> All watchdog APIs provided in this document can be called only in kernel mode. +When the system works properly, a signal is output to the watchdog to prevent it from timing out. This operation is called watchdog feeding. If the watchdog is not fed within the specified time, the watchdog times out and a reset signal is sent to the system to reset the system. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The watchdog module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a watchdog, starting or stopping a watchdog, setting or obtaining the watchdog timeout period, and feeding a watchdog +- Core layer: provides the capabilities of adding or removing a watchdog controller and managing watchdog devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode +![image1](figures/independent-service-mode.png "Watchdog independent service mode") ## Usage Guidelines +### When to Use -### How to Use +Watchdogs are used to automatically detect the software exceptions that cannot be directly observed and reset the system when an exception is detected. -The figure below shows how to use the watchdog APIs. +### Available APIs -Figure 1 Using watchdog APIs +The following table describes the APIs provided by the watchdog module. -![image](figures/using-watchdog-process.png) +**Table 1** Watchdog APIs +| API| Description| +| -------- | -------- | +| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | Opens a watchdog.| +| void WatchdogClose(DevHandle handle) | Closes a watchdog.| +| int32_t WatchdogStart(DevHandle handle) | Starts a watchdog.| +| int32_t WatchdogStop(DevHandle handle) | Stops a watchdog.| +| int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | Sets the watchdog timeout duration.| +| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | Obtains the watchdog timeout duration.| +| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | Obtains the watchdog status.| +| int32_t WatchdogFeed(DevHandle handle) | Feeds a watchdog or resets a watchdog timer.| + +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All watchdog APIs described in this document can be used in kernel mode and user mode. -### Opening a Watchdog +### How to Develop -Use **WatchdogOpen()** to open a watchdog. A system may have multiple watchdogs. You need to specify the ID of the watchdog to open. +The following figure shows how to use the watchdog driver APIs. -``` -DevHandle WatchdogOpen(int16_t wdtId); +**Figure 2** Using watchdog driver APIs + +![image2](figures/using-watchdog-process.png) + +#### Opening a Watchdog + +Before operating a watchdog, you need to use **WatchdogOpen()** to open a watchdog. A system may have multiple watchdogs. You need to specify the ID of the watchdog to open. + +```c +DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle); ``` **Table 2** Description of WatchdogOpen @@ -51,24 +85,26 @@ DevHandle WatchdogOpen(int16_t wdtId); | **Parameter**| **Description**| | -------- | -------- | | wdtId | Watchdog ID.| +| handle | Pointer to the watchdog device handle obtained.| | **Return Value**| **Description**| -| NULL | The operation failed.| -| **DevHandle** pointer| The operation is successful. The pointer to the watchdog device handle is returned.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| +```c +int16_t wdtId = 0; +int32_t ret; +DevHandle *handle = NULL; -``` -DevHandle handle = NULL; -handle = WatchdogOpen(0); /* Open watchdog 0.*/ -if (handle == NULL) { - HDF_LOGE("WatchdogOpen: failed, ret %d\n", ret); - return; +ret = WatchdogOpen(wdtId, handle); // Open watchdog 0. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); + return ret; } ``` +#### Obtaining the Watchdog Status -### Obtaining the Watchdog Status - -``` +```c int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); ``` @@ -79,25 +115,24 @@ int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); | handle | Watchdog device handle.| | status | Pointer to the watchdog status obtained.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| - -``` +```c int32_t ret; int32_t status; -/* Obtain the watchdog status. */ -ret = WatchdogGetStatus(handle, &status); -if (ret != 0) { - HDF_LOGE("WatchdogGetStatus: failed, ret %d\n", ret); - return; + +ret = WatchdogGetStatus(handle, &status); // Obtain the watchdog status. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogGetStatus: watchdog get status failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the Timeout Duration -### Setting the Timeout Duration -``` +```c int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); ``` @@ -108,25 +143,22 @@ int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); | handle | Pointer to the watchdog device handle.| | seconds | Timeout duration to set, in seconds.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -uint32_t timeOut = 60; -/* Set the timeout duration to 60 seconds. */ -ret = WatchdogSetTimeout(handle, timeOut); -if (ret != 0) { - HDF_LOGE("WatchdogSetTimeout: failed, ret %d\n", ret); - return; + +ret = WatchdogSetTimeout(handle, 2); // Set the timeout duration to 2 seconds. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogSetTimeout: watchdog set timeOut failed, ret:%d\n", ret); + return ret; } ``` +#### Obtaining the Timeout Duration -### Obtaining the Timeout Duration - -``` +```c int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); ``` @@ -135,27 +167,25 @@ int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); | **Parameter**| **Description**| | -------- | -------- | | handle | Pointer to the watchdog device handle.| -| seconds | Pointer to the timeout duration, in seconds.| +| seconds | Pointer to the watchdog timeout duration obtained.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| +```c + int32_t ret; + uint32_t timeOut; -``` -int32_t ret; -uint32_t timeOut; -/* Obtain the timeout duration, in seconds. */ -ret = WatchdogGetTimeout(handle, &timeOut); -if (ret != 0) { - HDF_LOGE("WatchdogGetTimeout: failed, ret %d\n", ret); - return; -} + ret = WatchdogGetTimeout(handle, &timeOut); // Obtain the watchdog timeout duration. + if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogGetTimeout: watchdog get timeOut failed, ret:%d\n", ret); + return ret; + } ``` +#### Starting a Watchdog -### Starting a Watchdog - -``` +```c int32_t WatchdogStart(DevHandle handle); ``` @@ -165,24 +195,22 @@ int32_t WatchdogStart(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Start the watchdog. */ -ret = WatchdogStart(handle); -if (ret != 0) { - HDF_LOGE("WatchdogStart: failed, ret %d\n", ret); - return; + +ret = WatchdogStart(handle); // Start a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogStart: start watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Feeding a Watchdog -### Feeding a Watchdog - -``` +```c int32_t WatchdogFeed(DevHandle handle); ``` @@ -192,24 +220,22 @@ int32_t WatchdogFeed(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Feed the watchdog. */ -ret = WatchdogFeed(handle); -if (ret != 0) { - HDF_LOGE("WatchdogFeed: failed, ret %d\n", ret); - return; + +ret = WatchdogFeed (handle); // Feed a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogFeed: feed watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Stopping a Watchdog -### Stopping a Watchdog - -``` +```c int32_t WatchdogStop(DevHandle handle); ``` @@ -219,26 +245,24 @@ int32_t WatchdogStop(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Stop the watchdog. */ -ret = WatchdogStop(handle); -if (ret != 0) { - HDF_LOGE("WatchdogStop: failed, ret %d\n", ret); - return; + +ret = WatchdogStop(handle); // Stop a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogStop: stop watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Closing a Watchdog -### Closing a Watchdog +After all operations are complete, use **WatchdogClose()** to close the watchdog. -If a watchdog is no longer required, call **WatchdogClose()** to close it. - -``` +```c void WatchdogClose(DevHandle handle); ``` @@ -248,29 +272,26 @@ void WatchdogClose(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| - +```c +WatchdogClose(handle); // Close a watchdog. ``` -/* Close the watchdog. */ -ret = WatchdogClose(handle); -``` - ## Example -The following example provides the complete development process. +The following uses the Hi3516D V300 development board as an example to describe how to operate the watchdog. The procedure is as follows: -1. Open a watchdog, set the timeout duration, and start the watchdog. +1. Open a watchdog. You need to pass in the watchdog ID. The device handle of the watchdog opened is returned. +2. Set the timeout duration for the watchdog. +3. Obtain the timeout duration of the watchdog. +4. Start the watchdog. +5. Feed the watchdog. +6. Stop the watchdog. +7. Close the watchdog. -2. Feed the watchdog periodically to ensure that the system is not reset due to timer expiry. -3. Stop feeding the watchdog and check whether the system is reset after the timer expires. - -Sample code: - -``` -#include "watchdog_if.h" -#include "hdf_log.h" -#include "osal_irq.h" -#include "osal_time.h" +```c +#include "watchdog_if.h" /* Header file of the standard watchdog APIs. */ +#include "hdf_log.h" /* Header file of the HDF log APIs. */ +#include "osal_time.h" /* Header file of the delay and sleep APIs. */ #define WATCHDOG_TEST_TIMEOUT 2 #define WATCHDOG_TEST_FEED_TIME 6 @@ -279,14 +300,16 @@ static int32_t TestCaseWatchdog(void) { int32_t i; int32_t ret; + int16_t wdtId = 0; + int32_t status; uint32_t timeout; - DevHandle handle = NULL; + DevHandle *handle = NULL; /* Open watchdog 0. */ - handle = WatchdogOpen(0); - if (handle == NULL) { - HDF_LOGE("Open watchdog failed!"); - return -1; + ret = WatchdogOpen(wdtId, handle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); + return ret; } /* Set the timeout duration. */ @@ -297,13 +320,19 @@ static int32_t TestCaseWatchdog(void) return ret; } - /* Obtain the timeout duration. */ + /* Obtain the timeout duration. */ ret = WatchdogGetTimeout(handle, &timeout); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret); WatchdogClose(handle); return ret; } + /* Check whether the timeout duration obtained is the same as the timeout duration set. */ + if (timeout != WATCHDOG_TEST_TIMEOUT) { + HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout); + WatchdogClose(handle); + return HDF_FAILURE; + } HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout); /* Start the watchdog. The timer starts. */ @@ -313,6 +342,19 @@ static int32_t TestCaseWatchdog(void) WatchdogClose(handle); return ret; } + /* Obtain the watchdog status and determine whether to start the watchdog. */ + status = WATCHDOG_STOP; + ret = WatchdogGetStatus(handle, &status); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + if (status != WATCHDOG_START) { + HDF_LOGE("%s: status is:%d after start", __func__, status); + WatchdogClose(handle); + return HDF_FAILURE; + } /* Feed the watchdog every other second. */ for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { @@ -328,15 +370,26 @@ static int32_t TestCaseWatchdog(void) /* Because the interval for feeding the watchdog is shorter than the timeout duration, the system does not reset, and logs can be printed normally. */ HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__); - /* Stop feeding the watchdog to make the timer expire. */ - for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { - HDF_LOGI("%s: waiting dog buck %d times... \n", __func__, i); - OsalSleep(1); + ret = WatchdogStop(handle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: stop fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + /* Obtain the watchdog status and determine whether to close the watchdog. */ + status = WATCHDOG_START; + ret = WatchdogGetStatus(handle, &status); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + if (status != WATCHDOG_STOP) { + HDF_LOGE("%s: status is:%d after stop", __func__, status); + WatchdogClose(handle); + return HDF_FAILURE; } - - /* The system resets when the timer expires. Theoretically, this log is not displayed. */ - HDF_LOGI("%s: dog hasn't back!!! \n", __func__, i); WatchdogClose(handle); - return -1; + return HDF_SUCCESS; } ``` diff --git a/en/device-dev/driver/driver-platform-watchdog-develop.md b/en/device-dev/driver/driver-platform-watchdog-develop.md index 67095216d8d78c6743e199b540fe38c9b5b18957..72691eb5d0cc0f728c60d9040dd4c212466a77a7 100644 --- a/en/device-dev/driver/driver-platform-watchdog-develop.md +++ b/en/device-dev/driver/driver-platform-watchdog-develop.md @@ -1,187 +1,198 @@ # Watchdog - ## Overview -A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. +### Function -In the Hardware Driver Foundation (HDF), the watchdog uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. Generally, it has an input to feed the watchdog and an output to the reset pin of the system. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. - **Figure 1** Independent service mode +### Basic Concepts - ![image](figures/independent-service-mode.png "Watchdog independent service mode") +When the system works properly, a signal is output to the watchdog to prevent it from timing out. This operation is called watchdog feeding. If the watchdog is not fed within the specified time, the watchdog times out and a reset signal is sent to the system to reset the system. +### Working Principles -## Available APIs +In the Hardware Driver Foundation (HDF), the watchdog module uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. -**WatchdogMethod**: +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. -``` +The watchdog module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a watchdog, starting or stopping a watchdog, setting or obtaining the watchdog timeout period, and feeding a watchdog +- Core layer: provides the capabilities of adding or removing a watchdog controller and managing watchdog devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![image](figures/independent-service-mode.png "Watchdog independent service mode") + +## Development Guidelines + +### When to Use + +Watchdogs are used to automatically detect the software exceptions that cannot be directly observed and reset the system when an exception is detected. Before using your watchdogs with OpenHarmony, you need to perform watchdog driver adaptation. The following describes how to do it. + +### **Available APIs** + +To enable the upper layer to successfully operate the watchdog controller by calling the watchdog APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/watchdog/watchdog_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**WatchdogMethod**: + +```c struct WatchdogMethod { - int32_t (*getStatus)(struct WatchdogCntlr *wdt, int32_t *status); - int32_t (*setTimeout)(struct WatchdogCntlr *wdt, uint32_t seconds); - int32_t (*getTimeout)(struct WatchdogCntlr *wdt, uint32_t *seconds); - int32_t (*start)(struct WatchdogCntlr *wdt); - int32_t (*stop)(struct WatchdogCntlr *wdt); - int32_t (*feed)(struct WatchdogCntlr *wdt); - int32_t (*getPriv)(struct WatchdogCntlr *wdt); // (Optional) If WatchdogCntlr has the priv member, instantiate priv. - void (*releasePriv)(struct WatchdogCntlr *wdt);// (Optional) + int32_t (*getStatus)(struct WatchdogCntlr *wdt, int32_t *status); + int32_t (*setTimeout)(struct WatchdogCntlr *wdt, uint32_t seconds); + int32_t (*getTimeout)(struct WatchdogCntlr *wdt, uint32_t *seconds); + int32_t (*start)(struct WatchdogCntlr *wdt); + int32_t (*stop)(struct WatchdogCntlr *wdt); + int32_t (*feed)(struct WatchdogCntlr *wdt); + int32_t (*getPriv)(struct WatchdogCntlr *wdt); // (Optional) If WatchdogCntlr has the priv member, instantiate priv. + void (*releasePriv)(struct WatchdogCntlr *wdt); // (Optional) }; ``` - **Table 1** Description of the callback functions in WatchdogMethod +**Table 1** Hook functions in WatchdogMethod | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| getStatus | **wdt**: structure pointer to the watchdog controller at the core layer.| **status**: int32_t pointer to the watchdog status (started or stopped).| HDF_STATUS| Obtains the watchdog status.| -| start | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Starts a watchdog.| -| stop | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS | Stops a watchdog.| -| setTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.
**seconds**: Timeout duration to set, in seconds. The value is of the uint32_t type. | – | HDF_STATUS | Sets the timeout duration for a watchdog. | -| getTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.| **seconds**: Pointer to the watchdog timeout duration obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the timeout duration of a watchdog.| +| getStatus | **wdt**: structure pointer to the watchdog controller at the core layer.| status: pointer to the watchdog status (opened or closed) obtained. The value is of the int32_t type.| HDF_STATUS| Obtains the watchdog status.| +| setTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.
**seconds**: watchdog timeout duration to set.| –| HDF_STATUS| Sets the watchdog timeout duration, in seconds. Ensure that the actual running time of the watchdog complies with the value set.| +| getTimeout | **wdt**: structure pointer to the watchdog controller at the core layer. | **seconds**: pointer to the timeout duration obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the watchdog timeout duration. | +| start | **wdt**: structure pointer to the watchdog controller at the core layer. | – | HDF_STATUS| Starts a watchdog. | +| stop | **wdt**: structure pointer to the watchdog controller at the core layer. | – | HDF_STATUS| Stops a watchdog. | | feed | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Feeds a watchdog. | +| getPriv | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Obtains the private data of the watchdog driver.| +| releasePriv | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Releases the private data of the watchdog driver.| + +### How to Develop +The watchdog module adaptation procedure is as follows: + +1. Instantiate the driver entry. +2. Configure attribute files. +3. Instantiate the watchdog controller object. +4. Debug the driver. -## How to Develop +### Example -The watchdog module adaptation involves the following steps: +The following uses the **//device_soc_hisilicon/common/platform/watchdog/watchdog_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the watchdog driver adaptation. 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. -2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **watchdog_config.hcs** file. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF framework, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. -3. Instantiate the watchdog controller object. - - Initialize **WatchdogCntlr**. - - Instantiate **WatchdogMethod** in the **WatchdogCntlr** object. - + Watchdog driver entry example: - > ![](../public_sys-resources/icon-note.gif) **NOTE** - > - > For details about the functions in **WatchdogMethod**, see [Available APIs](#available-apis). + ```c + struct HdfDriverEntry g_watchdogDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxWatchdogBind, // See the Bind function. + .Init = Hi35xxWatchdogInit, // See the Init function. + .Release = Hi35xxWatchdogRelease, // See the Release function. + .moduleName = "HDF_PLATFORM_WATCHDOG", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + }; + HDF_INIT(g_watchdogDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + ``` -4. Debug the driver. - - (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether the watchdog timer is successfully set. +2. Configure attribute files. + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses one watchdog controller as an example. If there are more watchdog controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **watchdog_config.hcs** are closely related to default values or value ranges of the **WatchdogCntlr** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + device_watchdog :: device { // Device node. + device0 :: deviceNode { // DeviceNode of the driver. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 20; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_WATCHDOG"; // (Mandatory) Driver name, which must be the same as that of moduleName in the driver entry structure. + serviceName = "HDF_PLATFORM_WATCHDOG_0"; // (Mandatory) Unique name of the service released by the driver. + deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) Controller private data, which must be the same as the value of match_attr in watchdog_config.hcs. + } + } + } + } + ``` -## Development Example + - **watchdog_config.hcs** example: + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + template watchdog_controller { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + id = 0; // Watchdog ID. + match_attr = ""; + regBase = 0x12050000; // (Mandatory) Physical base address used for address mapping. + regStep = 0x1000; // (Mandatory) Register offset step used for address mapping. + } + controller_0x12050000 :: watchdog_controller { // (Mandatory) Keyword for matching the private data of the device driver. + match_attr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + } + // Add node information for each watchdog device. + ... + } + } + ``` -The following uses **watchdog_hi35xx.c** as an example to present the information required for implementing device functions. + After the **watchdog_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. -1. Instantiate the driver entry. - - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - - Watchdog driver entry example: - - ``` - struct HdfDriverEntry g_watchdogDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxWatchdogBind, // See the Bind function. - .Init = Hi35xxWatchdogInit, // See the Init function. - .Release = Hi35xxWatchdogRelease, // See the Release function. - .moduleName = "HDF_PLATFORM_WATCHDOG",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. - }; - HDF_INIT(g_watchdogDriverEntry);// Call HDF_INIT to register the driver entry with the HDF. - ``` - -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the component attributes in the **watchdog_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **WatchdogCntlr** members at the core layer. - - In this example, there is only one watchdog controller. If there are multiple watchdog controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **watchdog_config** file for each controller. - - - **device_info.hcs** configuration example: - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - device_watchdog :: device {// Device node. - device0:: deviceNode { // Device node of the driver. - policy = 1; // Policy for the driver to provide services. - priority = 20; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "HDF_PLATFORM_WATCHDOG"; - // (Mandatory) Driver name. The value must be the same as that of moduleName in the driver entry structure. - serviceName = "HDF_PLATFORM_WATCHDOG_0"; - // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; - // (Mandatory) Keyword matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. - } - } - } - } - ``` - - - **watchdog_config.hcs** configuration example: - - - ``` - root { - platform { - template watchdog_controller {// (Mandatory) Template configuration. In the template, you can configure the common parameters shared by device nodes. - id = 0; - match_attr = ""; - regBase = 0x12050000; // (Mandatory) Used for address mapping. - regStep = 0x1000; // (Mandatory) Used for address mapping. - } - controller_0x12050000 :: watchdog_controller {// (Mandatory) Keyword for matching the private data of the device driver. - match_attr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - } - // Configure this parameter when there are multiple watchdogs. - ... - } - } - ``` - -3. Initialize the **WatchdogCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **WatchdogMethod** in **WatchdogCntlr** (so that the underlying driver functions can be called). - - Defining a custom structure - - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **watchdog_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the index and the number of pins, to the **WatchdogCntlr** object at the core layer. - - + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // Relative path of the file. ``` + +3. Instantiate the watchdog controller object. + + Initialize the **WatchdogCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **WatchdogMethod** in **WatchdogCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **watchdog_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the watchdog ID, to the object at the core layer. + + ```c struct Hi35xxWatchdog { - struct WatchdogCntlr wdt; // (Mandatory) Carrier that connects the upper and underlying layers. For details, see the following description. - OsalSpinlock lock; - volatile unsigned char *regBase;// [Mandatory] Used for address mapping. - uint32_t phyBase; // (Mandatory) Used for address mapping. - uint32_t regStep; // (Mandatory) Used for address mapping. + struct WatchdogCntlr wdt; // (Mandatory) Control object of the core layer. For details, see the following description. + OsalSpinlock lock; // (Mandatory) You need to implement the spinlock for your watchdog. + volatile unsigned char *regBase; // (Mandatory) Register base address used for address mapping. + uint32_t phyBase; // (Mandatory) Physical base address used for address mapping. + uint32_t regStep; // (Mandatory) Register offset step used for address mapping. }; - // WatchdogCntlr is the core layer controller structure. The Init function assigns values to the members of WatchdogCntlr. - struct WatchdogCntlr { - struct IDeviceIoService service;// Driver service. - struct HdfDeviceObject *device; // Driver device. - OsalSpinlock lock; // This variable is called by the HDF core layer to implement the spinlock function. - struct WatchdogMethod *ops; // Callbacks. - int16_t wdtId // ID of the watchdog device. - void *priv; // Pointer to the driver's private data. + + struct WatchdogCntlr { // WatchdogCntlr is the controller structure at the core layer. The Init function assigns values to WatchdogCntlr. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + OsalSpinlock lock; // Spinlock. + struct WatchdogMethod *ops; // Hook functions. + int16_t wdtId; // Watchdog ID. + void *priv; // Private data. }; ``` - - - Instantiating **WatchdogMethod** in **WatchdogCntlr** (other members are initialized by **Init** and **Bind**) - - ``` - static struct WatchdogMethod g_method = { - .getStatus = Hi35xxWatchdogGetStatus, - .start = Hi35xxWatchdogStart, - .stop = Hi35xxWatchdogStop, - .setTimeout = Hi35xxWatchdogSetTimeout, - .getTimeout = Hi35xxWatchdogGetTimeout, - .feed = Hi35xxWatchdogFeed, + - Instantiate **WatchdogMethod** in **WatchdogCntlr**. + + ```c + static struct WatchdogMethod g_method = { // Instantiate the hook functions. + .getStatus = Hi35xxWatchdogGetStatus, // Obtain the watchdog status. + .start = Hi35xxWatchdogStart, // Start the watchdog. + .stop = Hi35xxWatchdogStop, // Stop the watchdog. + .setTimeout = Hi35xxWatchdogSetTimeout, // Set the watchdog timeout duration. + .getTimeout = Hi35xxWatchdogGetTimeout, //Obtain the watchdog timeout duration. + .feed = Hi35xxWatchdogFeed, // Feed the watchdog. }; ``` - - **Init** and **Bind** functions + - Implement the **Init** and **Bind** functions. Input parameter: @@ -189,15 +200,13 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Return value: - HDF_STATUS + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** Description of HDF_STATUS - The table below lists some status. For more information, see **HDF_STATUS** in the /drivers/framework/include/utils/hdf_base.h file. - - **Table 2** HDF_STATUS - | Status| Description| | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | Failed to locate the watchdog device.| + | HDF_ERR_INVALID_OBJECT | Invalid controller object.| | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.| | HDF_ERR_IO | I/O error.| | HDF_SUCCESS | Initialization successful.| @@ -205,42 +214,41 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Function description: - Initializes the custom structure object and **WatchdogCntlr**, and calls the **WatchdogCntlrAdd** function at the core layer. - - - ``` + Initializes the custom structure object and **WatchdogCntlr**, and calls **WatchdogCntlrAdd()** at the core layer to add the watchdog controller. + + ```c // Generally, the Init function initializes the members of the Hi35xxWatchdog structure based on the attribute values in **HdfDeviceObject**. - // In this example, the Bind function initializes the Hi35xxWatchdog structure. + // In watchdog_hi35xx.c, it is implemented by the Bind function. static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device) { - (void)device; - return HDF_SUCCESS; + (void)device; + return HDF_SUCCESS; } - + static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device) { - int32_t ret; - struct Hi35xxWatchdog *hwdt = NULL; - ... - hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt));// Apply for memory for the Hi35xxWatchdog structure. - ... - hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // Address mapping - ... - hwdt->wdt.priv = (void *)device->property;// (Optional) Assign the device attribute values to priv. However, priv is not called subsequently. - //If the priv member is required, instantiate getPriv() and releasePriv() of WatchdogMethod. - hwdt->wdt.ops = &g_method; // (Mandatory) Assign the instantiated objects to the ops members so that the top layer can invoke the WatchdogMethod functions. - hwdt->wdt.device = device; // (Mandatory) Enable conversion between HdfDeviceObject and WatchdogcCntlr. - ret = WatchdogCntlrAdd(&hwdt->wdt); // (Mandatory) Call this function to initialize the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. - if (ret != HDF_SUCCESS) { // If the operation fails, release the resources used by Init(). - OsalIoUnmap((void *)hwdt->regBase); - OsalMemFree(hwdt); - return ret; - } - return HDF_SUCCESS; + int32_t ret; + struct Hi35xxWatchdog *hwdt = NULL; + ... + hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); // Allocate memory for the Hi35xxWatchdog structure pointer. + ... + hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // Address mapping. + ... + hwdt->wdt.priv = (void *)device->property; // (Mandatory) Use the device attributes to assign values to privr, but priv is not called later. + //If the priv member is required, instantiate getPriv() and releasePriv() of WatchdogMethod. + hwdt->wdt.ops = &g_method; // (Mandatory) Hook the WatchdogMethod instance. + hwdt->wdt.device = device; // (Mandatory) Enable conversion between HdfDeviceObject and WatchdogcCntlr. + ret = WatchdogCntlrAdd(&hwdt->wdt); // (Mandatory) Call this function to initialize the core layer structure. The driver can access the platform core layer only after a success signal is returned. + if (ret != HDF_SUCCESS) { // If the operation fails, remove the mapping and release the resources requested by the Init function. + OsalIoUnmap((void *)hwdt->regBase); + OsalMemFree(hwdt); + return ret; + } + return HDF_SUCCESS; } ``` - - - **Release** function + + - Implement the **Release** function. Input parameter: @@ -252,28 +260,29 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Function description: - Releases driver resources. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, **Release()** can be called to release driver resources. The **Release()** function must contain the operations for releasing the memory and deleting the controller. + Releases driver resources. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. The **Release()** function must contain the operations for releasing the memory and deleting the controller. - All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - - ``` + ```c static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device) { - struct WatchdogCntlr *wdt = NULL; - struct Hi35xxWatchdog *hwdt = NULL; - ... - wdt = WatchdogCntlrFromDevice(device);// Use service to convert HdfDeviceObject to WatchdogCntlr. - // return (device == NULL) ? NULL : (struct WatchdogCntlr *)device->service; - if (wdt == NULL) { - return; - } - WatchdogCntlrRemove(wdt); // Core layer function used to execute wdt->device->service = NULL and release cntlr->lock. - hwdt = (struct Hi35xxWatchdog *)wdt; // Convert WatchdogCntlr to HimciHost. - if (hwdt->regBase != NULL) { // Unmap addresses. - OsalIoUnmap((void *)hwdt->regBase); - hwdt->regBase = NULL; - } - OsalMemFree(hwdt); // Release the memory occupied by the vendor-defined objects. + struct WatchdogCntlr *wdt = NULL; + struct Hi35xxWatchdog *hwdt = NULL; + ... + wdt = WatchdogCntlrFromDevice(device); // (Mandatory) Obtain WatchdogCntlr through device. + ... + if (wdt == NULL) { + return; + } + WatchdogCntlrRemove(wdt); // (Mandatory) Call WatchdogCntlrRemove to release the WatchdogCntlr object. + hwdt = (struct Hi35xxWatchdog *)wdt; // Convert WatchdogCntlr to Hi35xxWatchdog. + if (hwdt->regBase != NULL) { // (Mandatory) Remove the address mapping. + OsalIoUnmap((void *)hwdt->regBase); + hwdt->regBase = NULL; + } + OsalMemFree(hwdt); // (Mandatory) Release the memory occupied by the custom object. } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/figures/4-wire-uart-communication.png b/en/device-dev/driver/figures/4-wire-uart-communication.png index 6ac63e41108abd4776621356c3034fc52b6f436f..b5e82f09cd764b0cd9dc835e55f8f878b77eb91e 100644 Binary files a/en/device-dev/driver/figures/4-wire-uart-communication.png and b/en/device-dev/driver/figures/4-wire-uart-communication.png differ diff --git a/en/device-dev/driver/figures/light_driver_model.png b/en/device-dev/driver/figures/light_driver_model.png index a2adb1c908d3a263299248c95ad5c1e1f202e61f..b08018af22fb7d3207dc75b597decf63bb3b4cdb 100644 Binary files a/en/device-dev/driver/figures/light_driver_model.png and b/en/device-dev/driver/figures/light_driver_model.png differ diff --git a/en/device-dev/driver/figures/using-I3C-process.png b/en/device-dev/driver/figures/using-I3C-process.png new file mode 100644 index 0000000000000000000000000000000000000000..0a9e19716387232216989f4f7cd3970aeb39ef6b Binary files /dev/null and b/en/device-dev/driver/figures/using-I3C-process.png differ diff --git a/en/device-dev/driver/figures/using-MIPI-CSI-process.png b/en/device-dev/driver/figures/using-MIPI-CSI-process.png index b5bae0cb889b7e655d1b2f650d6e42759b70b948..d4a28e15a598cd7d2ab1931a5bcafe896b864127 100644 Binary files a/en/device-dev/driver/figures/using-MIPI-CSI-process.png and b/en/device-dev/driver/figures/using-MIPI-CSI-process.png differ diff --git a/en/device-dev/driver/figures/using-PWM-process.png b/en/device-dev/driver/figures/using-PWM-process.png index 5ec65550b9996307c0432a7651a27d034628717e..b9be0064076e4eb4b981f5a9bf5068ddf6d1f19d 100644 Binary files a/en/device-dev/driver/figures/using-PWM-process.png and b/en/device-dev/driver/figures/using-PWM-process.png differ diff --git a/en/device-dev/driver/figures/using-UART-process.png b/en/device-dev/driver/figures/using-UART-process.png index 3259fd96752d1244da00eb9be85651997943d316..d4acf5276d878a09438eaada4e4c7441f227fae2 100644 Binary files a/en/device-dev/driver/figures/using-UART-process.png and b/en/device-dev/driver/figures/using-UART-process.png differ diff --git a/en/device-dev/driver/figures/using-watchdog-process.png b/en/device-dev/driver/figures/using-watchdog-process.png index 7a2bc982174295faea3be1c0e51d05bcdf69b523..6ad4752401bd7fc60680e552015ba2d70d6796ec 100644 Binary files a/en/device-dev/driver/figures/using-watchdog-process.png and b/en/device-dev/driver/figures/using-watchdog-process.png differ diff --git a/en/device-dev/faqs/faqs-environment-setup.md b/en/device-dev/faqs/faqs-environment-setup.md index 8a33cb7bafd0efbb166688b782ddd6b9a0d87f23..d087258233f14bfb59332dd874714ecdf49bbfe8 100644 --- a/en/device-dev/faqs/faqs-environment-setup.md +++ b/en/device-dev/faqs/faqs-environment-setup.md @@ -83,12 +83,10 @@ 1. Check that your computer has a good network connection. If the network connection is unstable, rectify the network fault and reinstall hb. 2. If the network is functional, run the following commands to install hb by specifying a temporary PyPI source: - + ``` python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ohos-build ``` - - ### Python 3 Installation @@ -109,9 +107,8 @@ - **Solution** 1. Run the **apt-get install gcc** command to install GCC online. - 2. Install Python 3. - + 2. Install Python 3. #### "-bash: make: command not found" @@ -134,8 +131,6 @@ 2. Install Python 3. - - #### "zlib not available" @@ -240,3 +235,43 @@ sudo apt-get remove python3-apt sudo apt-get install python3-apt ``` + +### Updating Source Code +- **Scenario** + + Update the OpenHarmony source code downloaded to the branch (for example, Master) source code. + +- **Solution** + + 1. Go to the OpenHarmony root directory. + + Check that the root directory has a **.repo** folder, which is usually hidden. If the **.repo** folder does not exist, decompress the obtained source code package again to obtain complete source code. + + 2. Run the **repo init** command to initialize the source code repository information. + + In the command, *branch* indicates the branch name, for example, **master** or **OpenHarmony-3.2-Beta3**. + ```shell + repo init -u https://gitee.com/openharmony/manifest -b branch --no-repo-verify + ``` + + 3. Download and update the source code file. + + ```shell + repo sync -c + repo forall -c 'pwd;git lfs pull' + ``` + To keep the working directory clean, run the following command: + + >![icon-note.gif](public_sys-resources/icon-caution.gif) **CAUTION**
+ >This command will reset the local workspace and delete files or directories that are not managed by gitee. Exercise caution when performing this operation. + ```shell + repo forall -c 'git reset --hard;git clean -fdx' + ``` + + 4. Run the prebuilts script to install the compiler and binary tool. + + ```shell + bash build/prebuilts_download.sh + ``` + + diff --git a/en/device-dev/faqs/faqs-startup.md b/en/device-dev/faqs/faqs-startup.md index a74cda50e97bdb84531c8c89a79888fc53e654c8..a9a319ce0f683f784a2951e491c50a089e399ce4 100644 --- a/en/device-dev/faqs/faqs-startup.md +++ b/en/device-dev/faqs/faqs-startup.md @@ -148,7 +148,7 @@ Improper caps configuration leads to a configuration error. The error informatio [ 4.620014] [pid=1 0][Init][ERROR][init_service_manager.c:818]GetServiceSecon secon section not found, skip [ 4.620216] [pid=1 0][Init][ERROR][init_service_manager.c:818]GetServiceSecon secon section not found, skip [ 4.620608] [pid=1 0][Init][ERROR][init_capability.c:119]service=mmi_uinput_service not support caps = CAP_DC_OVERRIDE caps 41 -``` + ``` **Possible Cause** 1. The caps configuration is not supported by the kernel. @@ -191,6 +191,42 @@ None. Enter the hdc shell mode on the device. Then, run the **sandbox -s service_name** command to move the current service to the sandbox, and run shell commands such as **ls** to view the sandbox directory. For details, see the [sandbox command description](../subsystems/subsys-boot-init-plugin.md). +### Timestamp in the Ready Phase of Some Bootevent Events Is 0 + +**Symptom** + +In manual bootevent mode, after device startup is completed and the **dump_service all bootevent** command is executed, the timestamps in the ready phase of some events are 0. + +**Possible Cause** + +1. The service does not send the bootevent event. +2. The service sends the bootevent event, but does not have the related permission. + +**Solution** + +1. For case 1, make sure the related service sends the bootevent in the code. +2. For case 2, make sure the service has the permission to set the bootevent parameter. + +### Failed to Boot from Partition A/B + +**Symptom** + +After the image is burnt, the system cannot be started, and information similar to the following can be found in the serial port log: + +``` +wait for file:/dev/block/platform/fe310000.sdhci/by-name/system_b failed after 5 second. +Mount /dev/block/platform/fe310000.sdhci/by-name/system_b to /usr failed 2 +``` + +**Possible Cause** + +As indicated by the log, partition B is used as the boot partition. However, it is not found during startup, resulting in the startup failure. This issue occurs because the active slot value in the misc partition is set to 2 (partition B), but partition B is not burnt. + +**Solution** + +1. Clear the misc partition by burning the corresponding partition with an empty misc image, erase the active slot value, and restart the system from the default partition. +2. Burn the **system_b** and **vendor_b** images to the development board by using the partition table configured with partition B. Then, the development board can boot from partition B. + ## Application Spawning ### Failed to Start appspawn @@ -221,11 +257,15 @@ Applications fail to be started by running the cold start command. 1. Cold start is not enabled. 2. The parameter of the cold start command is incorrect. +3. The socket request times out. +4. SELinux is enabled. **Solution** -1. For case 1, run **param get appspawn.cold.boot** to check the cold start switch and run **param set appspawn.cold.boot true** to enable cold start. +1. For case 1, run **param get startup.appspawn.cold.boot** to check the cold start switch and run **param set startup.appspawn.cold.boot 1** to enable cold start. 2. For case 2, correct the parameter of the cold start command. +3. For case 3, run the **param set persist.appspawn.client.timeout 5** command to set the timeout period to 5. +4. For case 4, run the **setenforce 0** command to disable SELinux. ### Failed to Create the Application Sandbox diff --git a/en/device-dev/guide/device-camera-control-example.md b/en/device-dev/guide/device-camera-control-example.md index dbe3e5744342b584ae913d8bca561323a67c1dd4..6507f3def826e2d8c105e52ccb5176eedbecad8d 100644 --- a/en/device-dev/guide/device-camera-control-example.md +++ b/en/device-dev/guide/device-camera-control-example.md @@ -1,64 +1,64 @@ # Use Case -This use case takes **camera\_sample** \(contained in the source code\) as an example for photographing, recording, and previewing on the development board. +This use case takes **camera\_sample** \(contained in the source code\) as an example for photographing, recording, and previewing on the development board. -- You can obtain source code of the sample from **applications/sample/camera/media/camera\_sample.cpp**. -- Before running the sample camera, you need to compile, burn, and run the image. For details, see [Hi3516 Development Board](../quick-start/quickstart-lite-introduction-hi3516#section26131214194212). +- You can obtain source code of the sample from **applications/sample/camera/media/camera\_sample.cpp**. +- Before running the sample camera, you need to compile, burn, and run the image. For details, see [Quick Start Overview](../quick-start/quickstart-overview.md). >![](../public_sys-resources/icon-note.gif) **NOTE**
- >After the development board is started, the home screen is loaded and displayed above the media layer by default. To prevent covering **camera\_sample**, you should remove the home screen during compilation or packaging. - >How to Remove: In **build/lite/components/applications.json**, comment out or delete the **//applications/sample/camera/launcher:launcher\_hap** line from the **target** field of **camera\_sample\_app**. + >After the development board is started, the home screen is loaded and displayed above the media layer by default. To prevent covering **camera\_sample**, you should remove the home screen during compilation or packaging. + >How to Remove: In **build/lite/components/applications.json**, comment out or delete the **//applications/sample/camera/launcher:launcher\_hap** line from the **target** field of **camera\_sample\_app**. -- The compilation result of the sample code is stored in **out/hi3516dv300/ipcamera\_hi3516dv300\_liteos/dev\_tools/bin**. To ensure that the code can be executed on the development board, you can copy the file to a TF card through a card reader, or modify the compilation script of **camera\_sample** to copy the compilation result to **rootfs.img**. +- The compilation result of the sample code is stored in **out/hi3516dv300/ipcamera\_hi3516dv300\_liteos/dev\_tools/bin**. To ensure that the code can be executed on the development board, you can copy the file to a TF card through a card reader, or modify the compilation script of **camera\_sample** to copy the compilation result to **rootfs.img**. - Modify the first **output\_dir** in the source code path **applications/sample/camera/media/BUILD.gn**. + Modify the first **output\_dir** in the source code path **applications/sample/camera/media/BUILD.gn**. - - Before: **output\_dir = "$root\_out\_dir/dev\_ools"** - - After: **output\_dir = "$root\_out\_dir/"** + - Before: **output\_dir = "$root\_out\_dir/dev\_ools"** + - After: **output\_dir = "$root\_out\_dir/"** - Recompile the source code repository and burn the code into the development board. Then you can find the **camera\_sample** file in the **bin** directory of the board. + Recompile the source code repository and burn the code into the development board. Then you can find the **camera\_sample** file in the **bin** directory of the board. >![](../public_sys-resources/icon-notice.gif) **NOTICE**
- >You should insert a TF card \(up to 128 GB supported\) for photographing and video recording before system startup. This way, the TF card will be automatically mounted to the **/sdcard** directory. If you insert the TF card after the system is started, you have to manually mount the TF card. + >You should insert a TF card \(up to 128 GB supported\) for photographing and video recording before system startup. This way, the TF card will be automatically mounted to the **/sdcard** directory. If you insert the TF card after the system is started, you have to manually mount the TF card. >To view the photos and videos in the TF card, copy them to a computer. If you just want to preview photos and videos, you do not need to insert a TF card. - Perform the following steps to run the sample: -1. Run the **cd** command to go to the end path of the executable program and start **camera\_sample** by running the command in the following figure. +1. Run the **cd** command to go to the end path of the executable program and start **camera\_sample** by running the command in the following figure. - **Figure 1** Starting camera\_sample + **Figure 1** Starting camera\_sample ![](figures/starting-camera_sample.png "starting-camera_sample") - The control commands are displayed as shown in the preceding figure. Press **S** to stop the current operation \(including video recording and preview\), and press **Q** to exit the program. + The control commands are displayed as shown in the preceding figure. Press **S** to stop the current operation \(including video recording and preview\), and press **Q** to exit the program. -2. Press **1** to take a photo in JPG format. The photo is saved in the **/sdcard** directory and named **Capture\***. +2. Press **1** to take a photo in JPG format. The photo is saved in the **/sdcard** directory and named **Capture\***. - **Figure 2** Serial port logs displayed after the photographing command is executed + **Figure 2** Serial port logs displayed after the photographing command is executed ![](figures/serial-port-logs-displayed-after-the-photographing-command-is-executed.png "serial-port-logs-displayed-after-the-photographing-command-is-executed") To view the saved file, exit the program and enter the file system. To start the program again, return to the previous step. - **Figure 3** Saved files + **Figure 3** Saved files ![](figures/saved-files.png "saved-files") -3. Press **2** to start recording. The video file is in MP4 format and saved in the **/sdcard** directory with the name **Record\***. Press **S** to stop recording. +3. Press **2** to start recording. The video file is in MP4 format and saved in the **/sdcard** directory with the name **Record\***. Press **S** to stop recording. - **Figure 4** Serial port logs displayed after the recording command is executed + **Figure 4** Serial port logs displayed after the recording command is executed ![](figures/serial-port-logs-displayed-after-the-recording-command-is-executed.png "serial-port-logs-displayed-after-the-recording-command-is-executed") -4. Press **3** to start preview. The preview is displayed on the screen. Press **S** to stop preview. +4. Press **3** to start preview. The preview is displayed on the screen. Press **S** to stop preview. - **Figure 5** Serial port logs displayed after the preview command is executed + **Figure 5** Serial port logs displayed after the preview command is executed ![](figures/serial-port-logs-displayed-after-the-preview-command-is-executed.png "serial-port-logs-displayed-after-the-preview-command-is-executed") The following figure shows the preview. - **Figure 6** Preview effect + **Figure 6** Preview effect ![](figures/preview-effect.jpg "preview-effect") -5. Press **Q** to exit. +5. Press **Q** to exit. - **Figure 7** Serial port logs displayed after the exit command is executed + **Figure 7** Serial port logs displayed after the exit command is executed ![](figures/serial-port-logs-displayed-after-the-exit-command-is-executed.png "serial-port-logs-displayed-after-the-exit-command-is-executed") diff --git a/en/device-dev/guide/device-camera-visual-run.md b/en/device-dev/guide/device-camera-visual-run.md index fd583adfc26355621fae774b2f2745827f481abd..2d2d4a3de11bb03e921aab7b01069264ea0aea34 100644 --- a/en/device-dev/guide/device-camera-visual-run.md +++ b/en/device-dev/guide/device-camera-visual-run.md @@ -1,8 +1,8 @@ # Running on the Device -Before you install the application and run it on the development board, install the DevEco Device Tool by following operations provided in [HUAWEI DevEco Device Tool User Guide](https://device.harmonyos.com/en/docs/ide/user-guides/service_introduction-0000001050166905). Burn OpenHarmony into the development board, and run it on the board. For details about how to compile, burn, and run an image, see the [Hi3516 Development Board](../quick-start/Readme-EN.md). After the image is running normally and the system is started properly, perform the following steps to install or uninstall the application: +Before you install the application and run it on the development board, install the DevEco Device Tool by following operations provided in [HUAWEI DevEco Device Tool User Guide](https://device.harmonyos.com/en/docs/ide/user-guides/service_introduction-0000001050166905). Burn OpenHarmony into the development board, and run it on the board. For details about how to compile, burn, and run an image, see the [Quick Start Overview](../quick-start/quickstart-overview.md). After the image is running normally and the system is started properly, perform the following steps to install or uninstall the application: -1. Store the compiled unsigned application installation package and installation tool in an SD card and insert the SD card into the development board slot. The installation tool is in **dev\_tools** of the directory where the image file is generated. +1. Store the compiled unsigned application installation package and installation tool in an SD card and insert the SD card into the development board slot. The installation tool is in **dev\_tools** of the directory where the image file is generated. 2. Run the following command to disable signature verification, which is enabled by default for application installation: ``` @@ -15,11 +15,11 @@ Before you install the application and run it on the development board, install ./sdcard/dev_tools/bin/bm install -p /sdcard/airquality.hap ``` - The **dev\_tools** directory stores the installation tool, and **airquality.hap** is the application installation package. Replace it with actual the package name. + The **dev\_tools** directory stores the installation tool, and **airquality.hap** is the application installation package. Replace it with actual the package name. 4. After the application is installed, touch the application icon on the home screen to enter the application. - **Figure 1** Home screen + **Figure 1** Home screen ![](figures/home-screen.png "home-screen") 5. \(Optional\) Uninstall the application. diff --git a/en/device-dev/guide/device-outerdriver-demo.md b/en/device-dev/guide/device-outerdriver-demo.md index 3fc094240ba51b042aad340af51668ba58a6dc79..5ff943c22f1a91a94fea476159087766e72c4d59 100644 --- a/en/device-dev/guide/device-outerdriver-demo.md +++ b/en/device-dev/guide/device-outerdriver-demo.md @@ -20,13 +20,13 @@ The input driver model mainly consists of the device manager, common drivers, an In addition, the input driver model implements functions for reporting input data and parsing input device configurations. -For details about the input driver model, see [Touchscreen Overview](../driver/driver-peripherals-touch-des.md#section175431838101617). +For details about the input driver model, see [Touchscreen Overview](../driver/driver-peripherals-touch-des.md). ## Setting Up the Environment Follow the instructions in [Quick Start Overview](../quick-start/quickstart-overview.md). ->![](../public_sys-resources/icon-notice.gif) **NOTICE:** +>![](../public_sys-resources/icon-notice.gif) **NOTICE** >This development example applies to standard, small, and mini OpenHarmony systems. The following sections use the standard system as an example. You can refer to the specific guide for your system to set up the environment. ## Developing a Touchscreen Driver @@ -37,9 +37,9 @@ Complete the following tasks to adapt a touchscreen IC based on the input driver Configure the touchscreen driver description required for registering the driver with the HDF, for example, whether the driver is loaded and what is the loading priority. -You can configure the device driver description in the configuration file at **./drivers/adapter/khdf/linux/hcs/device\_info/device\_info.hcs**. +You can configure the device driver description in the configuration file at **./drivers/adapter/khdf/linux/hcs/device\_info/device\_info.hcs**. -The **device\_info.hcs** file contains all necessary information for registering drivers in the input driver model with the HDF. You do not need to make any modification for the information unless otherwise required in special scenarios. The private configuration data of each driver uses the **deviceMatchAttr** field to match the **match\_attr** field in the **input\_config.hcs** file. +The **device\_info.hcs** file contains all necessary information for registering drivers in the input driver model with the HDF. You do not need to make any modification for the information unless otherwise required in special scenarios. The private configuration data of each driver uses the **deviceMatchAttr** field to match the **match\_attr** field in the **input\_config.hcs** file. The input-related fields in the configuration file are as follows. For details about these fields, see [Driver Development](../driver/driver-hdf-development.md). @@ -91,21 +91,21 @@ Pay attention to the following fields in the configuration file: **preload**: specifies whether to load the driver. -**moduleName**: This value must be the same as the **moduleName** value in the driver entry structure. +**moduleName**: This value must be the same as the **moduleName** value in the driver entry structure. **serviceName**: This value is used by the HDF to create a device node name. -**deviceMatchAttr**: This value must be the same as the **match\_attr** value in the private configuration data. +**deviceMatchAttr**: This value must be the same as the **match\_attr** value in the private configuration data. -After the device descriptions are configured, the HDF matches the configuration with the code registered with the driver entry structure based on the **moduleName** field, ensuring that drivers can be loaded properly. If multiple drivers are configured, the **priority** field determines the loading sequence of each driver. +After the device descriptions are configured, the HDF matches the configuration with the code registered with the driver entry structure based on the **moduleName** field, ensuring that drivers can be loaded properly. If multiple drivers are configured, the **priority** field determines the loading sequence of each driver. ### Configuring the Touchscreen The private data includes the power-on and power-off sequence, and the platform hardware information includes the GPIO port that connects the touchscreen to the main board. -You can configure the touchscreen in the configuration file at **./drivers/adapter/khdf/linux/hcs/input/input\_config.hcs**. +You can configure the touchscreen in the configuration file at **./drivers/adapter/khdf/linux/hcs/input/input\_config.hcs**. -The **input\_config.hcs** file consists of the private configuration data of both the common driver and chip driver. Information of this file is read and parsed by the driver code. The configuration in the file includes the board-level hardware information and private configuration of the touchscreen. You can tailor the configuration during your development. +The **input\_config.hcs** file consists of the private configuration data of both the common driver and chip driver. Information of this file is read and parsed by the driver code. The configuration in the file includes the board-level hardware information and private configuration of the touchscreen. You can tailor the configuration during your development. ``` root { @@ -197,7 +197,7 @@ root { } ``` -In the example, **touchConfig** contains the **touch0** configuration, which describes the **boardConfig** and **chipConfig** configuration information. The **boardConfig** field provides the board-level hardware information of Hi3516D V300, and the **chipConfig** field provides the private configuration data of the touchscreen. To use another touchscreen, you can change the value of the **chipConfig** field. You can also configure multiple touchscreens for your product. In this example, **touch0** represents the hardware interface and chip configuration of the default touchscreen. If you need to configure a secondary touchscreen, add a **touch1** block parallel to **touch0**. +In the example, **touchConfig** contains the **touch0** configuration, which describes the **boardConfig** and **chipConfig** configuration information. The **boardConfig** field provides the board-level hardware information of Hi3516D V300, and the **chipConfig** field provides the private configuration data of the touchscreen. To use another touchscreen, you can change the value of the **chipConfig** field. You can also configure multiple touchscreens for your product. In this example, **touch0** represents the hardware interface and chip configuration of the default touchscreen. If you need to configure a secondary touchscreen, add a **touch1** block parallel to **touch0**. ### Adapting to the Private Drivers of the Touchscreen @@ -207,7 +207,7 @@ The input driver model consists of three parts of drivers. To develop a brand-ne 1. Implement differentiated APIs for the touchscreen to adapt to the input chip driver. - You can obtain the sample code at **./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c**. + You can obtain the sample code at **./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c**. ``` static struct TouchChipOps g_gt911ChipOps = { // IC options of the touchscreen @@ -267,7 +267,7 @@ The input driver model consists of three parts of drivers. To develop a brand-ne 2. Initialize the input chip driver and register the driver with the HDF. - You can obtain the sample code at **./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c**. + You can obtain the sample code at **./drivers/framework/model/input/driver/touchscreen/touch\_gt911.c**. ``` static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device) @@ -304,7 +304,7 @@ The input driver model consists of three parts of drivers. To develop a brand-ne 1. Compile the Makefile. - Open the file at **./drivers/adapter/khdf/linux/model/input/Makefile**. + Open the file at **./drivers/adapter/khdf/linux/model/input/Makefile**. Add the following content: @@ -313,7 +313,7 @@ The input driver model consists of three parts of drivers. To develop a brand-ne $(INPUT_ROOT_DIR)/touchscreen/touch_gt911.o ``` - **touch\_gt911.o** is the content added in this example. + **touch\_gt911.o** is the content added in this example. 2. Build source code and burn images. For details, see the related sections in [Quick Start Overview](../quick-start/quickstart-overview.md). diff --git a/en/device-dev/guide/device-wlan-led-control.md b/en/device-dev/guide/device-wlan-led-control.md index 03613ab7217c9078cd600245ebf008da83926b15..22535b4011ca1b70b84c69ccacb03bfed082f95b 100644 --- a/en/device-dev/guide/device-wlan-led-control.md +++ b/en/device-dev/guide/device-wlan-led-control.md @@ -16,7 +16,7 @@ Based on the Hi3861 platform, the OpenHarmony WLAN module provides abundant peri #define LED_TEST_GPIO 9 ``` - >![](../public_sys-resources/icon-note.gif) **NOTE:** + >![](../public_sys-resources/icon-note.gif) **NOTE** > >For details about the schematic diagram of the development board, contact the Hi3861 customer service personnel. diff --git a/en/device-dev/kernel/Readme-EN.md b/en/device-dev/kernel/Readme-EN.md index 0dcbb0d34e34cff4ae76fc54d0abead443f3be54..243ae9fb87332aa7ba04b381fecc814f610ffb93 100644 --- a/en/device-dev/kernel/Readme-EN.md +++ b/en/device-dev/kernel/Readme-EN.md @@ -1,4 +1,4 @@ -# Kernel +# Kernel - Kernel - [Kernel Overview](kernel-overview.md) @@ -13,7 +13,7 @@ - [Mutex](kernel-mini-basic-ipc-mutex.md) - [Queue](kernel-mini-basic-ipc-queue.md) - [Semaphore](kernel-mini-basic-ipc-sem.md) - - [Time Management](kernel-basic-mini-time.md) + - [Time Management](kernel-mini-basic-time.md) - [Software Timer](kernel-mini-basic-soft.md) - [Doubly Linked List](kernel-mini-basic-list.md) - Extended Components @@ -65,6 +65,7 @@ - [Virtual File System](kernel-small-bundles-fs-virtual.md) - [Supported File Systems](kernel-small-bundles-fs-support.md) - [File System Adaptation](kernel-small-bundles-fs-new.md) + - [Plimitsfs](kernel-small-plimits.md) - Debugging and Tools - Shell - [Introduction to the Shell](kernel-small-debug-shell-overview.md) diff --git a/en/device-dev/kernel/figures/create_delete_plimits.png b/en/device-dev/kernel/figures/create_delete_plimits.png new file mode 100644 index 0000000000000000000000000000000000000000..6c338727abdc94c6b388acd38e5c867706d528b3 Binary files /dev/null and b/en/device-dev/kernel/figures/create_delete_plimits.png differ diff --git a/en/device-dev/kernel/figures/memory_limiter.png b/en/device-dev/kernel/figures/memory_limiter.png new file mode 100644 index 0000000000000000000000000000000000000000..032eab63bf16c1cf97deec94adfae287698d8220 Binary files /dev/null and b/en/device-dev/kernel/figures/memory_limiter.png differ diff --git a/en/device-dev/kernel/figures/newip-connections.png b/en/device-dev/kernel/figures/newip-connections.png index c4edde138f8cd92cfe8828a4467c49d0b3e5bf65..85aa191ccb599cfdc0a01c2750ccdc09cc345b0b 100644 Binary files a/en/device-dev/kernel/figures/newip-connections.png and b/en/device-dev/kernel/figures/newip-connections.png differ diff --git a/en/device-dev/kernel/figures/newip-development.png b/en/device-dev/kernel/figures/newip-development.png index e5e2b90263058fc847a711b3acffe627e94081f4..fd84b4e7a853822ec61d3dc8a25d18f8417de44e 100644 Binary files a/en/device-dev/kernel/figures/newip-development.png and b/en/device-dev/kernel/figures/newip-development.png differ diff --git a/en/device-dev/kernel/figures/newip-header.png b/en/device-dev/kernel/figures/newip-header.png index 9190bb298de56b73f98ffd142af014ded382c07b..828c8b843715fa85f918a65abe042cebc7922140 100644 Binary files a/en/device-dev/kernel/figures/newip-header.png and b/en/device-dev/kernel/figures/newip-header.png differ diff --git a/en/device-dev/kernel/figures/pids_limiter.png b/en/device-dev/kernel/figures/pids_limiter.png new file mode 100644 index 0000000000000000000000000000000000000000..10b60630537d586a13bba806a41712bb37b7c77d Binary files /dev/null and b/en/device-dev/kernel/figures/pids_limiter.png differ diff --git a/en/device-dev/kernel/figures/sched_limiter.png b/en/device-dev/kernel/figures/sched_limiter.png new file mode 100644 index 0000000000000000000000000000000000000000..26404168a4bb635cd1a7cfc882ea309b8677a82f Binary files /dev/null and b/en/device-dev/kernel/figures/sched_limiter.png differ diff --git a/en/device-dev/kernel/kernel-mini-basic-interrupt.md b/en/device-dev/kernel/kernel-mini-basic-interrupt.md index eed0a1b6cb29bebab54f406e8e5d97fc799dfeee..80ea5eebefdccd884afcfdded2b6e62c115cbdd5 100644 --- a/en/device-dev/kernel/kernel-mini-basic-interrupt.md +++ b/en/device-dev/kernel/kernel-mini-basic-interrupt.md @@ -1,105 +1,128 @@ # Interrupt Management + ## Basic Concepts An interrupt is a signal to the processor emitted by hardware or software indicating an event that needs immediate attention. An interrupt alerts the processor to a high-priority condition requiring the interruption of the current code being executed by the processor. When a hardware interrupt is triggered, the interrupt handler is located based on the interrupt ID and then executed to handle the interrupt. -By using the interrupt mechanism, the CPU responds to the interrupt request from a peripheral only when required, and execute other tasks when the peripherals do not require the CPU. In this way, the CPU does not need to spend a lot of time in waiting and querying the peripheral status, which effectively improves the real-time performance and execution efficiency of the system. +By using the interrupt mechanism, the CPU responds to the interrupt request (IRQ) from a peripheral only when required, and execute other tasks when the peripherals do not require the CPU. In this way, the CPU does not need to spend a lot of time in waiting and querying the peripheral status, which effectively improves the real-time performance and execution efficiency of the system. + +To understand interrupts, you need to know the following concepts: + +- Interrupt ID + + Identifies an IRQ signal. The computer locates the device that sends the IRQ based on the interrupt ID. +- IRQ + + An electrical pulse signal sent to the CPU, alerting the CPU to a high-priority event requiring the interruption of the current code being executed by the CPU. +- Interrupt priority + + Prioritizes the sources that trigger interrupts based on the importance and urgency of interrupt events, so that the CPU can respond to and handle all interrupts in a timely manner. +- Interrupt handler + + A program executed by the CPU to respond to the IRQ from a peripheral. Each device that triggers an interrupt has its own interrupt handler. +- Interrupt triggering + + The interrupt source sends an interrupt signal to the interrupt controller. The interrupt controller arbitrates all pending interrupts, determines the priority, and sends the interrupt signal to the CPU. When an interrupt source generates an interrupt signal, the interrupt trigger is set to **1**, alerting the CPU to respond to the interrupt. +- Interrupt vector + + Entry address of an interrupt handler. +- Interrupt vector table + + An area for storing interrupt vectors. It stores the mapping between interrupt vectors and interrupt IDs. -The following describes the concepts related to interrupts: -- Interrupt ID +## Available APIs - Identifies an interrupt request signal. The computer locates the device that sends the interrupt request based on the interrupt ID. +The following table describes APIs available for the OpenHarmony LiteOS-M interrupt module. For more details about the APIs, see the API reference. -- Interrupt request + **Table 1** APIs for creating and deleting an interrupt - A process in which an electrical pulse signal is sent to the CPU, alerting the CPU to a high-priority event requiring the interruption of the current code being executed by the CPU. +| API| Description| +| -------- | -------- | +| LOS_HwiCreate | Creates an interrupt and registers the interrupt ID, triggering mode, priority, and interrupt handler. When an interrupt is triggered, the interrupt handler will be called.| +| LOS_HwiDelete | Deletes an interrupt based on the specified interrupt ID.| -- Interrupt priority + **Table 2** APIs for enabling and disabling interrupts - Prioritizes the sources that trigger interrupts based on the importance and urgency of interrupt events, so that the CPU can respond to and handle all interrupts in a timely manner. +| API| Description| +| -------- | -------- | +| LOS_IntUnLock | Enables the CPU to respond to all IRQs.| +| LOS_IntLock | Disables the CPU from responding to IRQs.| +| LOS_IntRestore | Restores the interrupt status before the **LOS_IntLock** and **LOS_IntUnLock** operations are performed.| -- Interrupt handler + **Table 3** APIs for other interrupt operations - A program executed by the CPU to respond to the interrupt request from a peripheral. Each device that triggers an interrupt has its own interrupt handler. +| API | Description | +| :----------------- | ---------------- | +| LOS_HwiTrigger | Triggers an interrupt. | +| LOS_HwiEnable | Enables interrupts. | +| LOS_HwiDisable | Disables interrupts. | +| LOS_HwiClear | Clears an interrupt manually. | +| LOS_HwiSetPriority | Sets the interrupt priority.| +| LOS_HwiCurIrqNum | Obtains the current interrupt ID.| -- Interrupt triggering - The interrupt source sends an interrupt signal to the interrupt controller. The interrupt controller arbitrates all pending interrupts, determines the priority, and sends the interrupt signal to the CPU. When an interrupt source generates an interrupt signal, the interrupt trigger is set to **1**, alerting the CPU to respond to the interrupt. +## How to Develop -- Interrupt vector +1. Call **LOS_HwiCreate** to create an interrupt. - Entry address of an interrupt handler. +2. Call **LOS_HwiTrigger()** to trigger an interrupt (write the related register of the interrupt controller to simulate an external interrupt) or trigger an interrupt by using a peripheral. -- Interrupt vector table +3. Call **LOS_HwiDelete()** to delete an interrupt. Use this API based on actual requirements. - An area for storing interrupt vectors. It stores the mapping between interrupt vectors and interrupt IDs. +> **NOTE** +> - Set the maximum number of interrupts supported and the number of configurable interrupt priorities based on the hardware. +> - Avoid long interrupt disabling time or interrupt handler processing time. Otherwise, the CPU cannot respond to interrupts in a timely manner. +> - Do not directly or indirectly call the API that causes scheduling, such as **LOS_Schedule**, during the interrupt response process. +> - The input parameter of **LOS_IntRestore()** must be the return value of **LOS_IntLock()**, that is, the current program status register (CPSR) value before the interrupt is disabled. +> - Interrupts 0 to 15 are for internal use of the Cortex-M series processors. You are advised not to apply for or create interrupts 0 to 15. -## Available APIs -The following table describes APIs available for the OpenHarmony LiteOS-M interrupt module. For more details about the APIs, see the API reference. +## Development Example -**Table 1** APIs of the interrupt module - -| Category| API| Description| -| -------- | -------- | -------- | -| Creating or deleting an interrupt| LOS_HwiCreate | Creates an interrupt and registers the interrupt ID, triggering mode, priority, and interrupt handler. When an interrupt is triggered, the interrupt handler will be called.| -| | LOS_HwiDelete | Deletes an interrupt based on the specified interrupt ID.| -| Locking or unlocking interrupts| LOS_IntUnLock | Enables the CPU to respond to all interrupt requests.| -| | LOS_IntLock | Disables the CPU from responding to interrupt requests.| -| | LOS_IntRestore | Restores the interrupt status before the **LOS_IntLock** and **LOS_IntUnLock** operations are performed.| -| Enabling or disabling an interrupt| LOS_HwiDisable | Disables the CPU from responding to the specified interrupt by setting the register.| -| | LOS_HwiEnable | Enables the CPU to respond to the specified interrupt by setting the register.| -| Setting the interrupt priority| LOS_HwiSetPriority | Sets the interrupt priority.| -| Triggering an interrupt| LOS_HwiTrigger | Triggers an interrupt (simulate an external interrupt by writing the related register of the interrupt controller).| -| Clearing interrupt register status| LOS_HwiClear | Clears the status bit of the interrupt register corresponding to the interrupt ID. The implementation of this API depends on the interrupt controller version. It is optional.| +This example implements the following: -## How to Develop +1. Create an interrupt. -1. Call **LOS_HwiCreate** to create an interrupt. -2. Call **LOS_HwiTrigger** to trigger the interrupt. -3. Call **LOS_HwiDelete** to delete the specified interrupt. Use this API based on actual requirements. +2. Trigger an interrupt. ->![](../public_sys-resources/icon-note.gif) **NOTE**
->- Configure the maximum number of interrupts supported and the number of configurable interrupt priorities based on the specific hardware. ->- If the interrupt handler takes long time, the CPU cannot respond to interrupt requests in a timely manner. ->- Functions that trigger **LOS\_Schedule** cannot be directly or indirectly executed during interrupt response process. ->- The input parameter of **LOS\_IntRestore\(\)** must be the return value of **LOS\_IntLock\(\)**, that is, the current program status register \(CPSR\) value before the interrupt is disabled. Interrupts 0 to 15 in the Cortex-M series processors are for internal use. You are advised not to apply for or create interrupts 0 to 15. +3. Delete an interrupt. -## Development Example +The following sample code demonstrates how to create an interrupt, trigger the interrupt to invoke the interrupt handler, and delete the interrupt. -This example implements the following: - -1. Create an interrupt. -2. Trigger an interrupt. -3. Delete an interrupt. +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleInterrupt()** in **TestTaskEntry**. -The following sample code shows how to create and delete an interrupt. When the interrupt **HWI\_NUM\_TEST** is generated, the interrupt handler function will be called. ``` #include "los_interrupt.h" +#include "los_compiler.h" -/* Create an interrupt. */ +/* Interrupt ID to verify */ #define HWI_NUM_TEST 7 -STATIC VOID HwiUsrIrq(VOID) +/* Interrupt handler */ +STATIC VOID UsrIrqEntry(VOID) +{ + printf("in the func UsrIrqEntry\n"); +} + +/* Register a thread callback to trigger the interrupt */ +STATIC VOID InterruptTest(VOID) { - printf("in the func HwiUsrIrq \n"); + LOS_HwiTrigger(HWI_NUM_TEST); } -static UINT32 Example_Interrupt(VOID) +UINT32 ExampleInterrupt(VOID) { UINT32 ret; - HWI_PRIOR_T hwiPrio = 3; + HWI_PRIOR_T hwiPrio = 3; // Interrupt priority HWI_MODE_T mode = 0; - HwiIrqParam irqParam; - (void)memset_s(&irqParam, sizeof(HwiIrqParam), 0, sizeof(HwiIrqParam)); - irqParam.pDevId = 0; + HWI_ARG_T arg = 0; /* Create an interrupt. */ - ret = LOS_HwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)HwiUsrIrq, &irqParam); + ret = LOS_HwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)UsrIrqEntry, arg); if(ret == LOS_OK){ printf("Hwi create success!\n"); } else { @@ -107,32 +130,44 @@ static UINT32 Example_Interrupt(VOID) return LOS_NOK; } - /* Trigger the interrupt. */ - ret = LOS_HwiTrigger(HWI_NUM_TEST); - if(ret == LOS_OK){ - printf("Hwi trigger success!\n"); - } else { - printf("Hwi trigger failed!\n"); - return LOS_NOK; + TSK_INIT_PARAM_S taskParam = { 0 }; + UINT32 testTaskID; + + /* Create a thread with a low priority to verify interrupt triggering.*/ + taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)InterruptTest; + taskParam.uwStackSize = OS_TSK_TEST_STACK_SIZE; + taskParam.pcName = "InterruptTest"; + taskParam.usTaskPrio = TASK_PRIO_TEST - 1; + taskParam.uwResved = LOS_TASK_ATTR_JOINABLE; + ret = LOS_TaskCreate(&testTaskID, &taskParam); + if (LOS_OK != ret) { + PRINTF("InterruptTest task error\n"); } - /* Delete the interrupt. */ - ret = LOS_HwiDelete(HWI_NUM_TEST); + /* Delay 50 ticks to release the scheduling of the current thread. */ + LOS_TaskDelay(50); + + /* Delete the registered interrupt. */ + ret = LOS_HwiDelete(HWI_NUM_TEST, NULL); if(ret == LOS_OK){ printf("Hwi delete success!\n"); } else { printf("Hwi delete failed!\n"); return LOS_NOK; } + return LOS_OK; } ``` + ## Verification The development is successful if the return result is as follows: ``` Hwi create success! +in the func UsrIrqEntry Hwi delete success! ``` + diff --git a/en/device-dev/kernel/kernel-mini-basic-ipc-event.md b/en/device-dev/kernel/kernel-mini-basic-ipc-event.md index aa0b7289138b1d1ba1440725960757ba9c243403..a39a68606f1a780ea75b05fe788f4d577ac554a5 100644 --- a/en/device-dev/kernel/kernel-mini-basic-ipc-event.md +++ b/en/device-dev/kernel/kernel-mini-basic-ipc-event.md @@ -1,30 +1,35 @@ -# Event +# Events + ## Basic Concepts An event is a mechanism for communication between tasks. It can be used to synchronize tasks. The events have the following features: -- Events can be synchronized in one-to-many or many-to-many mode. In one-to-many mode, a task can wait for multiple events. In many-to-many mode, multiple tasks can wait for multiple events. However, a write event wakes up only one task from the block. -- Event read timeout mechanism is used. -- Events are used only for task synchronization, but not for data transmission. +- Events can be synchronized in one-to-many or many-to-many mode. In one-to-many mode, a task can wait for multiple events. In many-to-many mode, multiple tasks can wait for multiple events. However, a write event wakes up only one task from the block. + +- Event read timeout mechanism is used. + +- Events are used only for task synchronization, but not for data transmission. APIs are provided to initialize, read/write, clear, and destroy events. + ## Working Principles ### Event Control Block +The event control block is a struct configured in the event initialization function. It is passed in as an input parameter to identify the event for operations such as event read and write. The data structure of the event control block is as follows: + + ``` -/** -* Event control block data structure - */ typedef struct tagEvent { UINT32 uwEventID; /* Event set, which is a collection of events processed (written and cleared). */ - LOS_DL_LIST stEventList; /* List of tasks waiting for specific events*/ + LOS_DL_LIST stEventList; /* List of tasks waiting for specific events. */ } EVENT_CB_S, *PEVENT_CB_S; ``` -### Working Principles + +### Working Principles **Initializing an event**: An event control block is created to maintain a collection of processed events and a linked list of tasks waiting for specific events. @@ -32,127 +37,98 @@ typedef struct tagEvent { **Reading an event**: If the read event already exists, it is returned synchronously. In other cases, the return time is determined based on the timeout period and event triggering status. If the wait event condition is met before the timeout period expires, the blocked task will be directly woken up. Otherwise, the blocked task will be woken up only after the timeout period has expired. -The input parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** indicates the mask of the event. **mode** indicates the handling mode, which can be any of the following: +The input parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** indicates the mask of the event. **mode** indicates the handling mode, which can be any of the following: + +- **LOS_WAITMODE_AND**: Event reading is successful only when all the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned. + +- **LOS_WAITMODE_OR**: Event reading is successful when any of the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned. -- **LOS\_WAITMODE\_AND**: Event reading is successful only when all the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned. -- **LOS\_WAITMODE\_OR**: Event reading is successful when any of the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned. -- **LOS\_WAITMODE\_CLR**: This mode must be used with **LOS\_WAITMODE\_AND** or **LOS\_WAITMODE\_OR** \(LOS\_WAITMODE\_AND | LOS\_WAITMODE\_CLR or LOS\_WAITMODE\_OR | LOS\_WAITMODE\_CLR\). In this mode, if **LOS\_WAITMODE\_AND** or **LOS\_WAITMODE\_OR** is successful, the corresponding event type bit in the event control block will be automatically cleared. +- **LOS_WAITMODE_CLR**: This mode must be used with one or all of the event modes (LOS_WAITMODE_AND | LOS_WAITMODE_CLR or LOS_WAITMODE_OR | LOS_WAITMODE_CLR). In this mode, if all event modes or any event mode is successful, the corresponding event type bit in the event control block will be automatically cleared. -**Clearing event**: Clear the event set of the event control block based on the specified mask. If the mask is **0**, the event set will be cleared. If the mask is **0xffff**, no event will be cleared, and the event set remains unchanged. +**Clearing events**: Clear the event set of the event control block based on the specified mask. If the mask is **0**, the event set will be cleared. If the mask is **0xffff**, no event will be cleared, and the event set remains unchanged. **Destroying an event**: Destroy the specified event control block. -**Figure 1** Event working mechanism for mini systems +**Figure 1** Event working mechanism for a mini system + ![](figures/event-working-mechanism-for-mini-systems.png "event-working-mechanism-for-mini-systems") + ## Available APIs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Checking events

-

LOS_EventPoll

-

Checks whether the expected event occurs based on eventID, eventMask, and mode.

-
NOTICE:

If mode contains LOS_WAITMODE_CLR and the expected event occurs, the event that meets the requirements in eventID will be cleared. In this case, eventID is an input parameter and an output parameter. In other cases, eventID is used only as an input parameter.

-
-

Initializing events

-

LOS_EventInit

-

Initializes an event control block.

-

Reading events

-

LOS_EventRead

-

Reads an event (wait event). The task is blocked to wait based on the timeout period (in ticks).

-

If no event is read, 0 is returned.

-

If an event is successfully read, a positive value (event set) is returned.

-

In other cases, a specific error code is returned.

-

Writing events

-

LOS_EventWrite

-

Writes a specific event to the event control block.

-

Clearing events

-

LOS_EventClear

-

Clears an event in the event control block based on the event mask.

-

Destroying events

-

LOS_EventDestroy

-

Destroys an event control block.

-
+| Category| API| Description| +| -------- | -------- | -------- | +| Event check| LOS_EventPoll | Checks whether the expected event occurs based on **eventID**, **eventMask**, and **mode**.
**NOTICE**

If **mode** contains **LOS_WAITMODE_CLR** and the expected event occurs, the event that meets the requirements in **eventID** will be cleared. In this case, **eventID** is an input parameter and an output parameter. In other cases, **eventID** is used only as an input parameter.| +| Initialization| LOS_EventInit | Initializes an event control block.| +| Event read| LOS_EventRead | Reads an event (wait event). The task will be blocked to wait based on the timeout period (in ticks).
If no event is read, **0** is returned.
If an event is successfully read, a positive value (event set) is returned.
In other cases, an error code is returned.| +| Event write| LOS_EventWrite | Writes an event to the event control block.| +| Event clearance| LOS_EventClear | Clears an event in the event control block based on the event mask.| +| Event destruction| LOS_EventDestroy | Destroys an event control block.| + ## How to Develop The typical event development process is as follows: -1. Initialize an event control block. -2. Block a read event control block. -3. Write related events. -4. Wake up a blocked task, read the event, and check whether the event meets conditions. -5. Handle the event control block. -6. Destroy an event control block. +1. Initialize an event control block. + +2. Block a read event control block. + +3. Write related events. + +4. Wake up a blocked task, read the event, and check whether the event meets conditions. + +5. Handle the event control block. + +6. Destroy an event control block. + + +> **NOTE** +> - When an event is read or written, the 25th bit of the event is reserved and cannot be set. +> +> - Repeated writes of the same event are treated as one write. ->![](../public_sys-resources/icon-note.gif) **NOTE:** ->- When an event is read or written, the 25th bit of the event is reserved and cannot be set. ->- Repeated writes of the same event are treated as one write. ## Development Example + ### Example Description -In this example, run the **Example\_TaskEntry** task to create the **Example\_Event** task. Run the **Example\_Event** task to read an event to trigger task switching. Run the **Example\_TaskEntry** task to write an event. You can understand the task switching during event operations based on the sequence in which logs are recorded. +In the **ExampleEvent** task, create an **EventReadTask** task with a timout period of 100 ticks. When the **EventReadTask** task is blocked, **ExampleEvent** task writes an event. You can understand the task switching during event operations based on the sequence in which logs are recorded. + +1. In the **ExampleEvent** task, create an **EventReadTask** task with a timeout period of 100 ticks. The **EventReadTask** task has a higher priority than the **ExampleEvent** task. + +2. **EventReadTask** is scheduled to read event **0x00000001**, but suspended to wait 100 ticks. The **ExampleEvent** task is scheduled to write event **0x00000001**. -1. Create the **Example\_Event** task in the **Example\_TaskEntry** task with a higher priority than the **Example\_TaskEntry** task. -2. Run the **Example\_Event** task to read event **0x00000001**. Task switching is triggered to execute the **Example\_TaskEntry** task. -3. Run the **Example\_TaskEntry** task to write event **0x00000001**. Task switching is triggered to execute the **Example\_Event** task. -4. The **Example\_Event** task is executed. -5. The **Example\_TaskEntry** task is executed. +3. When **ExampleEvent** is scheduled to write event **0x00000001**, the wait time of **EventReadTask** expires and **EventReadTask** task is scheduled to run. -### Sample Code +4. The **EventReadTask** task is executed. + +5. The **ExampleEvent** task is executed. + + +### Sample Code The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleEvent()** in **TestTaskEntry**. + + ``` #include "los_event.h" #include "los_task.h" -#include "securec.h" - -/* Task ID*/ -UINT32 g_testTaskId; -/* Event control structure*/ +/* Event control struct */ EVENT_CB_S g_exampleEvent; -/* Type of the wait event*/ +/* Type of the wait event */ #define EVENT_WAIT 0x00000001 -/* Example task entry function*/ -VOID Example_Event(VOID) +/* Wait timeout time */ +#define EVENT_TIMEOUT 100 + +/* Example task entry function */ +VOID EventReadTask(VOID) { UINT32 ret; UINT32 event; @@ -160,39 +136,39 @@ VOID Example_Event(VOID) /* Set a timeout period for event reading to 100 ticks. If the specified event is not read within 100 ticks, the read operation times out and the task is woken up. */ printf("Example_Event wait event 0x%x \n", EVENT_WAIT); - event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, 100); + event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, EVENT_TIMEOUT); if (event == EVENT_WAIT) { - printf("Example_Event,read event :0x%x\n", event); + printf("Example_Event, read event :0x%x\n", event); } else { - printf("Example_Event,read event timeout\n"); + printf("Example_Event, read event timeout\n"); } } -UINT32 Example_TaskEntry(VOID) +UINT32 ExampleEvent(VOID) { UINT32 ret; - TSK_INIT_PARAM_S task1; + UINT32 taskId; + TSK_INIT_PARAM_S taskParam = { 0 }; - /* Initialize the event. */ + /* Initialize the event control block. */ ret = LOS_EventInit(&g_exampleEvent); if (ret != LOS_OK) { printf("init event failed .\n"); - return -1; + return LOS_NOK; } /* Create a task. */ - (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); - task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event; - task1.pcName = "EventTsk1"; - task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE; - task1.usTaskPrio = 5; - ret = LOS_TaskCreate(&g_testTaskId, &task1); + taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)EventReadTask; + taskParam.pcName = "EventReadTask"; + taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam.usTaskPrio = 3; + ret = LOS_TaskCreate(&taskId, &taskParam); if (ret != LOS_OK) { printf("task create failed.\n"); return LOS_NOK; } - /* Write the task wait event (g_testTaskId). */ + /* Write an event. */ printf("Example_TaskEntry write event.\n"); ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT); @@ -206,10 +182,10 @@ UINT32 Example_TaskEntry(VOID) LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID); printf("EventMask:%d\n", g_exampleEvent.uwEventID); - /* Delete the task. */ - ret = LOS_TaskDelete(g_testTaskId); + /* Delete the event. */ + ret = LOS_EventDestroy(&g_exampleEvent); if (ret != LOS_OK) { - printf("task delete failed.\n"); + printf("destory event failed .\n"); return LOS_NOK; } @@ -217,15 +193,17 @@ UINT32 Example_TaskEntry(VOID) } ``` + ### Verification The development is successful if the return result is as follows: + + ``` -Example_Event wait event 0x1 +Example_Event wait event 0x1 Example_TaskEntry write event. -Example_Event,read event :0x1 +Example_Event, read event :0x1 EventMask:1 EventMask:0 ``` - diff --git a/en/device-dev/kernel/kernel-mini-basic-ipc-mutex.md b/en/device-dev/kernel/kernel-mini-basic-ipc-mutex.md index da657fe36e0bb3f1ea8f20e336f643b85c5609d7..af163e60de4c5d0b68b1de0bc749c0c1622473f0 100644 --- a/en/device-dev/kernel/kernel-mini-basic-ipc-mutex.md +++ b/en/device-dev/kernel/kernel-mini-basic-ipc-mutex.md @@ -1,152 +1,134 @@ # Mutex + ## Basic Concepts -A mutual exclusion \(mutex\) is a special binary semaphore used for exclusive access to shared resources. +A mutual exclusion (mutex) is a special binary semaphore used for exclusive access to shared resources. -A mutex can be unlocked or locked. When a mutex is held by a task, the mutex is locked and the task obtains the ownership of the mutex. When the task releases the mutex, the mutex is unlocked and the task will lose the ownership of the mutex. When a task holds a mutex, other tasks cannot unlock or hold the mutex. +A mutex can be unlocked or locked. When a task holds a mutex, the mutex is locked and the task obtains the ownership of the mutex. When the task releases the mutex, the mutex is unlocked and the task loses the ownership of the mutex. When a task holds a mutex, other tasks cannot unlock or hold the mutex. In an environment where multiple tasks compete for shared resources, the mutex can protect the shared resources via exclusive access. In addition, the mutex can prevent semaphore priority inversion, which occurs when a low-priority task holds a semaphore but a high-priority task has to wait until the low-priority task releases it. -## Working Principles + +## Working Principles In a multi-task environment, multiple tasks may access the same shared resources. However, certain shared resources are not shared, and can only be accessed exclusively by tasks. A mutex can be used to address this issue. When non-shared resources are accessed by a task, the mutex is locked. Other tasks will be blocked until the mutex is released by the task. The mutex allows only one task to access the shared resources at a time, ensuring integrity of operations on the shared resources. -**Figure 1** Mutex working mechanism for mini systems +**Figure 1** Mutex working mechanism for a mini system ![](figures/mutex-working-mechanism-for-mini-systems.png "mutex-working-mechanism-for-mini-systems") + ## Available APIs -**Table 1** APIs of the mutex module - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Creating or deleting a mutex

-

LOS_MuxCreate

-

Creates a mutex.

-

LOS_MuxDelete

-

Deletes the specified mutex.

-

Requesting or releasing a mutex

-

LOS_MuxPend

-

Requests the specified mutex.

-

LOS_MuxPost

-

Releases the specified mutex.

-
+ **Table 1** APIs of the mutex module + +| Category| Description| +| -------- | -------- | +| Creating or deleting a mutex| **LOS_MuxCreate**: creates a mutex.
**LOS_MuxDelete**: eeletes a mutex.| +| Requesting or releasing a mutex| **LOS_MuxPend**: requests a mutex.
**LOS_MuxPost**: releases a mutex.| + ## How to Develop The typical mutex development process is as follows: -1. Call **LOS\_MuxCreate** to create a mutex. -2. Call **LOS\_MuxPend** to request a mutex. +1. Call **LOS_MuxCreate** to create a mutex. - The following modes are available: +2. Call **LOS_MuxPend** to request a mutex. + The following modes are available: - - Non-block mode: A task acquires the mutex if the requested mutex is not held by any task or the task holding the mutex is the same as the task requesting the mutex. - - Permanent block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be unlocked and executed only when the mutex is released. - - Scheduled block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be executed only when the mutex is released within the specified timeout period or when the specified timeout period expires. + - Non-block mode: A task acquires the mutex if the requested mutex is not held by any task or the task holding the mutex is the same as the task requesting the mutex. + - Permanent block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with a highest priority in the ready queue will be executed. The blocked task can be unlocked and executed only when a mutex is acquired. + - Scheduled block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be executed only when the mutex is released within the specified timeout period or when the specified timeout period expires. -3. Call **LOS\_MuxPost** to release a mutex. - - If tasks are blocked by the specified mutex, the task with a higher priority will be unblocked when the mutex is released. The unblocked task changes to the Ready state and is scheduled. - - If no task is blocked by the specified mutex, the mutex is released successfully. +3. Call **LOS_MuxPost** to release a mutex. + - If tasks are blocked by the specified mutex, the task with a higher priority will be unblocked when the mutex is released. The unblocked task changes to the Ready state and is scheduled. + - If no task is blocked by the specified mutex, the mutex is released successfully. -4. Call **LOS\_MuxDelete** to delete a mutex. +4. Call **LOS_MuxDelete** to delete a mutex. + +> **NOTE** +> - Nested mutexes are supported. That is, if a task that attempts to apply for a mutex and the task that already holds the mutex are the same task, the application is considered successful, and the lock is released based on the number of application times. +> +> - Mutexes cannot be used in an interrupt handler. +> +> - The LiteOS-M kernel must ensure real-time task scheduling and avoid long-time task blocking. Therefore, a mutex must be released as soon as possible after use. +> +> - When a mutex is held by a task, the task priority cannot be changed by using APIs such as **LOS_TaskPriSet**. ->![](../public_sys-resources/icon-note.gif) **NOTE**
->- Two tasks cannot lock the same mutex. If a task attempts to lock a mutex held by another task, the task will be blocked until the mutex is unclocked. ->- Mutexes cannot be used in the interrupt service program. ->- When using the LiteOS-M kernel, OpenHarmony must ensure real-time task scheduling and avoid long-time task blocking. Therefore, a mutex must be released as soon as possible after use. ->- When a mutex is held by a task, the task priority cannot be changed by using APIs such as **LOS\_TaskPriSet**. ## Development Example + ### Example Description This example implements the following: -1. Create the **Example\_TaskEntry** task. In this task, create a mutex to lock task scheduling, and create two tasks **Example\_MutexTask1** \(with a lower priority\) and **Example\_MutexTask2** \(with a higher priority\) to unlock task scheduling. -2. When being scheduled, **Example\_MutexTask2** requests a mutex in permanent block mode. After acquiring the mutex, **Example\_MutexTask2** enters the sleep mode for 100 ticks. **Example\_MutexTask2** is suspended, and **Example\_MutexTask1** is woken up. -3. **Example\_MutexTask1** requests a mutex in scheduled block mode, and waits for 10 ticks. Because the mutex is still held by **Example\_MutexTask2**, **Example\_MutexTask1** is suspended. After 10 ticks, **Example\_MutexTask1** is woken up and attempts to request a mutex in permanent block mode. **Example\_MutexTask1** is suspended because the mutex is still held by **Example\_MutexTask2**. -4. After 100 ticks, **Example\_MutexTask2** is woken up and releases the mutex, and then **Example\_MutexTask1** is woken up. **Example\_MutexTask1** acquires the mutex and then releases the mutex. At last, the mutex is deleted. +1. Create a mutex for the **ExampleMutex** task. Lock task scheduling, and create two tasks **ExampleMutexTask1** and **ExampleMutexTask2**. Enable **ExampleMutexTask2** to permanently wait until a mutex is acquired, and enter sleep for 10 ticks after successfully acquiring a mutest. Enable **ExampleMutexTask1** to apply for a mutex with a timeout period of 10 ticks and then wait permanently until obtaining a mutex. **ExampleMutexTask2** has a higher priority than **ExampleMutexTask1**. Then, unlock task scheduling. + +2. **ExampleMutexTask2** (which has a higher priority) is scheduled and applies for a mutex. After acquiring the mutex, **ExampleMutexTask2** starts to sleep for 100 ticks. **ExampleMutexTask2** is suspended, and **ExampleMutexTask1** is woken up. + +3. **ExampleMutexTask1** applies for the mutex with a timeout period of 10 ticks. Because the mutex is still held by **ExampleMutexTask2**, **ExampleMutexTask1** is suspended. After 10 ticks, **ExampleMutexTask1** is woken up and starts to wait permanently for a mutex. **ExampleMutexTask1** is suspended because the mutex is still held by **ExampleMutexTask2**. + +4. After 100 ticks, **ExampleMutexTask2** is woken up and releases the mutex, and **ExampleMutexTask1** is woken up. **ExampleMutexTask1** acquires the mutex and is executed. After the task is complte, **ExampleMutexTask1** releases the mutex. At last, the mutex is deleted. + ### Sample Code The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleMutex** in **TestTaskEntry**. + + ``` -#include #include "los_mux.h" -/* Mutex handler ID*/ +/* Mutex handle. */ UINT32 g_testMux; -/* Task ID*/ -UINT32 g_testTaskId01; -UINT32 g_testTaskId02; -VOID Example_MutexTask1(VOID) +VOID ExampleMutexTask1(VOID) { UINT32 ret; printf("task1 try to get mutex, wait 10 ticks.\n"); /* Request a mutex. */ ret = LOS_MuxPend(g_testMux, 10); - if (ret == LOS_OK) { printf("task1 get mutex g_testMux.\n"); - /*Release the mutex. */ + /* Release the mutex. This branch is reserved for exceptions. */ LOS_MuxPost(g_testMux); + LOS_MuxDelete(g_testMux); return; - } + } + if (ret == LOS_ERRNO_MUX_TIMEOUT ) { - printf("task1 timeout and try to get mutex, wait forever.\n"); - /* Request a mutex. */ - ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER); - if (ret == LOS_OK) { - printf("task1 wait forever, get mutex g_testMux.\n"); - /*Release the mutex. */ - LOS_MuxPost(g_testMux); - /* Delete the mutex. */ - LOS_MuxDelete(g_testMux); - printf("task1 post and delete mutex g_testMux.\n"); - return; - } + printf("task1 timeout and try to get mutex, wait forever.\n"); + /* Request a mutex. */ + ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER); + if (ret == LOS_OK) { + printf("task1 wait forever, get mutex g_testMux.\n"); + /* Release the mutex. */ + LOS_MuxPost(g_testMux); + /* Delete the mutex. */ + LOS_MuxDelete(g_testMux); + printf("task1 post and delete mutex g_testMux.\n"); + return; + } } + return; } -VOID Example_MutexTask2(VOID) +VOID ExampleMutexTask2(VOID) { printf("task2 try to get mutex, wait forever.\n"); /* Request a mutex. */ (VOID)LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER); - printf("task2 get mutex g_testMux and suspend 100 ticks.\n"); - /* Enable the task to enter sleep mode for 100 ticks. */ + /* Enable the task to enter sleep mode for 100 ticks. */ LOS_TaskDelay(100); printf("task2 resumed and post the g_testMux\n"); @@ -155,11 +137,13 @@ VOID Example_MutexTask2(VOID) return; } -UINT32 Example_TaskEntry(VOID) +UINT32 ExampleMutex(VOID) { UINT32 ret; - TSK_INIT_PARAM_S task1; - TSK_INIT_PARAM_S task2; + TSK_INIT_PARAM_S task1 = { 0 }; + TSK_INIT_PARAM_S task2 = { 0 }; + UINT32 taskId01; + UINT32 taskId02; /* Create a mutex. */ LOS_MuxCreate(&g_testMux); @@ -168,24 +152,22 @@ UINT32 Example_TaskEntry(VOID) LOS_TaskLock(); /* Create task 1. */ - memset(&task1, 0, sizeof(TSK_INIT_PARAM_S)); - task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1; + task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask1; task1.pcName = "MutexTsk1"; task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; task1.usTaskPrio = 5; - ret = LOS_TaskCreate(&g_testTaskId01, &task1); + ret = LOS_TaskCreate(&taskId01, &task1); if (ret != LOS_OK) { printf("task1 create failed.\n"); return LOS_NOK; } /* Create task 2. */ - memset(&task2, 0, sizeof(TSK_INIT_PARAM_S)); - task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2; + task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask2; task2.pcName = "MutexTsk2"; task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; task2.usTaskPrio = 4; - ret = LOS_TaskCreate(&g_testTaskId02, &task2); + ret = LOS_TaskCreate(&taskId02, &task2); if (ret != LOS_OK) { printf("task2 create failed.\n"); return LOS_NOK; @@ -198,9 +180,10 @@ UINT32 Example_TaskEntry(VOID) } ``` + ### Verification -The development is successful if the return result is as follows: + The development is successful if the return result is as follows: ``` task2 try to get mutex, wait forever. @@ -211,4 +194,3 @@ task2 resumed and post the g_testMux task1 wait forever, get mutex g_testMux. task1 post and delete mutex g_testMux. ``` - diff --git a/en/device-dev/kernel/kernel-mini-basic-ipc-queue.md b/en/device-dev/kernel/kernel-mini-basic-ipc-queue.md index 1994a100158ff0469249581005f7ee8021d68330..3f874e55624965233b940bf1a33d378120a47762 100644 --- a/en/device-dev/kernel/kernel-mini-basic-ipc-queue.md +++ b/en/device-dev/kernel/kernel-mini-basic-ipc-queue.md @@ -1,170 +1,149 @@ # Queue -## Basic Concepts -A queue, also called a message queue, is a data structure used for communication between tasks. The queue receives messages of unfixed length from tasks or interrupts, and determines whether to store the transferred messages in the queue based on different APIs. +## Basic Concepts -Tasks can read messages from a queue. When the queue has no messages, the tasks are suspended. When the queue has a new message, the suspended tasks are woken up and process the new message. Tasks can also write messages to the queue. When the queue is full, the write task is suspended. When there is an available message node in the queue, the suspended write task is woken up and writes a message. +A message queue, also called a queue, is a mechanism for communication between tasks. The queue receives messages of unfixed length from tasks or interrupts, and determines whether to put the messages in the queue based on different APIs. + +Tasks can read messages from a queue. When the queue has no messages, the read task is suspended. When the queue has a new message, the suspended task is woken up to process new messages. Tasks can also write messages to the queue. When the queue is full, the write task is suspended. When there is an available message node in the queue, the suspended write task is woken up and writes a message. You can adjust the timeout period of the read queue and write queue to adjust the block mode of the read and write APIs. If the timeout period is set to **0** for the read queue and write queue, tasks will not be suspended and the API directly returns. This is the non-block mode. If the timeout period is greater than **0**, block mode is used. An asynchronous processing mechanism is provided to allow messages in a queue not to be processed immediately. In addition, queues can be used to buffer messages and implement asynchronous task communication. Queues have the following features: -- Messages are queued in first-in-first-out \(FIFO\) mode and can be read and written asynchronously. -- Both the read queue and write queue support the timeout mechanism. -- Each time a message is read, the message node becomes available. -- The types of messages to be sent are determined by the parties involved in communication. Messages of different lengths \(not exceeding the message node size of the queue\) are allowed. -- A task can receive messages from and send messages to any message queue. -- Multiple tasks can receive messages from and send messages to the same queue. -- When a queue is created, the required dynamic memory space is automatically allocated in the queue API. +- Messages are queued in first-in-first-out (FIFO) mode and can be read and written asynchronously. +- Both the read queue and write queue support the timeout mechanism. +- Each time a message is read, the message node becomes available. +- The types of messages to be sent are determined by the parties involved in communication. Messages of different lengths (not exceeding the message node size of the queue) are allowed. +- A task can receive messages from and send messages to any message queue. +- Multiple tasks can receive messages from and send messages to the same queue. +- The system dynamically applies for memory space required for creating normal queues. +- The space required for creating a static queue is passed in by the user. When the static queue is deleted, the space also needs to be released by the user. + ## Working Principles ### Queue Control Block +During the initialization of a queue, a control block, containing the queue name and status, is allocated. The control block is released when the queue is deleted. + +The data structure of the queue control block is as follows: + + ``` -/** - * Data structure of the queue control block - */ typedef struct { - UINT8 *queue; /* Pointer to the memory space of queue messages */ - UINT16 queueState; /* Queue status*/ - UINT16 queueLen; /* Number of message nodes in a queue, that is, the queue length */ - UINT16 queueSize; /* Size of a message node */ - UINT16 queueID; /*Queue ID */ - UINT16 queueHead; /* Position of the message head node (array subscript)*/ - UINT16 queueTail; /* Position of the message tail node (array subscript) */ - UINT16 readWriteableCnt[OS_READWRITE_LEN];/* The array element with subscript 0 indicates the number of readable messages in a queue. - The element with subscript 1 indicates the number of writable messages in a queue. */ - LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* A linked list of tasks waiting to read or write messages. - Subscript 0: list of tasks waiting to read messages. Subscript 1: list of tasks waiting to write messages. */ - LOS_DL_LIST memList; /* A linked list of memory blocks*/ + UINT8 *queue; /* Pointer to the memory space of the queue */ + UINT8 *queueName /* Queue name */ + UINT16 queueState; /* Queue status */ + UINT16 queueLen; /* Number of message nodes in the queue, that is, the queue length */ + UINT16 queueSize; /* Size of a message node */ + UINT16 queueID; /* Queue ID */ + UINT16 queueHead; /* Position of the message head node (array subscript) */ + UINT16 queueTail; /* Position of the message tail node (array subscript) */ + UINT16 readWriteableCnt[OS_READWRITE_LEN]; /* The element whose array subscript is 0 indicates the number of readable messages in the queue, + The element whose array subscript is 1 indicates the number of writable messages in the queue */ + LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* A linked list of tasks waiting to read or write messages. + Subscript 0: read the linked list. Subscript 1: write the linked list. */ + LOS_DL_LIST memList; /* A linked list of memory blocks */ } LosQueueCB; ``` Each queue control block contains information about the queue status. -- **OS\_QUEUE\_UNUSED**: The queue is not in use. -- **OS\_QUEUE\_INUSED**: The queue is in use. +- **OS_QUEUE_UNUSED**: The queue is not in use. + +- **OS_QUEUE_INUSED**: The queue is in use. + + +### Working Principles + +- The queue ID is returned when a queue is created successfully. + +- The queue control block contains **Head** and **Tail**, which indicate the storage status of messages in a queue. **Head** indicates the start position of occupied message nodes in the queue. **Tail** indicates the end position of the occupied message nodes and the start position of idle message nodes. When a queue is created, **Head** and **Tail** point to the start position of the queue. -### Working Principles +- When data is to be written to a queue, **readWriteableCnt[1]** is used to determine whether data can be written to the queue. If **readWriteableCnt[1]** is **0**, the queue is full and data cannot be written to it. Data can be written to the head node or tail node of a queue. To write data to the tail node, locate the start idle message node based on **Tail** and write data to it. If **Tail** is pointing to the tail of the queue, the rewind mode is used. To write data to the head node, locate previous node based on **Head** and write data to it. If **Head** is pointing to the start position of the queue, the rewind mode is used. -- The queue ID is returned if a queue is created successfully. -- The queue control block contains **Head** and **Tail**, which indicate the storage status of messages in a queue. **Head** indicates the start position of occupied message nodes in the queue. **Tail** indicates the end position of the occupied message nodes and the start position of idle message nodes. When a queue is created, **Head** and **Tail** point to the start position of the queue. -- When data is to be written to a queue, **readWriteableCnt\[1\]** is used to determine whether data can be written to the queue. If **readWriteableCnt\[1\]** is **0**, the queue is full and data cannot be written to it. Data can be written to the head node or tail node of a queue. To write data to the tail node, locate the start idle message node based on **Tail** and write data to it. If **Tail** is pointing to the tail of the queue, the rewind mode is used. To write data to the head node, locate previous node based on **Head** and write data to it. If **Head** is pointing to the start position of the queue, the rewind mode is used. -- When a queue is to be read, **readWriteableCnt\[0\]** is used to determine whether the queue has messages to read. Reading an idle queue \(**readWriteableCnt\[0\]** is **0**\) will cause task suspension. If the queue has messages to read, the system locates the first node to which data is written based on **Head** and read the message from the node. If **Head** is pointing to the tail of the queue, the rewind mode is used. -- When a queue is to be deleted, the system locates the queue based on the queue ID, sets the queue status to **OS\_QUEUE\_UNUSED**, sets the queue control block to the initial state, and releases the memory occupied by the queue. +- When a queue is to be read, **readWriteableCnt[0]** is used to determine whether the queue has messages to read. Reading an idle queue (**readWriteableCnt[0]** is** 0**) will cause task suspension. If the queue has messages to read, the system locates the first node to which data is written based on **Head** and read the message from the node. If **Head** is pointing to the tail of the queue, the rewind mode is used. -**Figure 1** Reading and writing data in a queue -![](figures/reading-and-writing-data-in-a-queue.png "reading-and-writing-data-in-a-queue") +- When a queue is to be deleted, the system locates the queue based on the queue ID, sets the queue status to **OS_QUEUE_UNUSED**, sets the queue control block to the initial state, and releases the memory occupied by the queue. + + **Figure 1** Reading and writing data in a queue + + ![](figures/reading-and-writing-data-in-a-queue.png "reading-and-writing-data-in-a-queue") The preceding figure illustrates how to write data to the tail node only. Writing data to the head node is similar. + ## Available APIs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Creating or deleting a message queue

-

LOS_QueueCreate

-

Creates a message queue. The system dynamically allocates the queue space.

-

LOS_QueueDelete

-

Deletes the specified queue based on the queue ID.

-

Reading or writing data in a queue (without the content contained in the address)

-

LOS_QueueRead

-

Reads data in the head node of the specified queue. The data in the queue node is an address.

-

LOS_QueueWrite

-

Writes the value of the input parameter bufferAddr (buffer address) to the tail node of the specified queue.

-

LOS_QueueWriteHead

-

Writes the value of the input parameter bufferAddr (buffer address) to the head node of the specified queue.

-

Reading or writing in a queue (with the content contained in the address)

-

LOS_QueueReadCopy

-

Reads data from the head node of the specified queue.

-

LOS_QueueWriteCopy

-

Writes the data saved in the input parameter bufferAddr to the tail node of the specified queue.

-

LOS_QueueWriteHeadCopy

-

Writes the data saved in the input parameter bufferAddr to the head node of the specified queue.

-

Obtaining queue information

-

LOS_QueueInfoGet

-

Obtains information about the specified queue, including the queue ID, queue length, message node size, head node, tail node, number of readable nodes, number of writable nodes, tasks waiting for read operations, and tasks waiting for write operations.

-
+| Category| Description| +| -------- | -------- | +| Creating or deleting a message queue| **LOS_QueueCreate**: creates a message queue. The system dynamically allocates the queue space.
**LOS_QueueCreateStatic**: creates a static message queue. You need to pass in the queue space.
**LOS_QueueDelete**: deletes a message queue. After a static message queue is deleted, you need to release the queue space.| +| Reading or writing data (address without the content) in a queue| **LOS_QueueRead**: reads data in the head node of the specified queue. The data in the queue node is an address.
**LOS_QueueWrite**: writes the **bufferAddr** (buffer address) to the tail node of the specified queue.
**LOS_QueueWriteHead**: writes the **bufferAddr** (buffer address) to the head node of the specified queue.| +| Reading or writing data (data and address) in a queue| **LOS_QueueReadCopy**: reads data from the head node of a specified queue.
**LOS_QueueWriteCopy**: writes the data saved in the **bufferAddr** to the tail node of the specified queue.
**LOS_QueueWriteHeadCopy**: writes the data saved in the **bufferAddr** to the head node of the specified queue.| +| Obtaining queue information| **LOS_QueueInfoGet**: obtains queue information, including the queue ID, queue length, message node size, head node, tail node, number of readable/writable nodes, and tasks waiting for read/write operations.| + ## How to Develop -1. Call **LOS\_QueueCreate** to create a queue. The queue ID is returned when the queue is created. -2. Call **LOS\_QueueWrite** or **LOS\_QueueWriteCopy** to write messages to the queue. -3. Call **LOS\_QueueRead** or **LOS\_QueueReadCopy** to read messages from the queue. -4. Call **LOS\_QueueInfoGet** to obtain queue information. -5. Call **LOS\_QueueDelete** to delete the queue. - ->![](../public_sys-resources/icon-note.gif) **NOTE**
->- The maximum number of queues supported by the system is the total number of queue resources of the system, not the number of queue resources available to users. For example, if the system software timer occupies one more queue resource, the number of queue resources available to users decreases by one. ->- The input parameters queue name and flags passed when a queue is created are reserved for future use. ->- The input parameter **timeOut** in the queue interface function is relative time. ->- **LOS\_QueueReadCopy**, **LOS\_QueueWriteCopy**, and **LOS\_QueueWriteHeadCopy** are a group of APIs that must be used together. **LOS\_QueueRead**, **LOS\_QueueWrite**, and **LOS\_QueueWriteHead** are a group of APIs that must be used together. ->- As **LOS\_QueueWrite**, **LOS\_QueueWriteHead**, and **LOS\_QueueRead** are used to manage data addresses, you must ensure that the memory directed by the pointer obtained by calling **LOS\_QueueRead** is not modified or released abnormally when the queue is being read. Otherwise, unpredictable results may occur. ->- If the input parameter **bufferSize** in **LOS\_QueueReadCopy** is less than the length of the message, the message will be truncated. ->- **LOS\_QueueWrite**, **LOS\_QueueWriteHead**, and **LOS\_QueueRead** are called to manage data addresses, which means that the actual data read or written is pointer data. Therefore, before using these APIs, ensure that the message node size is the pointer length during queue creation, to avoid waste and read failures. +1. Call **LOS_QueueCreate** to create a queue. The queue ID is returned when the queue is created. + +2. Call **LOS_QueueWrite** or **LOS_QueueWriteCopy** to write data to the queue. + +3. Call **LOS_QueueRead** or **LOS_QueueReadCopy** to read data from the queue. + +4. Call **LOS_QueueInfoGet** to obtain queue information. + +5. Call **LOS_QueueDelete** to delete a queue. + + +> **NOTE** +> - The maximum number of queues supported by the system is the total number of queue resources of the system, not the number of queue resources available to users. For example, if the system software timer occupies one more queue resource, the number of queue resources available to users decreases by one. +> +> - The input parameters queue name and flags passed when a queue is created are reserved for future use. +> +> - The input parameter **timeOut** in the queue interface function is relative time. +> +> - **LOS_QueueReadCopy**, **LOS_QueueWriteCopy**, and **LOS_QueueWriteHeadCopy** are a group of APIs that must be used together. **LOS_QueueRead**, **LOS_QueueWrite**, and **LOS_QueueWriteHead** are a group of APIs that must be used together. +> +> - As **LOS_QueueWrite**, **LOS_QueueWriteHead**, and **LOS_QueueRead** are used to manage data addresses, you must ensure that the memory directed by the pointer obtained by calling **LOS_QueueRead** is not modified or released abnormally when the queue is being read. Otherwise, unpredictable results may occur. +> +> - If the read length of **LOS_QueueReadCopy** is less than the actual message length, the message will be truncated. +> +> - **LOS_QueueWrite**, **LOS_QueueWriteHead**, and **LOS_QueueRead** are called to manage data addresses, which means that the actual data read or written is pointer data. Therefore, before using these APIs, ensure that the message node size is the pointer length during queue creation, to avoid waste and read failures. + ## Development Example + ### Example Description -Create a queue and two tasks. Enable task 1 to call the queue write API to send messages, and enable task 2 to receive messages by calling the queue read API. +Create a queue and two tasks. Enable task 1 to write data to the queue, and task 2 to read data from the queue. + +1. Call **LOS_TaskCreate** to create task 1 and task 2. + +2. Call **LOS_QueueCreate** to create a message queue. + +3. Task 1 sends a message in **SendEntry**. + +4. Task 2 receives message in **RecvEntry**. + +5. Call **LOS_QueueDelete** to delete the queue. -1. Create task 1 and task 2 by calling **LOS\_TaskCreate**. -2. Create a message queue by calling **LOS\_QueueCreate**. -3. Enable messages to be sent in task 1 by calling **SendEntry**. -4. Enable messages to be received in task 2 by calling **RecvEntry**. -5. Call **LOS\_QueueDelete** to delete the queue. ### Sample Code The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleQueue** in **TestTaskEntry**. + + ``` #include "los_task.h" #include "los_queue.h" -static UINT32 g_queue; + +STATIC UINT32 g_queue; #define BUFFER_LEN 50 VOID SendEntry(VOID) @@ -174,8 +153,8 @@ VOID SendEntry(VOID) UINT32 len = sizeof(abuf); ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0); - if(ret != LOS_OK) { - printf("Failed to send the message, error: %x\n", ret); + if (ret != LOS_OK) { + printf("send message failure, error: %x\n", ret); } } @@ -185,70 +164,75 @@ VOID RecvEntry(VOID) CHAR readBuf[BUFFER_LEN] = {0}; UINT32 readLen = BUFFER_LEN; - // Sleep for 1s. + /* Sleep for 1s */ usleep(1000000); ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0); - if(ret != LOS_OK) { - printf("Failed to receive the message, error: %x\n", ret); + if (ret != LOS_OK) { + printf("recv message failure, error: %x\n", ret); } - printf("recv message: %s\n", readBuf); + printf("recv message: %s.\n", readBuf); ret = LOS_QueueDelete(g_queue); - if(ret != LOS_OK) { - printf("Failed to delete the queue, error: %x\n", ret); + if (ret != LOS_OK) { + printf("delete the queue failure, error: %x\n", ret); } - printf("Deleted the queue successfully.\n"); + printf("delete the queue success.\n"); } UINT32 ExampleQueue(VOID) { printf("start queue example.\n"); UINT32 ret = 0; - UINT32 task1, task2; - TSK_INIT_PARAM_S initParam = {0}; - - initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry; - initParam.usTaskPrio = 9; - initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; - initParam.pcName = "SendQueue"; + UINT32 task1; + UINT32 task2; + TSK_INIT_PARAM_S taskParam1 = { 0 }; + TSK_INIT_PARAM_S taskParam2 = { 0 }; LOS_TaskLock(); - ret = LOS_TaskCreate(&task1, &initParam); + + taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry; + taskParam1.usTaskPrio = 9; + taskParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam1.pcName = "SendQueue"; + ret = LOS_TaskCreate(&task1, &taskParam1); if(ret != LOS_OK) { - printf("Failed to create task1, error: %x\n", ret); + printf("create task1 failed, error: %x\n", ret); return ret; } - initParam.pcName = "RecvQueue"; - initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry; - initParam.usTaskPrio = 10; - ret = LOS_TaskCreate(&task2, &initParam); + taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry; + taskParam2.usTaskPrio = 10; + taskParam2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam2.pcName = "RecvQueue"; + ret = LOS_TaskCreate(&task2, &taskParam2); if(ret != LOS_OK) { - printf("Failed to create task2, error: %x\n", ret); + printf("create task2 failed, error: %x\n", ret); return ret; } ret = LOS_QueueCreate("queue", 5, &g_queue, 0, 50); if(ret != LOS_OK) { - printf("Failed to create the queue, error: %x\n", ret); + printf("create queue failure, error: %x\n", ret); } - printf("Created the queue successfully.\n"); + printf("create the queue success.\n"); LOS_TaskUnlock(); return ret; } ``` + ### Verification The development is successful if the return result is as follows: + + ``` start queue example. create the queue success. recv message: test message. delete the queue success. ``` - diff --git a/en/device-dev/kernel/kernel-mini-basic-ipc-sem.md b/en/device-dev/kernel/kernel-mini-basic-ipc-sem.md index cc810659be1db081a7a1710081c90718358b46b3..97c5d38a77ca4ec02f63f130603f9ab7addcf972 100644 --- a/en/device-dev/kernel/kernel-mini-basic-ipc-sem.md +++ b/en/device-dev/kernel/kernel-mini-basic-ipc-sem.md @@ -1,132 +1,111 @@ # Semaphore + ## Basic Concepts Semaphore is a mechanism for implementing communication between tasks. It implements synchronization between tasks or exclusive access to shared resources. In the data structure of a semaphore, there is usually a counter value indicating the available resources. The counter value can be: -- **0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist. -- Positive number: The semaphore is available. +- **0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist. + +- Positive number: The semaphore is available. + +Semaphores can be used to solve a problem of mutual exclusion or process synchronization. The usage of the counter value varies with the function of the semaphore. -The usage of the counter value varies with the function of the semaphore. +- If the semaphore is used to solve a problem of mutual exclusion, the counter value indicates the number of units of the shared resources available and its initial value cannot be **0**. The semaphore must be acquired before the shared resource is used, and released after the resource is used. When all shared resources are used, the semaphore counter is reduced to **0** and the tasks that need to obtain the semaphores will be blocked. This ensures exclusive access to shared resources. In addition, when the number of shared resources is **1**, a binary semaphore (similar to the mutex mechanism) is recommended. + +- If the semaphore is used to solve a problem of process synchronization, the initial semaphore counter value is **0**. Task 1 cannot acquire the semaphore and is blocked. Task 1 enters Ready or Running state only when the semaphore is released by task 2 or an interrupt. In this way, task synchronization is implemented. -- If the semaphore is used as a mutex, the counter value indicates the number of units of the shared resources available and its initial value cannot be **0**. The semaphore must be acquired before the shared resource is used, and released after the resource is used. When all shared resources are used, the semaphore counter is reduced to **0** and the tasks that need to obtain the semaphores will be blocked. This ensures exclusive access to shared resources. In addition, when the number of shared resources is **1**, a binary semaphore \(similar to the mutex mechanism\) is recommended. -- If the semaphore is used for synchronization, the initial semaphore counter value is **0**. When a task fails to acquire the semaphore, it will be blocked and enters Ready or Running state only when the semaphore is released. In this way, task synchronization is implemented. ## Working Principles -### Semaphore control block + +### Semaphore Control Block + ``` /** - * Data structure of the semaphore control block + * Data structure of the semaphore control block */ typedef struct { UINT16 semStat; /* Semaphore status */ UINT16 semType; /* Semaphore type */ UINT16 semCount; /* Semaphore count */ UINT16 semId; /* Semaphore index */ - LOS_DL_LIST semList; /* Insert the task blocked by the semaphore to the DL list.*/ + LOS_DL_LIST semList; /* Insert the task blocked by the semaphore to the DL list. */ } LosSemCB; ``` + ### Working Principles -Initializing semaphores: Request memory for the semaphores configured \(the number of semaphores can be configured in the **LOSCFG\_BASE\_IPC\_SEM\_LIMIT** macro by users\), set all semaphores to the unused state, and add them to the linked list for unused semaphores. +Initializing semaphores: Request memory for the semaphores configured (the number of semaphores can be configured in the **LOSCFG_BASE_IPC_SEM_LIMIT** macro), set all semaphores to the unused state, and add them to the linked list for unused semaphores. Creating a semaphore: Obtain a semaphore from the linked list for unused semaphores and set its initial value. -Requesting a semaphore: If the counter value is greater than **0**, the system allocates a semaphore, decreases the counter value by 1, and returns a success message. If the counter value is **0**, the task is blocked and waits for other tasks to release a semaphore. The waiting timeout period can be set. When a task is blocked by a semaphore, the task will be added to the end of the semaphore waiting task queue. +Semaphore request: If the counter value is greater than 0, the system allocates a semaphore, decreases the value by 1, and returns a success message. Otherwise, the system blocks the task and adds the task to the end of a task queue waiting for semaphores. The wait timeout period can be set. -Releasing a semaphore: If there is no task waiting for the semaphore released, the counter is incremented by 1. Otherwise, wake up the first task in the semaphore waiting queue. +When a semaphore is released, if there is no task waiting for it, the counter is increased by 1. Otherwise, the first task in the wait queue is woken up. -Deleting a semaphore: Set the semaphore in use to the unused state, and adds it to the linked list for unused semaphores. +Semaphore deletion: The system sets a semaphore in use to the unused state and inserts it to the linked list of unused semaphores. -A semaphore can also be used to limit the number of tasks that can access the shared resource at the same time. When the number of tasks accessing the resource reaches the limit, other tasks will be blocked until a task releases the semaphore. +A semaphore places a limit on the number of tasks accessing the shared resource concurrently. When the number of tasks accessing the shared resource reaches the maximum, other tasks that attempt to obtain the resource are blocked until a semaphore is released. -**Figure 1** Semaphore working mechanism for mini systems +**Figure 1** Semaphore working mechanism for the mini system ![](figures/semaphore-working-mechanism-for-mini-systems.png "semaphore-working-mechanism-for-mini-systems") + ## Available APIs - - - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Creating or deleting a semaphore

-

LOS_SemCreate

-

Creates a semaphore and returns the semaphore ID.

-

LOS_BinarySemCreate

-

Creates a binary semaphore. The maximum counter value is 1.

-

LOS_SemDelete

-

Deletes a semaphore.

-

Requesting or releasing a semaphore

-

LOS_SemPend

-

Requests a specified semaphore and sets the timeout period.

-

LOS_SemPost

-

Posts (releases) a semaphore.

-
+| Category| Description| +| -------- | -------- | +| Creating or deleting a semaphore| **LOS_SemCreate**: creates a semaphore and returns the semaphore ID.
**LOS_BinarySemCreate**: creates a binary semaphore. The maximum count value is **1**.
**LOS_SemDelete**: deletes a semaphore.| +| Requesting or releasing a semaphore| **LOS_SemPend**: requests a semaphore and sets the timeout period.
**LOS_SemPost**: releases a semaphore.| + ## How to Develop -1. Call **LOS\_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS\_BinarySemCreate**. -2. Call **LOS\_SemPend** to request a semaphore. -3. Call **LOS\_SemPost** to release a semaphore. -4. Call **LOS\_SemDelete** to delete a semaphore. +1. Call **LOS_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS_BinarySemCreate**. + +2. Call **LOS_SemPend** to request a semaphore. + +3. Call **LOS_SemPost** to release a semaphore. + +4. Call **LOS_SemDelete** to delete a semaphore. + + +> **NOTE**
+> As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts. ->![](../public_sys-resources/icon-note.gif) **NOTE:** ->As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts. ## Development Example + ### Example Description This example implements the following: -1. Create a semaphore in task **ExampleSem** and lock task scheduling. Create two tasks **ExampleSemTask1** and **ExampleSemTask2** \(with higher priority\). Enable the two tasks to request the same semaphore. Unlock task scheduling. Enable task **ExampleSem** to enter sleep mode for 400 ticks. Release the semaphore in task **ExampleSem**. -2. Enable** ExampleSemTask2** to enter sleep mode for 20 ticks after acquiring the semaphore. \(When **ExampleSemTask2** is delayed, **ExampleSemTask1** is woken up.\) -3. Enable **ExampleSemTask1** to request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. \(Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended. **ExampleSemTask1** is woken up after 10 ticks.\) Enable **ExampleSemTask1** to request the semaphore in permanent block mode after it is woken up 10 ticks later. \(Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended.\) -4. After 20 ticks, **ExampleSemTask2** is woken up and releases the semaphore. **ExampleSemTask1** acquires the semaphore and is scheduled to run. When **ExampleSemTask1** is complete, it releases the semaphore. -5. Task **ExampleSem** is woken up after 400 ticks and deletes the semaphore. +1. Create a semaphore for the **ExampleSem** task, and lock task scheduling. Create two tasks **ExampleSemTask1** and **ExampleSemTask2**. The priority of **ExampleSemTask2** is higher than that of **ExampleSemTask1**. **ExampleSemTask1** and **ExampleSemTask2** apply for the same semaphore. Make **ExampleSemTask2** sleep for 20 ticks after acquiring the semaphore. Make **ExampleSemTask1** to apply for the semaphore with a timeout period of 10 ticks. If **ExampleSemTask1** still fails to acquire the semaphore after 10 ticks, **ExampleSemTask1** will wait until a semaphore is acquired. Unlock task scheduling. **ExampleSemTask1** and **ExampleSemTask2** are blocked and attempt to acquire a semaphore. The **ExampleSem** task releases the semaphore. + +2. **ExampleSemTask2** (which has the higher priority) acquires the semaphore and starts to sleep for 20 ticks. In this case, **ExampleSemTask1** is woken up. + +3. **ExampleSemTask1** requests a semaphore with a timeout period of 10 ticks. During this period, the semaphore is still held by **ExampleSemTask2**, therefore, **ExampleSemTask1** is suspended. After 10 ticks, **ExampleSemTask1** is woken up and waits permanently to acquire a semaphore. **ExampleSemTask1** is suspended. + +4. After 20 ticks, **ExampleSemTask2** is woken up and releases the semaphore. **ExampleSemTask1** acquires the semaphore and is scheduled to run. When **ExampleSemTask1** is complete, it releases the semaphore. + +5. After 400 ticks, **ExampleSem** is woken up and deletes the semaphore. + ### Sample Code The sample code is as follows: -``` -#include "los_sem.h" -#include "securec.h" +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleSem** in **TestTaskEntry**. -/* Task ID*/ -static UINT32 g_testTaskId01; -static UINT32 g_testTaskId02; -/* Task priority */ -#define TASK_PRIO_TEST 5 +``` +#include "los_sem.h" /* Semaphore structure ID */ static UINT32 g_semId; @@ -136,19 +115,17 @@ VOID ExampleSemTask1(VOID) UINT32 ret; printf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n"); - /* Request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. */ ret = LOS_SemPend(g_semId, 10); - /* The semaphore is acquired. */ if (ret == LOS_OK) { LOS_SemPost(g_semId); return; } + /* The semaphore is not acquired when the timeout period has expired. */ if (ret == LOS_ERRNO_SEM_TIMEOUT) { printf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n"); - /* Request the semaphore in permanent block mode. */ ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER); printf("ExampleSemTask1 wait_forever and get sem g_semId.\n"); @@ -166,15 +143,14 @@ VOID ExampleSemTask2(VOID) /* Request the semaphore in permanent block mode. */ ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER); - if (ret == LOS_OK) { printf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n"); } - /* Enable the task to enter sleep mode for 20 ticks. */ + /* Enable the task to enter sleep mode for 20 ticks. */ LOS_TaskDelay(20); - printf("ExampleSemTask2 post sem g_semId.\n"); + /* Release the semaphore. */ LOS_SemPost(g_semId); return; @@ -183,8 +159,10 @@ VOID ExampleSemTask2(VOID) UINT32 ExampleSem(VOID) { UINT32 ret; - TSK_INIT_PARAM_S task1; - TSK_INIT_PARAM_S task2; + TSK_INIT_PARAM_S task1 = { 0 }; + TSK_INIT_PARAM_S task2 = { 0 }; + UINT32 taskId1; + UINT32 taskId2; /* Create a semaphore. */ LOS_SemCreate(0, &g_semId); @@ -193,24 +171,22 @@ UINT32 ExampleSem(VOID) LOS_TaskLock(); /* Create task 1. */ - (VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1; task1.pcName = "TestTask1"; task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; - task1.usTaskPrio = TASK_PRIO_TEST; - ret = LOS_TaskCreate(&g_testTaskId01, &task1); + task1.usTaskPrio = 5; + ret = LOS_TaskCreate(&taskId1, &task1); if (ret != LOS_OK) { printf("task1 create failed.\n"); return LOS_NOK; } /* Create task 2. */ - (VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)); task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2; task2.pcName = "TestTask2"; task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; - task2.usTaskPrio = (TASK_PRIO_TEST - 1); - ret = LOS_TaskCreate(&g_testTaskId02, &task2); + task2.usTaskPrio = 4; + ret = LOS_TaskCreate(&taskId2, &task2); if (ret != LOS_OK) { printf("task2 create failed.\n"); return LOS_NOK; @@ -221,26 +197,27 @@ UINT32 ExampleSem(VOID) ret = LOS_SemPost(g_semId); - /* Enable the task to enter sleep mode for 400 ticks. */ + /* Enable the task to enter sleep mode for 400 ticks. */ LOS_TaskDelay(400); - /* Delete the semaphore. */ + /* Delete the semaphore. */ LOS_SemDelete(g_semId); return LOS_OK; } ``` + ### Verification The development is successful if the return result is as follows: + + ``` ExampleSemTask2 try get sem g_semId wait forever. -ExampleSemTask2 get sem g_semId and then delay 20 ticks. ExampleSemTask1 try get sem g_semId, timeout 10 ticks. - +ExampleSemTask2 get sem g_semId and then delay 20 ticks. ExampleSemTask1 timeout and try get sem g_semId wait forever. ExampleSemTask2 post sem g_semId. ExampleSemTask1 wait_forever and get sem g_semId. ``` - diff --git a/en/device-dev/kernel/kernel-mini-basic-list.md b/en/device-dev/kernel/kernel-mini-basic-list.md index 3a6b52a2e4a7c117d12bdc15787c5a961e8122df..8aa95e7bb40b481913cf60f4885f61f5afa9fa3c 100644 --- a/en/device-dev/kernel/kernel-mini-basic-list.md +++ b/en/device-dev/kernel/kernel-mini-basic-list.md @@ -3,150 +3,90 @@ ## Basic Concepts -A doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains a pointer to the previous node and a pointer to the next node in the sequence of nodes. The pointer head is unique. - -A doubly linked list allows access from a list node to its next node and also the previous node on the list. This data structure facilitates data search, especially traversal of a large amount of data. The symmetry of the doubly linked list also makes operations, such as insertion and deletion, easy. However, pay attention to the pointer direction when performing operations. - -## Available APIs - -The doubly linked list module provides the following APIs. For more details about the APIs, see the API reference. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Function

-

API

-

Description

-

Initializing a linked list

-

LOS_ListInit

-

Initializes a specified doubly linked list node as a doubly linked list.

-

LOS_DL_LIST_HEAD

-

Defines a doubly linked list node and initializes the node as a doubly linked list.

-

Adding a node

-

LOS_ListAdd

-

Inserts the specified node to the head of a doubly linked list.

-

LOS_ListTailInsert

-

Inserts the specified node to the end of a doubly linked list.

-

Deleting a node

-

LOS_ListDelete

-

Deletes the specified node from a doubly linked list.

-

LOS_ListDelInit

-

Deletes the specified node from the linked list and uses the node to initialize the linked list.

-

Checking whether a doubly linked list is empty

-

LOS_ListEmpty

-

Checks whether a linked list is empty.

-

Obtaining structure information

-

LOS_DL_LIST_ENTRY

-

Obtains the address of the structure that contains the linked list. The first input parameter of the API indicates a node in the list, the second input parameter indicates the name of the structure to be obtained, and the third input parameter indicates the name of the linked list in the structure.

-

LOS_OFF_SET_OF

-

Obtains the offset of a member in a specified structure relative to the start address of the structure.

-

Traversing a doubly linked list

-

LOS_DL_LIST_FOR_EACH

-

Traverses a doubly linked list.

-

LOS_DL_LIST_FOR_EACH_SAFE

-

Traverses a doubly linked list, and stores the next node of the current node for security verification.

-

Traversing the structure that contains the doubly linked list

-

LOS_DL_LIST_FOR_EACH_ENTRY

-

Traverses the specified doubly linked list and obtains the address of the structure that contains the linked list node.

-

LOS_DL_LIST_FOR_EACH_ENTRY_SAFE

-

Traverses the specified doubly linked list, obtains the structure address of the node that contains the linked list, and stores the structure address that contains the next node of the current node.

-
+A doubly linked list (DLL) is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains a pointer to the previous node and a pointer to the next node in the sequence of nodes. The pointer head is unique. + +A DLL allows access from a list node to its next node and also the previous node on the list. This data structure facilitates data search, especially traversal of a large amount of data. The symmetry of the DLL also makes operations, such as insertion and deletion, easy. However, pay attention to the pointer direction when performing operations. + + +## **Function Description** + +The table below describes APIs available for the DLL. For more details about the APIs, see the API reference. + +| **Category** | **Description** | +| ------------------------ | ------------------------------------------------------------ | +| Initializing and deleting a DLL | **LOS_ListInit**: initializes a DLL node as a DLL.
**LOS_DL_LIST_HEAD**: Defines a DLL node and initializes the node as a DLL.
**LOS_ListDelInit**: deletes a DLL.| +| Adding a node | **LOS_ListAdd**: adds a node to the head of a DLL.
**LOS_ListTailInsert**: inserts a node to the tail of a DLL.| +| Deleting a node | **LOS_ListDelete**: deletes a node from this DLL.
**LOS_ListDelInit**: deletes a node from this DLL and uses this node to initialize the DLL.| +| Checking a DLL | **LOS_ListEmpty**: checks whether a DLL is empty. | +| Obtaining structure information | **LOS_DL_LIST_ENTRY**: obtains the address of the structure that contains the DLL. The first input parameter of the API indicates a node in the list, the second input parameter indicates the name of the structure to be obtained, and the third input parameter indicates the name of the DLL in the structure.
**LOS_OFF_SET_OF**: obtains the offset of a member in the specified structure relative to the start address of the structure.| +| Traversing a DLL | **LOS_DL_LIST_FOR_EACH**: traverses a DLL.
**LOS_DL_LIST_FOR_EACH_SAFE**: traverses the DLL and stores the subsequent nodes of the current node for security verification.| +| Traversing the structure that contains a DLL| - **LOS_DL_LIST_FOR_EACH_ENTRY**: traverses a DLL and obtains the address of the structure that contains the linked list node.
**LOS_DL_LIST_FOR_EACH_ENTRY_SAFE**: traverses a DLL, obtains the address of the structure that contains the linked list node, and stores the address of the structure that contains the subsequent node of the current node.| + ## How to Develop -The typical development process of the doubly linked list is as follows: +The typical development process of the DLL is as follows: + +1. Call **LOS_ListInit** or **LOS_DL_LIST_HEAD** to initialize a DLL. + +2. Call **LOS_ListAdd** to add a node into the DLL. + +3. Call **LOS_ListTailInsert** to insert a node into the tail of the DLL. + +4. Call **LOS_ListDelete** to delete the specified node. + +5. Call **LOS_ListEmpty** to check whether the DLL is empty. + +6. Call **LOS_ListDelInit** to delete the specified node and initialize the DLL based on the node. -1. Call **LOS\_ListInit/LOS\_DL\_LIST\_HEAD** to initialize a doubly linked list. -2. Call **LOS\_ListAdd** to insert a node to the list. -3. Call **LOS\_ListTailInsert** to insert a node to the end of the list. -4. Call **LOS\_ListDelete** to delete the specified node. -5. Call **LOS\_ListEmpty** to check whether a linked list is empty. -6. Call **LOS\_ListDelInit** to delete a specified node, and initialize the linked list based on this node. ->![](../public_sys-resources/icon-note.gif) **NOTE:** ->- Pay attention to the operations of the front and back pointer of the node. ->- The linked list operation APIs are underlying APIs and do not check whether the input parameters are empty. You must ensure that the input parameters are valid. ->- If the memory of a linked list node is dynamically requested, release the memory after deleting the node. +> **NOTE** +> +> - Pay attention to the operations operations of the front and back pointer of the node. +> +> - The DLL APIs are underlying interfaces and do not check whether the input parameters are empty. You must ensure that the input parameters are valid. +> +> - If the memory of a linked list node is dynamically allocated, release the memory when deleting the node. -## Development Example -### Example Description +## Development Example + + +### Example Description This example implements the following: -1. Initialize a doubly linked list. -2. Add nodes. -3. Delete a node. -4. Check whether the operation is performed successfully. +1. Initialize the DLL. + +2. Add nodes. + +3. Delete nodes. + +4. Check the operation result. + ### Sample Code The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleList** in **TestTaskEntry**. + + ``` #include "stdio.h" #include "los_list.h" -static UINT32 ListSample(VOID) +STATIC UINT32 ExampleList(VOID) { LOS_DL_LIST listHead = {NULL,NULL}; LOS_DL_LIST listNode1 = {NULL,NULL}; LOS_DL_LIST listNode2 = {NULL,NULL}; - // Initialize the linked list. + /* Initialize a DLL. */ printf("Initial head\n"); LOS_ListInit(&listHead); - // Add node 1 and node 2 and verify their relationship. + /* Add node 1 and node 2 and verify their relationship. */ LOS_ListAdd(&listHead, &listNode1); if (listNode1.pstNext == &listHead && listNode1.pstPrev == &listHead) { printf("Add listNode1 success\n"); @@ -157,11 +97,11 @@ static UINT32 ListSample(VOID) printf("Tail insert listNode2 success\n"); } - // Delete the two nodes. + /* Delete the two nodes. */ LOS_ListDelete(&listNode1); LOS_ListDelete(&listNode2); - // Check that the linked list is empty. + /* Check whether the DLL is empty. */ if (LOS_ListEmpty(&listHead)) { printf("Delete success\n"); } @@ -170,14 +110,15 @@ static UINT32 ListSample(VOID) } ``` + ### Verification The development is successful if the return result is as follows: + ``` Initial head Add listNode1 success Tail insert listNode2 success Delete success ``` - diff --git a/en/device-dev/kernel/kernel-mini-basic-memory.md b/en/device-dev/kernel/kernel-mini-basic-memory.md index 3f48e6585876659e439d0bac9a578ab427074768..35586134b0c7228751958858e577695b2158650d 100644 --- a/en/device-dev/kernel/kernel-mini-basic-memory.md +++ b/en/device-dev/kernel/kernel-mini-basic-memory.md @@ -27,8 +27,9 @@ The static memory is a static array. The block size in the static memory pool is The static memory pool consists of a control block **LOS_MEMBOX_INFO** and several memory blocks **LOS_MEMBOX_NODE** of the same size. The control block is located at the head of the memory pool and used for memory block management. It contains the memory block size (**uwBlkSize**), number of memory blocks (**uwBlkNum**), number of allocated memory blocks (**uwBlkCnt**), and free list (**stFreeList**). Memory is allocated and released by block. Each memory block contains the pointer **pstNext** that points to the next memory block. - **Figure 1** Static memory - ![](figures/static-memory.png "static-memory") +**Figure 1** Static memory + +![](figures/static-memory.png "static-memory") ### Development Guidelines @@ -43,14 +44,14 @@ Use static memory allocation to obtain memory blocks of the fixed size. When the The following table describes APIs available for OpenHarmony LiteOS-M static memory management. For more details about the APIs, see the API reference. - **Table 1** APIs of the static memory module +**Table 1** APIs of the static memory module | Category| API| | -------- | -------- | | Initializing the static memory pool| **LOS_MemboxInit**: initializes a static memory pool, that is, sets the start address, total size, and size of each memory block based on input parameters.| | Clearing static memory blocks| **LOS_MemboxClr**: clears the memory blocks allocated from the static memory pool.| -| Allocating or releasing static memory| - **LOS_MemboxAlloc**: allocates a memory block from a specified static memory pool.
- **LOS_MemboxFree**: releases a memory block allocated from the static memory pool.| -| Obtaining or printing static memory pool information| - **LOS_MemboxStatisticsGet**: obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.
- **LOS_ShowBox**: prints information about all nodes in a specified static memory pool (the print level is **LOS_INFO_LEVEL**). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.| +| Allocating or releasing static memory| **LOS_MemboxAlloc**: allocates a memory block from a specified static memory pool.
**LOS_MemboxFree**: releases a memory block allocated from the static memory pool.| +| Obtaining or printing static memory pool information| **LOS_MemboxStatisticsGet**: obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.
**LOS_ShowBox**: prints information about all nodes in a specified static memory pool (the print level is **LOS_INFO_LEVEL**). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.| > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> The number of memory blocks in the memory pool after initialization is not equal to the total memory size divided by the memory block size. The reason is the control block of the memory pool and the control header of each memory block have memory overheads. When setting the total memory size, you need to consider these factors. @@ -91,21 +92,26 @@ This example implements the following: 6. Release the memory block. The sample code is as follows: + + The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleStaticMem** in **TestTaskEntry**. ``` #include "los_membox.h" -VOID Example_StaticMem(VOID) +#define MEMBOX_POOL_SIZE 100 +#define MEMBOX_BLOCK_SZIE 10 +#define MEMBOX_WR_TEST_NUM 828 +VOID ExampleStaticMem(VOID) { UINT32 *mem = NULL; - UINT32 blkSize = 10; - UINT32 boxSize = 100; - UINT32 boxMem[1000]; + UINT32 blkSize = MEMBOX_BLOCK_SZIE; + UINT32 poolSize = MEMBOX_POOL_SIZE; + UINT32 boxMem[MEMBOX_POOL_SIZE]; UINT32 ret; /* Initialize the memory pool. */ - ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize); + ret = LOS_MemboxInit(&boxMem[0], poolSize, blkSize); if(ret != LOS_OK) { printf("Membox init failed!\n"); return; @@ -115,19 +121,19 @@ VOID Example_StaticMem(VOID) /* Request a memory block. */ mem = (UINT32 *)LOS_MemboxAlloc(boxMem); - if (NULL == mem) { + if (mem == NULL) { printf("Mem alloc failed!\n"); return; } printf("Mem alloc success!\n"); - /* Assign a value. */ - *mem = 828; + /*Verify the read and write operations on the memory address. */ + *mem = MEMBOX_WR_TEST_NUM; printf("*mem = %d\n", *mem); /* Clear the memory. */ LOS_MemboxClr(boxMem, mem); - printf("Mem clear success \n *mem = %d\n", *mem); + printf("Mem clear success \n*mem = %d\n", *mem); / Release the memory. */ ret = LOS_MemboxFree(boxMem, mem); @@ -139,6 +145,7 @@ VOID Example_StaticMem(VOID) return; } + ``` @@ -164,8 +171,9 @@ Dynamic memory management allows memory blocks of any size to be allocated from The dynamic memory of the OpenHarmony LiteOS-M has optimized the memory space partitioning based on the Two-Level Segregate Fit (TLSF) algorithm to achieve higher performance and minimize fragmentation. The figure below shows the core algorithm of the dynamic memory. - **Figure 1** Dynamic memory algorithm for mini systems - ![](figures/dynamic-memory-algorithm-for-mini-systems.png "dynamic-memory-algorithm-for-mini-systems") +**Figure 2** Dynamic memory algorithm for mini systems + +![](figures/dynamic-memory-algorithm-for-mini-systems.png "dynamic-memory-algorithm-for-mini-systems") Multiple free lists are used for management based on the size of the free memory block. The free memory blocks are divided into two parts: [4, 127] and [27, 231], as indicated by the size class in the above figure. @@ -173,28 +181,25 @@ Multiple free lists are used for management based on the size of the free memory 2. The memory greater than 127 bytes is managed in power of two increments. The size of each range is [2^n, 2^(n+1) -1], where n is an integer in [7, 30]. This range is divided into 24 parts, each of which is further divided into 8 second-level (L2) ranges, as shown in Size Class and Size SubClass in the upper part of the figure. Each L2 range corresponds to a free list and a bit that indicates whether the free list is empty. There are a total of 192 (24 x 8) L2 ranges, corresponding to 192 free lists and 192 bits. -For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of [40, 43], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. - -If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th (31 + 2 x 8) free list in L2 range [2^9, 2^9+2^6], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time. +For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of [40, 43], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th (31 + 2 x 8) free list in L2 range [2^9, 2^9+2^6], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time. The figure below shows the memory management structure. - **Figure 2** Dynamic memory management structure for mini systems - ![](figures/dynamic-memory-management-structure-for-mini-systems.png "dynamic-memory-management-structure-for-mini-systems") +**Figure 3** Dynamic memory management structure for mini systems + +![](figures/dynamic-memory-management-structure-for-mini-systems.png "dynamic-memory-management-structure-for-mini-systems") - Memory pool header - The memory pool header contains the memory pool information, bitmap flag array, and free list array. The memory pool information includes the start address of the memory pool, total size of the heap memory, and attributes of the memory pool. The bitmap flag array consists of seven 32-bit unsigned integers. Each bit indicates whether the free list is inserted with free memory block nodes. The free list contains information about 223 free memory head nodes. The free memory head node information contains a memory node header and information about the previous and next nodes in the free list. + - Memory pool nodes - There are three types of nodes: free node, used node, and end node. Each memory node maintains the size and use flag of the memory node and a pointer to the previous memory node in the memory pool. The free nodes and used nodes have a data area, but the end node has no data area. -The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool. +The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool. The figure below shows how the discontiguous memory regions are logically integrated. -The figure below shows how the discontiguous memory regions are logically integrated. + **Figure 4** Integrating discontiguous memory regions - **Figure 3** Integrating discontiguous memory regions - ![](figures/integrating-discontiguous-memory-regions.png "integrating-discontiguous-memory-regions") +![](figures/integrating-discontiguous-memory-regions.png "integrating-discontiguous-memory-regions") The discontiguous memory regions are integrated into a unified memory pool as follows: @@ -202,7 +207,7 @@ The discontiguous memory regions are integrated into a unified memory pool as fo 2. Obtain the start address and length of the next memory region, and calculate the **gapSize** between the current memory region and its previous memory region. The **gapSize** is considered as a used virtual node. -3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS_MEM_NODE_HEAD_SIZE**. +3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS_MEM_NODE_HEAD_SIZE**, that is, **sizeof(struct OsMemUsedNodeHead)**. 4. Divide the current memory region into a free memory node and an end node, insert the free memory node to the free list, and set the link relationship between the nodes. @@ -221,15 +226,15 @@ Dynamic memory management allocates and manages memory resources requested by us The following table describes APIs available for OpenHarmony LiteOS-M dynamic memory management. For more details about the APIs, see the API reference. - **Table 1** APIs of the dynamic memory module +**Table 2** APIs of the dynamic memory module | Category| Description| | -------- | -------- | -| Initializing or deleting a memory pool| - **LOS_MemInit**: initializes a dynamic memory pool of the specified size.
- **LOS_MemDeInit**: deletes a memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.| -| Allocating or releasing dynamic memory| - **LOS_MemAlloc**: allocates memory of the specified size from the dynamic memory pool.
- **LOS_MemFree**: releases the memory allocated from the specified dynamic memory.
- **LOS_MemRealloc**: re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.| -| Obtaining memory pool information| - **LOS_MemPoolSizeGet**: obtains the total size of the specified dynamic memory pool.
- **LOS_MemTotalUsedGet**: obtains the total memory usage of the specified dynamic memory pool.
- **LOS_MemInfoGet**: obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.
- **LOS_MemPoolList**: prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled. | -| Obtaining memory block information| - **LOS_MemFreeNodeShow**: prints the size and number of free memory blocks in the specified memory pool.
- **LOS_MemUsedNodeShow**: prints the size and number of used memory blocks in the specified memory pool.| -| Checking memory pool integrity| **LOS_MemIntegrityCheck**: checks the integrity of the specified memory pool. It is valid only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled.| +| Initializing or deleting a memory pool| **LOS_MemInit**: initializes a dynamic memory pool of the specified size.
**LOS_MemDeInit**: deletes a memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.| +| Allocating or releasing dynamic memory| **LOS_MemAlloc**: allocates memory of the specified size from the dynamic memory pool.
**LOS_MemFree**: releases the memory allocated from the specified dynamic memory.
**LOS_MemRealloc**: re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.| +| Obtaining memory pool information| **LOS_MemPoolSizeGet**: obtains the total size of the specified dynamic memory pool.
**LOS_MemTotalUsedGet**: obtains the total memory usage of the specified dynamic memory pool.
**LOS_MemInfoGet**: obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.
**LOS_MemPoolList**: prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. This parameter is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.| +| Obtaining memory block information| **LOS_MemFreeNodeShow**: prints the size and number of free memory blocks in a memory pool.
**LOS_MemUsedNodeShow**: prints the size and number of used memory blocks in a memory pool.| +| Checking memory pool integrity| **LOS_MemIntegrityCheck**: checks the integrity of a memory pool. This parameter is valid only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled.| | Adding discontiguous memory regions| **LOS_MemRegionsAdd**: logically integrates multiple discontiguous memory regions into a unified memory pool. This parameter is valid only when **LOSCFG_MEM_MUL_REGIONS** is enabled. If the memory pool pointer **pool** is empty, initialize the first of the multiple memory regions in the memory pool and insert other memory regions as free nodes. If **pool** is not empty, insert the multiple memory regions into the specified memory pool as free nodes.| > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
@@ -245,15 +250,12 @@ The following table describes APIs available for OpenHarmony LiteOS-M dynamic me The typical development process of dynamic memory is as follows: 1. Call **LOS_MemInit** to initialize a memory pool. - After a memory pool is initialized, a memory pool control header and end node will be generated, and the remaining memory is marked as free nodes. The end node is the last node in the memory pool, and its size is **0**. -2. Call **LOS_MemAlloc** to allocate dynamic memory of any size. - +1. Call **LOS_MemAlloc** to allocate dynamic memory of any size. The system checks whether the dynamic memory pool has free memory blocks greater than the requested size. If yes, the system allocates a memory block and returns the pointer to the memory block. If no, the system returns NULL. If the memory block allocated is greater than the requested size, the system splits the memory block and inserts the remaining memory block to the free list. -3. Call **LOS_MemFree** to release dynamic memory. - +1. Call **LOS_MemFree** to release dynamic memory. The released memory block can be reused. When **LOS_MemFree** is called, the memory block will be reclaimed and marked as free nodes. When memory blocks are reclaimed, adjacent free nodes are automatically merged. @@ -273,18 +275,24 @@ This example implements the following: The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleDynMem** in **TestTaskEntry**. + ``` #include "los_memory.h" + #define TEST_POOL_SIZE (2*1024) -__attribute__((aligned(4))) UINT8 g_testPool[TEST_POOL_SIZE]; -VOID Example_DynMem(VOID) +#define MEMBOX_WR_TEST_NUM 828 + +__attribute__((aligned(4))) UINT8 g_testDynPool[TEST_POOL_SIZE]; + +VOID ExampleDynMem(VOID) { UINT32 *mem = NULL; UINT32 ret; /* Initialize the memory pool. */ - ret = LOS_MemInit(g_testPool, TEST_POOL_SIZE); + ret = LOS_MemInit(g_testDynPool, TEST_POOL_SIZE); if (LOS_OK == ret) { printf("Mem init success!\n"); } else { @@ -292,20 +300,20 @@ VOID Example_DynMem(VOID) return; } - /* Allocate memory. */ - mem = (UINT32 *)LOS_MemAlloc(g_testPool, 4); - if (NULL == mem) { + /* Request a memory block. */ + mem = (UINT32 *)LOS_MemAlloc(g_testDynPool, 4); + if (mem == NULL) { printf("Mem alloc failed!\n"); return; } printf("Mem alloc success!\n"); - /* Assign a value. */ - *mem = 828; + /*Verify the read and write operations on the memory address. */ + *mem = MEMBOX_WR_TEST_NUM; printf("*mem = %d\n", *mem); / Release the memory. */ - ret = LOS_MemFree(g_testPool, mem); + ret = LOS_MemFree(g_testDynPool, mem); if (LOS_OK == ret) { printf("Mem free success!\n"); } else { diff --git a/en/device-dev/kernel/kernel-mini-basic-soft.md b/en/device-dev/kernel/kernel-mini-basic-soft.md index e6bb601669fcc6fd7403c25ccea520323eeea70d..9acedf2ec41d9873d5ee1bb15836d2d617954ff5 100644 --- a/en/device-dev/kernel/kernel-mini-basic-soft.md +++ b/en/device-dev/kernel/kernel-mini-basic-soft.md @@ -32,20 +32,18 @@ The software timer counts time in ticks. When a software timer is created and st When a tick interrupt occurs, the tick interrupt handler scans the global timing list for expired timers. If such timers are found, the timers are recorded. -When the tick interrupt handling function is complete, the software timer task (with the highest priority) is woken up. In this task, the timeout callback function for the recorded timer is called. +When the tick interrupt handler is complete, the software timer task (with the highest priority) will be woken up. In this task, the timeout callback for the recorded timer is called. ### Timer States - OS_SWTMR_STATUS_UNUSED - The timer is not in use. When the timer module is initialized, all timer resources in the system are set to this state. + - OS_SWTMR_STATUS_CREATED - The timer is created but not started or the timer is stopped. When **LOS_SwtmrCreate** is called for a timer that is not in use or **LOS_SwtmrStop** is called for a newly started timer, the timer changes to this state. - + - OS_SWTMR_STATUS_TICKING - The timer is running (counting). When **LOS_SwtmrStart** is called for a newly created timer, the timer enters this state. @@ -64,15 +62,13 @@ The OpenHarmony LiteOS-M kernel provides the following types of software timers: The following table describes APIs available for the OpenHarmony LiteOS-M software timer module. For more details about the APIs, see the API reference. - **Table 1** Software timer APIs +**Table 1** Software timer APIs -| API| Description| +| Category| Description| | -------- | -------- | -| LOS_SwtmrCreate| Creates a timer.| -| LOS_SwtmrDelete| Deletes a timer.| -| LOS_SwtmrStart| Starts a timer.| -| LOS_SwtmrStop| Stops a timer.| -| LOS_SwtmrTimeGet| Obtains the remaining ticks of a software timer.| +| Creating or deleting a timer| **LOS_SwtmrCreate**: creates a timer.
**LOS_SwtmrDelete**: deletes a timer.| +| Starting or stopping a timer| **LOS_SwtmrStart**: starts a timer.
**LOS_SwtmrStop**: Stops a timer.| +| Obtaining remaining ticks of a software timer| **LOS_SwtmrTimeGet**: obtains the remaining ticks of a software timer.| ## How to Develop @@ -96,14 +92,14 @@ The typical development process of software timers is as follows: 6. Call **LOS_SwtmrDelete** to delete the software timer. ->![](../public_sys-resources/icon-note.gif) **NOTE** -> - Avoid too many operations in the callback function of the software timer. Do not use APIs or perform operations that may cause task suspension or blocking. +> **NOTE** +> - Avoid too many operations in the callback of the software timer. Do not use APIs or perform operations that may cause task suspension or blocking. > > - The software timers use a queue and a task resource of the system. The priority of the software timer tasks is set to **0** and cannot be changed. > > - The number of software timer resources that can be configured in the system is the total number of software timer resources available to the entire system, not the number of software timer resources available to users. For example, if the system software timer occupies one more resource, the number of software timer resources available to users decreases by one. > -> - If a one-shot software timer is created, the system automatically deletes the timer and reclaims resources after the timer times out and the callback function is executed. +> - If a one-shot software timer is created, the system automatically deletes the timer and reclaims resources after the timer times out and the callback is invoked. > > - For a one-shot software timer that will not be automatically deleted after expiration, you need to call **LOS_SwtmrDelete** to delete it and reclaim the timer resource to prevent resource leakage. @@ -122,7 +118,7 @@ The following programming example demonstrates how to: ### Sample Code -Prerequisites +**Prerequisites** - In **los_config.h**, **LOSCFG_BASE_CORE_SWTMR** is enabled. @@ -134,86 +130,107 @@ Prerequisites The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleSwtmr** in **TestTaskEntry**. + ``` #include "los_swtmr.h" -/* Timer count */ -UINT32 g_timerCount1 = 0; -UINT32 g_timerCount2 = 0; +/* Timer interval. */ +#define SWTMR_INTERVAL_LONG 1000 +#define SWTMR_INTERVAL_SHORT 100 -/* Task ID*/ -UINT32 g_testTaskId01; +/* Number of times that the timers are triggered. */ +UINT32 g_timerCount1 = 0; +UINT32 g_timerCount2 = 0; -void Timer1_Callback(UINT32 arg) //Callback 1 +/* Callback 1, for the one-shot software timer. */ +void Timer1Callback(UINT32 arg) { - UINT32 tick_last1; g_timerCount1++; - tick_last1 = (UINT32)LOS_TickCountGet(); // Obtain the current number of ticks. - printf("g_timerCount1=%d, tick_last1=%d\n", g_timerCount1, tick_last1); -} + printf("g_timerCount1=%d\n", g_timerCount1); +} -void Timer2_Callback(UINT32 arg) //Callback 2 +/* Callback 2, for the periodic software timer. */ +void Timer2Callback(UINT32 arg) { - UINT32 tick_last2; - tick_last2 = (UINT32)LOS_TickCountGet(); g_timerCount2++; - printf("g_timerCount2=%d tick_last2=%d\n", g_timerCount2, tick_last2); -} + printf("g_timerCount2=%d\n", g_timerCount2); +} -void Timer_example(void) +void SwtmrTest(void) { UINT32 ret; - UINT32 id1; // timer id1 - UINT32 id2; // timer id2 + UINT32 id1; // One-shot software timer. + UINT32 id2; // Periodic software timer. UINT32 tickCount; - /* Create a one-shot software timer, with the number of ticks set to 1000. When the number of ticks reaches 1000, callback function 1 is executed. */ - LOS_SwtmrCreate(1000, LOS_SWTMR_MODE_ONCE, Timer1_Callback, &id1, 1); +#if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1) + /* Create a one-shot software timer, with the number of ticks set to 1000. Invoke callback 1 when the number of ticks reaches 1000. */ + LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0, + OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE); + + /* Create a periodic software timer and invoke callback 2 every 100 ticks. */ + LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0, + OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE); +#else + /* Create a one-shot software timer, with the number of ticks set to 1000. Callback 1 will be invoked when the number of ticks reaches 1000. */ + LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0); - /* Create a periodic software timer and execute callback function 2 every 100 ticks. */ - LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, Timer2_Callback, &id2, 1); - printf("create Timer1 success\n"); + /* Create a periodic software timer and invoke callback 2 every 100 ticks. */ + LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0); +#endif - LOS_SwtmrStart(id1); // Start the one-shot software timer. - printf("start Timer1 success\n"); + /* Start the one-time software timer. */ + ret = LOS_SwtmrStart(id1); + printf("start Timer1 %s\n", (ret == LOS_OK) ? "success" : "failed"); - LOS_TaskDelay(200); // Delay 200 ticks. - LOS_SwtmrTimeGet(id1, &tickCount); // Obtain the number of remaining ticks of the one-short software timer. - printf("tickCount=%d\n", tickCount); + /* Short delay. The timer is not triggered yet. */ + LOS_TaskDelay(SWTMR_INTERVAL_SHORT); - LOS_SwtmrStop(id1); // Stop the software timer. - printf("stop Timer1 success\n"); + /* The one-short timer is not triggered yet. The timer can be stopped successfully. */ + ret = LOS_SwtmrStop(id1); + printf("stop timer1 %s\n", (ret == LOS_OK) ? "success" : "failed"); LOS_SwtmrStart(id1); - LOS_TaskDelay(1000); + + /* Long-time delay, triggered by the timer. */ + LOS_TaskDelay(SWTMR_INTERVAL_LONG); + + /* The timer is automatically deleted after being triggered. The stop operation should fail. */ + ret = LOS_SwtmrStop(id1); + printf("timer1 self delete test %s\n", (ret != LOS_OK) ? "success" : "failed"); - LOS_SwtmrStart(id2); // Start the periodic software timer. - printf("start Timer2\n"); + /* Start the periodic software timer. */ + ret = LOS_SwtmrStart(id2); + printf("start Timer2 %s\n", (ret == LOS_OK) ? "success" : "failed"); + + /* Long-time delay, triggered periodically by the timer. */ + LOS_TaskDelay(SWTMR_INTERVAL_LONG); - LOS_TaskDelay(1000); LOS_SwtmrStop(id2); - ret = LOS_SwtmrDelete(id2); // Delete the software timer. + + ret = LOS_SwtmrDelete(id2); if (ret == LOS_OK) { printf("delete Timer2 success\n"); } } -UINT32 Example_TaskEntry(VOID) +UINT32 ExampleSwtmr(VOID) { UINT32 ret; - TSK_INIT_PARAM_S task1; + TSK_INIT_PARAM_S taskParam = { 0 }; + UINT32 taskId; /* Lock task scheduling. */ LOS_TaskLock(); - /* Create task 1. */ - (VOID)memset(&task1, 0, sizeof(TSK_INIT_PARAM_S)); - task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Timer_example; - task1.pcName = "TimerTsk"; - task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; - task1.usTaskPrio = 5; - ret = LOS_TaskCreate(&g_testTaskId01, &task1); + /* Create a task. */ + taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SwtmrTest; + taskParam.pcName = "TimerTsk"; + taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam.usTaskPrio = 5; + ret = LOS_TaskCreate(&taskId, &taskParam); if (ret != LOS_OK) { printf("TimerTsk create failed.\n"); return LOS_NOK; @@ -221,7 +238,6 @@ UINT32 Example_TaskEntry(VOID) /* Unlock task scheduling. */ LOS_TaskUnlock(); - return LOS_OK; } ``` @@ -233,22 +249,20 @@ The output is as follows: ``` -create Timer1 success start Timer1 success -tickCount=798 -stop Timer1 success -g_timerCount1=1, tick_last1=1208 -delete Timer1 success -start Timer2 -g_timerCount2=1 tick_last2=1313 -g_timerCount2=2 tick_last2=1413 -g_timerCount2=3 tick_last2=1513 -g_timerCount2=4 tick_last2=1613 -g_timerCount2=5 tick_last2=1713 -g_timerCount2=6 tick_last2=1813 -g_timerCount2=7 tick_last2=1913 -g_timerCount2=8 tick_last2=2013 -g_timerCount2=9 tick_last2=2113 -g_timerCount2=10 tick_last2=2213 +stop timer1 success +g_timerCount1=1 +timer1 self delete test success +start Timer2 success +g_timerCount2=1 +g_timerCount2=2 +g_timerCount2=3 +g_timerCount2=4 +g_timerCount2=5 +g_timerCount2=6 +g_timerCount2=7 +g_timerCount2=8 +g_timerCount2=9 +g_timerCount2=10 delete Timer2 success ``` diff --git a/en/device-dev/kernel/kernel-mini-basic-task.md b/en/device-dev/kernel/kernel-mini-basic-task.md index e2b71e961f6335ce9da5485e8b025b0df88e7308..03d1ed7350e1f0d1de4f7769a1cf798d8eb5d985 100644 --- a/en/device-dev/kernel/kernel-mini-basic-task.md +++ b/en/device-dev/kernel/kernel-mini-basic-task.md @@ -5,7 +5,7 @@ From the perspective of the operating system, tasks are the minimum running units that compete for system resources. They can use or wait for CPUs, use system resources such as memory, and run independently. -The task module of the OpenHarmony LiteOS-M provides multiple tasks and supports switching between tasks, helping users manage business process procedures. The task module has the following features: +The task module of the OpenHarmony LiteOS-M supports switching between tasks to help users manage business process procedures. The task module has the following features: - Multiple tasks are supported. @@ -36,9 +36,11 @@ A task can be in any of the following states: **Task State Transitions** -**Figure 1** Task state transition
+**Figure 1** Task state transitions - ![](figures/task-state-transitions.png "task-state-transitions") +![](figures/task-state-transitions.png "task-state-transitions") + +A system may have multiple tasks at the same time. Therefore, tasks in the Ready state and Blocked state are added to the **Ready** queue and **Blocked** queue respectively. A queue is a collection of tasks in the same state. The sequence of adding tasks to a queue is irrelevant to the sequence of task status transition. There is only one task running at a time. Therefore, there is no queue for the running task. The task state transition process is as follows: @@ -48,12 +50,12 @@ The task state transition process is as follows: - Running → Blocked When a running task is blocked (suspended, delayed, or reading semaphores), it will be inserted to the blocked task queue and changes from the Running state to the Blocked state. Then, task switching is triggered to run the task with the highest priority in the Ready queue. -- Blocked → Ready (Blocked → Running) +- Blocked -> Ready (Prerequisites for Blocked -> Running) When a blocked task is recovered (for example, the task is resumed, the delay period or semaphore read period times out, or the task successfully reads a semaphore), the task will be added to the Ready queue and change from the Blocked state to the Ready state. If the priority of the recovered task is higher than that of the running task, task switching will be triggered to run the recovered task. Then, the task changes from the Ready state to the Running state. - Ready → Blocked - When a task in the Ready state is blocked (suspended), the task changes to the Blocked state and is deleted from the Ready queue. The blocked task will not be scheduled until it is recovered. + When a task in the Ready state is blocked (suspended), the task changes to the Blocked state and is removed from the Ready queue. The blocked task will not be scheduled until it is recovered. - Running → Ready When a task with a higher priority is created or recovered, tasks will be scheduled. The task with the highest priority in the Ready queue changes to the Running state. The originally running task changes to the Ready state and remains in the Ready queue. @@ -66,7 +68,7 @@ The task state transition process is as follows: **Task ID** -You will receive a task ID after successfully creating a task. The task IDs are unique in the operating system. You can suspend, restore, or query tasks by task ID. +A task ID is returned when a task is created. The task ID uniquely identifies a task in the system. You can suspend, restore, or query tasks by task ID. **Task Priority** @@ -84,7 +86,7 @@ An independent memory space for each task. The stack stores information such as Resources, such as registers, used during the running of a task. When a task is suspended, other running tasks might modify the register values of the suspended task. If the original task context is not saved when task switching occurs, an unknown error may occur when the task is recovered. The context information of switched-out tasks is saved into their own task stacks so that the context information can be resumed along with tasks and the system can start from the interrupted code after the tasks are resumed. -**Task Control Block** +**TCB** Each task has a task control block (TCB). A TCB contains task information, such as context stack pointer, state, priority, ID, name, and stack size. The TCB reflects the running status of a task. @@ -95,24 +97,23 @@ Task switching involves actions, such as obtaining the task with the highest pri ### Task Running Mechanism -When a task is created, the system initializes the task stack and presets the context. The system places the task entry function in the corresponding position so that the function is executed when the task enters the running state for the first time. +When a task is created, the system initializes the task stack and presets the context. The system places the task entry function in the corresponding position so that the function can be executed when the task enters the running state for the first time. ## Available APIs The following table describes APIs available for the OpenHarmony LiteOS-M task module. For more details about the APIs, see the API reference. - **Table 1** APIs of the task management module +**Table 1** APIs of the task management module | Category| Description| | -------- | -------- | -| Creating or deleting a task| **LOS_TaskCreateOnly**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.
**LOS_TaskCreate**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.
**LOS_TaskDelete**: deletes a task.| -| Controlling task status| **LOS_TaskResume**: resumes a suspended task to place the task in the Ready state.
**LOS_TaskSuspend**: suspends the specified task and performs task switching.
**LOS_TaskJoin**: suspends this task till the specified task is complete and the task control block resources are reclaimed.
**LOS_TaskDelay**: makes a task wait for a period of time (in ticks) and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of ticks.
**LOS_Msleep**: converts the input parameter number of milliseconds into number of ticks, and use the result to call **LOS_TaskDelay**.
**LOS_TaskYield**: sets the time slice of the current task to **0** to release CPU resources and schedule the task with the highest priority in the Ready queue to run.| +| Creating or deleting a task| **LOS_TaskCreateOnly**: creates a task and places the task in the Blocked state.
**LOS_TaskCreate**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.
**LOS_TaskDelete**: deletes a task.| +| Controlling task status| **LOS_TaskResume**: resumes a suspended task to place the task in the Ready state.
**LOS_TaskSuspend**: suspends the specified task and performs task switching.
**LOS_TaskJoin**: suspends this task till the specified task is complete and the task control block resources are reclaimed.
**LOS_TaskDelay**: makes a task wait for a period of time (in ticks) and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of ticks.
**LOS_Msleep**: makes a task wait for a period of time and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of milliseconds.
**LOS_TaskYield**: sets the time slice of the current task to **0** to release CPU resources and schedule the task with the highest priority in the Ready queue to run.| | Controlling task scheduling| **LOS_TaskLock**: locks task scheduling. However, tasks can still be interrupted.
**LOS_TaskUnlock**: unlocks task scheduling.
**LOS_Schedule**: triggers task scheduling.| | Controlling task priority| **LOS_CurTaskPriSet**: sets the priority for the current task.
**LOS_TaskPriSet**: sets the priority for a specified task.
**LOS_TaskPriGet**: obtains the priority of a specified task.| | Obtaining Job information| **LOS_CurTaskIDGet**: obtains the ID of the current task.
**LOS_NextTaskIDGet**: obtains the ID of the task with the highest priority in the Ready queue.
**LOS_NewTaskIDGet**: equivalent to **LOS_NextTaskIDGet**.
**LOS_CurTaskNameGet**: obtains the name of the current task.
**LOS_TaskNameGet**: obtains the name of a task.
**LOS_TaskStatusGet**: obtains the state of a task.
**LOS_TaskInfoGet**: obtains information about a specified task, including the task state, priority, stack size, stack pointer (SP), task entry function, and used stack space.
**LOS_TaskIsRunning**: checks whether the task module has started scheduling.| -| Updating task information| **LOS_TaskSwitchInfoGet**: obtains task switching information. The macro **LOSCFG_BASE_CORE_EXC_TSK_SWITCH** must be enabled.| - +| Updating task information| **LOS_TaskSwitchInfoGet**: obtains the task switching information. The macro **LOSCFG_BASE_CORE_EXC_TSK_SWITCH** must be enabled.| ## How to Develop @@ -130,7 +131,7 @@ The typical development process of the task module is as follows: 6. Use **LOS_TaskResume** to resume the suspended task. ->![](../public_sys-resources/icon-note.gif) **NOTE**
+> **NOTE** > - Running idle tasks reclaims the TCBs and stacks in the to-be-recycled linked list. > > - The task name is a pointer without memory space allocated. When setting the task name, do not assign the local variable address to the task name pointer. @@ -162,16 +163,18 @@ The typical development process of the task module is as follows: This example describes the priority-based task scheduling and use of task-related APIs, including creating, delaying, suspending, and resuming two tasks with different priorities, and locking/unlocking task scheduling. -The sample code is as follows: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleTask** in **TestTaskEntry**. ``` +#include "los_task.h" + UINT32 g_taskHiId; UINT32 g_taskLoId; -#define TSK_PRIOR_HI 4 -#define TSK_PRIOR_LO 5 +#define TSK_PRIOR_HI 3 /* Priority of a high-priority task. */ +#define TSK_PRIOR_LO 4 /* Priority of a low-priority task. */ -UINT32 Example_TaskHi(VOID) +UINT32 ExampleTaskHi(VOID) { UINT32 ret; @@ -184,7 +187,7 @@ UINT32 Example_TaskHi(VOID) return LOS_NOK; } - /*After 100 ticks elapse, the task is resumed. */ + /* After 100 ticks elapse, the task is resumed. */ printf("TaskHi LOS_TaskDelay Done.\n"); /* Suspend the task. */ @@ -198,7 +201,7 @@ UINT32 Example_TaskHi(VOID) } /* Entry function of low-priority tasks */ -UINT32 Example_TaskLo(VOID) +UINT32 ExampleTaskLo(VOID) { UINT32 ret; @@ -222,25 +225,26 @@ UINT32 Example_TaskLo(VOID) return ret; } -/* Task entry function used to create two tasks with different priorities */ -UINT32 Example_TskCaseEntry(VOID) +/* Task entry function used to create two tasks with different priorities. */ +UINT32 ExampleTask(VOID) { UINT32 ret; - TSK_INIT_PARAM_S initParam; + TSK_INIT_PARAM_S taskParam1 = { 0 }; + TSK_INIT_PARAM_S taskParam2 = { 0 }; /* Lock task scheduling to prevent newly created tasks from being scheduled prior to this task due to higher priority. */ LOS_TaskLock(); printf("LOS_TaskLock() Success!\n"); - initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskHi; - initParam.usTaskPrio = TSK_PRIOR_HI; - initParam.pcName = "TaskHi"; - initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; - initParam.uwResved = 0; /* Detach attributes. */ + taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskHi; + taskParam1.usTaskPrio = TSK_PRIOR_HI; + taskParam1.pcName = "TaskHi"; + taskParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam1.uwResved = LOS_TASK_ATTR_JOINABLE; /* Detach attribute. */ /* Create a task with higher priority. The task will not be executed immediately after being created, because task scheduling is locked. */ - ret = LOS_TaskCreate(&g_taskHiId, &initParam); + ret = LOS_TaskCreate(&g_taskHiId, &taskParam1); if (ret != LOS_OK) { LOS_TaskUnlock(); @@ -250,13 +254,13 @@ UINT32 Example_TskCaseEntry(VOID) printf("Example_TaskHi create Success!\n"); - initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskLo; - initParam.usTaskPrio = TSK_PRIOR_LO; - initParam.pcName = "TaskLo"; - initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskLo; + taskParam2.usTaskPrio = TSK_PRIOR_LO; + taskParam2.pcName = "TaskLo"; + taskParam2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; /* Create a low-priority task. The task will not be executed immediately after being created, because task scheduling is locked. */ - ret = LOS_TaskCreate(&g_taskLoId, &initParam); + ret = LOS_TaskCreate(&g_taskLoId, &taskParam2); if (ret != LOS_OK) { LOS_TaskUnlock(); printf("Example_TaskLo create Failed!\n"); @@ -269,7 +273,7 @@ UINT32 Example_TskCaseEntry(VOID) LOS_TaskUnlock(); ret = LOS_TaskJoin(g_taskHiId, NULL); if (ret != LOS_OK) { - printf("Join Example_TaskHi Failed!\n"); + printf("Join Example_TaskHi Failed!, 0x%x\n", ret); } else { printf("Join Example_TaskHi Success!\n"); } @@ -277,13 +281,12 @@ UINT32 Example_TskCaseEntry(VOID) } ``` - -### Verification +**Verification** The development is successful if the return result is as follows: -``` +``` LOS_TaskLock() Success! Example_TaskHi create Success! Example_TaskLo create Success! diff --git a/en/device-dev/kernel/kernel-basic-mini-time.md b/en/device-dev/kernel/kernel-mini-basic-time.md similarity index 61% rename from en/device-dev/kernel/kernel-basic-mini-time.md rename to en/device-dev/kernel/kernel-mini-basic-time.md index 375e481f4c04354b83d1e043cac3d3f7f0a83c0c..baca6e07c1faacb9efae35b2bfc1cdb577281433 100644 --- a/en/device-dev/kernel/kernel-basic-mini-time.md +++ b/en/device-dev/kernel/kernel-mini-basic-time.md @@ -15,10 +15,9 @@ The time management module of the OpenHarmony LiteOS-M kernel provides time conv ## Time Unit - Cycle - Cycle is the minimum time unit in the system. The cycle duration is determined by the system clock frequency, that is, the number of cycles per second. + - Tick - Tick is the basic time unit of the operating system and is determined by the number of ticks per second configured by the user. @@ -40,9 +39,22 @@ The following table describes APIs available for OpenHarmony LiteOS-M time manag | API| Description| | -------- | -------- | | LOS_SysClockGet | Obtains the system clock.| -| LOS_TickCountGet | Obtains the number of ticks since the system starts.| -| LOS_CyclePerTickGet | Obtains the number of cycles for each tick.| +| LOS_TickCountGet | Obtains the number of ticks since the system starts.| +| LOS_CyclePerTickGet | Obtains the number of cycles for each tick. | +| LOS_CurrNanosec | Obtains the current time, in nanoseconds. | + +**Table 3** API for time registration + +| API | Description | +| --------------------- | ---------------------------------------------- | +| LOS_TickTimerRegister | Re-registers the timer of the system clock and the corresponding interrupt handler.| +**Table 4** APIs for delay + +| API | Description | +| ---------- | ------------------------ | +| LOS_MDelay | Delays a task, in ms.| +| LOS_UDelay | Delays a task, in μs.| ## How to Develop @@ -52,13 +64,12 @@ The typical development process of time management is as follows: 2. Call the clock conversion and statistics APIs. ->![](../public_sys-resources/icon-note.gif) **NOTE** -> +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** > - The time management module depends on **OS_SYS_CLOCK** and **LOSCFG_BASE_CORE_TICK_PER_SECOND**. > > - The number of system ticks is not counted when the interrupt feature is disabled. Therefore, the number of ticks cannot be used as the accurate time. > -> - The configuration options are maintained in the **target_config.h** file of the development board project. +> - The preceding configuration is in the **target_config.h** file of the development board project. The default values of some configuration items are defined in the **los_config.h** file of the kernel. ## Development Example @@ -68,14 +79,14 @@ The typical development process of time management is as follows: The following example describes basic time management methods, including: -- Time conversion: convert milliseconds to ticks or convert ticks to milliseconds. +1. Time conversion: convert milliseconds to ticks or convert ticks to milliseconds. -- Time statistics: obtain the number of cycles per tick, number of ticks since system startup, and number of delayed ticks. +2. Time statistics: obtain the number of cycles per tick, number of ticks since system startup, and number of delayed ticks. ### Sample Code -Prerequisites +**Prerequisites** - The default value of **LOSCFG_BASE_CORE_TICK_PER_SECOND** is **100**. @@ -83,17 +94,22 @@ Prerequisites Time conversion: +The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleTransformTime** and **ExampleGetTime** in **TestTaskEntry**. + ``` -VOID Example_TransformTime(VOID) +VOID ExampleTransformTime(VOID) { UINT32 ms; UINT32 tick; - tick = LOS_MS2Tick(10000); // Convert 10000 ms into ticks. - dprintf("tick = %d \n", tick); - ms = LOS_Tick2MS(100); // Convert 100 ticks into ms. - dprintf("ms = %d \n", ms); + /* Convert 10000 ms to ticks. */ + tick = LOS_MS2Tick(10000); + printf("tick = %d \n", tick); + + /* Convert 100 ticks to ms. */ + ms = LOS_Tick2MS(100); + printf("ms = %d \n", ms); } ``` @@ -101,26 +117,21 @@ Time statistics and delay: ``` -VOID Example_GetTime(VOID) +VOID ExampleGetTime(VOID) { UINT32 cyclePerTick; - UINT64 tickCount; + UINT64 tickCountBefore; + UINT64 tickCountAfter; cyclePerTick = LOS_CyclePerTickGet(); - if(0 != cyclePerTick) { - dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick); - } - - tickCount = LOS_TickCountGet(); - if(0 != tickCount) { - dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount); + if (0 != cyclePerTick) { + printf("LOS_CyclePerTickGet = %d \n", cyclePerTick); } + tickCountBefore = LOS_TickCountGet(); LOS_TaskDelay(200); - tickCount = LOS_TickCountGet(); - if(0 != tickCount) { - dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount); - } + tickCountAfter = LOS_TickCountGet(); + printf("LOS_TickCountGet after delay rising = %d \n", (UINT32)(tickCountAfter - tickCountBefore)); } ``` @@ -140,8 +151,7 @@ ms = 1000 Time statistics and delay: -``` -LOS_CyclePerTickGet = 495000 -LOS_TickCountGet = 1 -LOS_TickCountGet after delay = 201 +``` +LOS_CyclePerTickGet = 250000 (The data may vary depending on the actual running environment.) +LOS_TickCountGet after delay rising = 200 ``` diff --git a/en/device-dev/kernel/kernel-mini-overview.md b/en/device-dev/kernel/kernel-mini-overview.md index 0a5388953620c2df5ecf1d8c567cec9b54cccc36..5ae0dae4f1225a817de12a3ae9670363661af8b4 100644 --- a/en/device-dev/kernel/kernel-mini-overview.md +++ b/en/device-dev/kernel/kernel-mini-overview.md @@ -24,92 +24,95 @@ The CPU architecture includes two layers: general architecture definition layer | Header file name| los_<function>.h | los_arch_<function>.h | | Function name| Halxxxx | Halxxxx | -LiteOS-M supports mainstream architectures, such as ARM Cortex-M3, ARM Cortex-M4, ARM Cortex-M7, ARM Cortex-M33, and RISC-V. +LiteOS-M supports mainstream architectures, such as RISC-V and ARM Cortex-M3, Cortex-M4, Cortex-M7, and Cortex-M33. ## Working Principles -In the **target\_config.h** file of the development board, configure the system clock and number of ticks per second, and configure the task, memory, inter-process communication (IPC), and exception handling modules based on service requirements. When the system boots, the modules are initialized based on the configuration. The kernel startup process includes peripheral initialization, system clock configuration, kernel initialization, and OS boot, as shown in the figure below. +In the **target_config.h** file of the development board, configure the system clock and number of ticks per second, and configure the task, memory, inter-process communication (IPC), and exception handling modules based on service requirements. When the system boots, the modules are initialized based on the configuration. The kernel startup process includes peripheral initialization, system clock configuration, kernel initialization, and OS boot, as shown in the figure below. + + **Figure 2** Kernel startup process - **Figure 2** Kernel startup process
![](figures/kernel-startup-process.png "kernel-startup-process") -## Directory Structure -The directory structure is as follows. +## Directory Structure + +The directory structure is as follows: ```text /kernel/liteos_m -├── arch # Code of the kernel instruction architecture layer -│ ├── arm # Code of the ARM32 architecture -│ │ ├── arm9 # Code of the ARM9 architecture -│ │ ├── cortex-m3 # Code of the cortex-m3 architecture -│ │ ├── cortex-m33 # Code of the cortex-m33 architecture -│ │ ├── cortex-m4 # Code of the cortex-m4 architecture -│ │ ├── cortex-m7 # Code of the cortex-m7 architecture -│ │ └── include # Arm architecture public header file directory -│ ├── csky # Code of the csky architecture -│ │ └── v2 # Code of the csky v2 architecture +├── arch # Kernel instruction architecture layer directory +│ └── arm # Code of the ARM architectures +│ │ ├── arm9 # Code of ARM9 +│ │ ├── cortex-m3 # Code of ARM Cortex-M3 +│ │ ├── cortex-m33 # Code of ARM Cortex-M33 +│ │ ├── cortex-m4 # Code of ARM Cortex-M4 +│ │ ├── cortex-m55 # Code of ARM Cortex-M55 +│ │ ├── cortex-m7 # code of ARM Cortex-M7 +│ │ └── include # Directory of the common header files of the ARM architectures +│ ├── csky # Code of the C-SKY architecture +│ │ └── v2 # code of C-SKY v2 │ ├── include # APIs exposed externally -│ ├── risc-v # Code of the risc-v architecture -│ │ ├── nuclei # Code of the nuclei system technology risc-v architecture -│ │ └── riscv32 # Code of the risc-v architecture -│ └── xtensa # Code of the xtensa architecture -│ └── lx6 # Code of the lx6 xtensa architecture +│ ├── risc-v # Code of the RISC-V architecture +│ │ ├── nuclei # Code of RISC-V for Nuclei +│ │ └── riscv32 # Code of the official RISC-V architecture +│ └── xtensa # Code of the Xtensa architecture +│ └── lx6 # Code of Xtensa LX6 ├── components # Optional components -│ ├── backtrace # Backtrace support +│ ├── backtrace # Backtrace │ ├── cppsupport # C++ support -│ ├── cpup # CPU percent (CPUP) +│ └── cpup # CPU percent (CPUP) │ ├── dynlink # Dynamic loading and linking │ ├── exchook # Exception hooks │ ├── fs # File systems -│ ├── lmk # Low memory killer functions -│ ├── lms # Lite memory sanitizer functions -│ ├── net # Networking functions -│ ├── power # Power management -│ ├── shell # Shell function -│ ├── fs # File systems +│ ├── lmk # Low memory killer mechanism +│ ├── lms # Lite memory sanitizer mechanism +│ └── net # Networking functions +│ ├── power # Power consumption management +│ ├── shell # Shell │ └── trace # Trace tool -├── drivers # driver Kconfig +├── drivers # Driver framework Kconfig ├── kal # Kernel abstraction layer │ ├── cmsis # CMSIS API support │ └── posix # POSIX API support ├── kernel # Minimum kernel function set │ ├── include # APIs exposed externally │ └── src # Source code of the minimum kernel function set -├── testsuites # Kernel testsuites +├── testsuites # Kernel test cases ├── tools # Kernel tools -├── utils # Common directory +├── utils # Common code ``` -## Constraints +## Constraints OpenHarmony LiteOS-M supports only C and C++. -Applicable architecture: See the directory structure for the arch layer. +OpenHarmony LiteOS-M applies only to the architectures in **arch**. -As for dynamic loading module, the shared library to be loaded needs signature verification or source restriction to ensure security. +For the shared library to be dynamically loaded, signature verification must be performed or the library source must be strictly controlled for security purposes. -## Usage +## Usage -The OpenHarmony LiteOS-M kernel build system is a modular build system based on Generate Ninja (GN) and Ninja. It supports module-based configuration, tailoring, and assembling, and helps you build custom products. This document describes how to build a LiteOS-M project based on GN and Ninja. For details about the methods such as GCC+gn, IAR, and Keil MDK, visit the community websites. +OpenHarmony +The OpenHarmony LiteOS-M kernel build system is a modular build system based on Generate Ninja (GN) and Ninja. It supports modular configuration, tailoring, and assembling, and helps you build custom products. This document describes how to build a LiteOS-M project based on GN and Ninja. For details about other building methods, such as GCC+gn, IAR, and Keil MDK, visit the related community websites. ### Setting Up the Environment -Before setting up the environment for a development board, you must set up the basic system environment for OpenHarmony first. The basic system environment includes the OpenHarmony build environment and development environment. For details, see [Quick Start Overview](../quick-start/quickstart-overview.md). +Before setting up the environment for a development board, you must set up the basic system environment for OpenHarmony first. The basic system environment refers to the OpenHarmony build environment and development environment. For details, see [Quick Start Overview](../quick-start/quickstart-overview.md). -### Obtaining the OpenHarmony Source Code +### Obtaining OpenHarmony Source Code -For details about how to obtain the source code, see [Source Code Acquisition](../get-code/sourcecode-acquire.md). This document assumes that the clone directory is `~/openHarmony` after the complete OpenHarmony repository code is obtained. +For details about how to obtain the source code, see [Obtaining Source Code](../get-code/sourcecode-acquire.md). The directory to clone is **~/openHarmony** after the OpenHarmony repository code is obtained. -### Example projects +### Supported Sample Projects -Qemu simulator: `arm_mps2_an386、esp32、riscv32_virt、SmartL_E802`. For details about how to compile and run, see [qemu guide](https://gitee.com/openharmony/device_qemu). +QEMU: **arm_mps2_an386**, **esp32**, **riscv32_virt**, and **SmartL_E802**. For details, see [QEMU](https://gitee.com/openharmony/device_qemu). -Bestechnic: `bes2600`. For details about how to compile and run, see [Bestechnic developer guide](https://gitee.com/openharmony/device_soc_bestechnic). +bestechnic: **bes2600**. For details, see [device_bestechnic](https://gitee.com/openharmony/device_soc_bestechnic). -### Community Porting Project Links +### Community Porting Project -The LiteOS-M kernel porting projects for specific development boards are provided by community developers. The following provides the links to these projects. If you have porting projects for more development boards, you can provide your links to share your projects. +The LiteOS-M kernel porting projects for specific development boards are provided by community developers. The following provides the links to these projects. You are also welcomed to share your porting projects. - Cortex-M3: @@ -123,22 +126,22 @@ The LiteOS-M kernel porting projects for specific development boards are provide This repository provides the project code for porting the OpenHarmony LiteOS-M kernel to support the STM32F429IGTb development board. The code supports build in Ninja, GCC, and IAR modes. -## Contribution +## Contribution -[How to involve](../../contribute/contribution-process.md) +[How To Contribute](../../contribute/how-to-contribute.md) -[Commit message spec](https://gitee.com/openharmony/kernel_liteos_m/wikis/Commit%20message%E8%A7%84%E8%8C%83) +[Commit Message Specifications]((https://gitee.com/openharmony/kernel_liteos_m/wikis/Commit%20message%E8%A7%84%E8%8C%83)) -[Liteos-M kernel coding style guide](https://gitee.com/openharmony/kernel_liteos_m/wikis/OpenHarmony%E8%BD%BB%E5%86%85%E6%A0%B8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83) +[LiteOS-M Kernel Coding Specifications](https://gitee.com/openharmony/kernel_liteos_m/wikis/OpenHarmony%E8%BD%BB%E5%86%85%E6%A0%B8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83) -How to contribute a chip based on Liteos-M kernel: +Contribute a chip based on LiteOS-M: -[Mini System SoC Porting Guide](../porting/porting-minichip-overview.md) +[Mini-System Chip Porting Guide](../porting/porting-minichip-overview.md) -[Mini System SoC Porting Cases](../porting/porting-bes2600w-on-minisystem-display-demo.md) +[Mini-System Devices with Screens – Bestechnic SoC Porting Case](../porting/porting-bes2600w-on-minisystem-display-demo.md) -## Repositories Involved +## Repositories Involved -[Kernel Subsystem](../../readme/kernel.md) +[Kernel](../../readme/kernel.md) -[kernel\_liteos\_m](https://gitee.com/openharmony/kernel_liteos_m/blob/master/README.md) +[kernel\_liteos\_m](https://gitee.com/openharmony/kernel_liteos_m) diff --git a/en/device-dev/kernel/kernel-overview.md b/en/device-dev/kernel/kernel-overview.md index ffcf8d171b3a05a88a08a0c2a8d9c70b86653aec..873d8b0a6c09eabbaaee70005e8eda10793045dd 100644 --- a/en/device-dev/kernel/kernel-overview.md +++ b/en/device-dev/kernel/kernel-overview.md @@ -108,7 +108,7 @@ To keep pace with the rapid development of the IoT industry, the OpenHarmony lig **Figure 4** LiteOS-A kernel architecture - ![](figures/architecture-of-the-openharmony-liteos-a-kernel.png "architecture-of-the-openharmony-liteos-a-kernel") + ![](figures/Liteos-a-architecture.png "Liteos-a-architecture.png") ### How to Use diff --git a/en/device-dev/kernel/kernel-small-plimits.md b/en/device-dev/kernel/kernel-small-plimits.md new file mode 100644 index 0000000000000000000000000000000000000000..cf4070f39090c4ebc395d37781c69e9663deb9dc --- /dev/null +++ b/en/device-dev/kernel/kernel-small-plimits.md @@ -0,0 +1,336 @@ +# plimits + +## Overview + +The complexity of app environments and growing number of processes will cause contention and waste of resources if restrictions are applied on containers. Process Limits (plimits) is a mechanism provided by the kernel to limit the resources used by a single process or multiple processes. It can implement refined control on resources such as CPUs and memory. + +plimitsfs is a file system that provides an interface for creating and deleting plimits. plimitsfs enables processes and process resources to be grouped for management through operations on files. Plimiters are configured to restrict the usage of resources, such as memory and sched, in process groups. + +## Basic Concepts + +- plimits: a feature provided by the kernel to limit, record, and isolate the resources used by a group of processes. +- plimitsfs: a file system, which provides an interface for users to create and delete plimits and displays the plimits directories. +- plimiter: a collection of resource limiters. plimiter includes the memory limiter, pids limiter, and sched limiter. +- sched limiter: limits the time to use CPUs for all processes in a plimits group in a specified period. +- memory limiter: limits the total memory that can be used by all processes in a plimits group. +- pids limiter: limits the maximum number of processes that can be mounted in a plimits group. + +## Working Principles + +During the system initialization process, the **plimits** directory is mounted to the **proc** directory. + +``` +├─proc +│ ├─plimits +│ │ ├─plimits.plimiter_add +│ │ ├─plimits.plimiter_delete +│ │ ├─plimits.procs +│ │ ├─plimits.limiters +│ │ ├─pids.max +│ │ ├─sched.period +│ │ ├─sched.quota +│ │ ├─sched.stat +│ │ ├─memory.failcnt +│ │ ├─memory.limit +│ │ ├─memory.peak +│ │ ├─memory.usage +│ │ ├─memory.oom_ctrl +│ │ └─memory.stat +``` + +- plimits groups + + **Figure 1** Creating or deleting plimits + + ![](figures/create_delete_plimits.png) + +- sched limiter + + **Figure 2** sched limiter configuration + + ![](figures/sched_limiter.png) + +- Memory limiter + + **Figure 3** Memory limiter configuration + + ![](figures/memory_limiter.png) + +- pids limiter + + **Figure 4** pids limiter configuration + + ![](figures/pids_limiter.png) + + +## How to Develop + + +### Available APIs + +The plimits root directory of LiteOS-A is in **/proc/plimits**. All files in this directory are read-only. The values setting in the limiter file are maximum values by default. You can view the status of process resources in the group from the files. +Run the **mkdir** command to create a **plimitsA** directory to group process resources and restrict resource allocation. The created **plimitsA** directory inherits its parent **plimits** directory. + +The following table lists the files in the **plimitsA** directory. + +| Permissions | Size| User| User Group| File | Description| +| --------- | ---- | ---- | ------ | ---------------------- | --------- | +|-r--r--r-- | 0 | u:0 | g:0 | sched.stat | Time slice of each thread in the last period. The time slice information is used for test and verification.| +|-rw-r--r-- | 0 | u:0 | g:0 | sched.quota | Sum of time slices of all processes in a group in a period, in ns.| +|-rw-r--r-- | 0 | u:0 | g:0 | sched.period | Statistical period of the time slice, in ns.| +|-r--r--r-- | 0 | u:0 | g:0 | memory.stat | Memory usage statistics, in bytes.| +|-r--r--r-- | 0 | u:0 | g:0 | memory.usage | Memory quota used, in bytes.| +|-r--r--r-- | 0 | u:0 | g:0 | memory.peak | Historical peak memory usage, in bytes.| +|-rw-r--r-- | 0 | u:0 | g:0 | memory.limit | Memory usage limit, in bytes.| +|-r--r--r-- | 0 | u:0 | g:0 | memory.failcnt | Number of memory allocation failures when the memory usage exceeds the limit.| +|-rw-r--r-- | 0 | u:0 | g:0 | pids.max | Maximum number of processes that can be mounted to a group.| +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.procs | All processes mounted to a group.| +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.limiter_delete | Used to delete a limiter. | +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.limiter_add | Used to add a limiter. | +|-r--r--r-- | 0 | u:0 | g:0 | plimits.limiters | Used to view the limiters of a group. | + +All the files in the **plimitsA** directory in **/proc/plimits/** are readable, and some are writable. You can allocate and restrict process resources by writing data to the files in the **plimitsA** directory. You can: +- Write time, in ns, to the **sched.quota** file to limit the time for all processes in the group to use CPUs. +- Write time, in ns, to the **sched.period** file to set the period for collecting statistics in a group. +- Write the memory, in bytes, to the **memory.limit** file to limit the memory that can be used by a group. +- Write a decimal number to the **pids.max** file to limit the number of processes that can be mounted to a group. +- Write process IDs (PIDs) to the **plimits.procs** file to mount processes to different plimits groups. +- Read files to view the resource usage configuration of a group. + +#### Deleting the **plimitsA** Group + +Write **sched**, **memory**, and **pids** to the **/proc/plimits/plimitsA/plimits.limiter_delete** file in sequence to delete the limiters, and then run the **rmdir** command to delete **plimitsA**. + +| Permissions | Size | User | User Group| File | +| --------- | ------- | ------ | ------ | ----------------------- | +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.procs | +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.limiter_delete | +|-rw-r--r-- | 0 | u:0 | g:0 | plimits.limiter_add | +|-r--r--r-- | 0 | u:0 | g:0 | plimits.limiters | + +### How to Develop + +1. Create **plimitsA** and write PIDs to **/plimitsA/plimits.procs** to group process resources. +2. Write the **/plimitsA/memory.limit** file to limit the maximum memory that can be used by the **plimitsA** group. +3. Write a decimal number to the **/plimitsA/pids.max** file to limit the number of processes that can be mounted to the **plimitsA** group. +4. Configure the limiter files in the **plimitsA** group to allocate and limit resources. If you do not want to limit the use of resources, delete **plimitsA**. + +### Development Example + +The following example demonstrates how to create the **plimitsA** group and implement resource control of this group by reading and writing the files of **plimitsA**. + +``` +#include +#include +#include +#include +#include +#include +#include + +#define LOS_OK 0 +#define LOS_NOK -1 + +int main () +{ + int ret; + ssize_t len; + int fd = -1; + //get main pid + int mainpid = getpid(); + char plimitsA[128] = "/proc/plimits/plimitsA"; + char plimitsAPids[128] = "/proc/plimits/plimitsA/pids.max"; + char plimitsAMemoryLimit[128] = "/proc/plimits/plimitsA/memory.limit"; + char plimitsAMemoryUsage[128] = "/proc/plimits/plimitsA/memory.usage"; + char plimitsAProcs[128] = "/proc/plimits/plimitsA/plimits.procs"; + char plimitsAAdd[128] = "/proc/plimits/plimitsA/plimits.limiter_add"; + char plimitsADelete[128] = "/proc/plimits/plimitsA/plimits.limiter_delete"; + char plimitsMem[128] = "/proc/plimits/memory.usage"; + char plimitsPid[128] = "/proc/plimits/plimits.procs"; + char *mem = NULL; + char writeBuf[128]; + char readBuf[128]; + + /* Check the processes in the plimits group. */ + memset(readBuf, 0, sizeof(readBuf)); + fd = open(plimitsPid, O_RDONLY); + len = read(fd, readBuf, sizeof(readBuf)); + if (len != strlen(readBuf)) { + printf("read file failed.\n"); + return LOS_NOK; + } + close(fd); + printf ("Processes in /proc/plimits: %s\n," readBuf); + + /* Check the memory usage of the plimits group. */ + memset(readBuf, 0, sizeof(readBuf)); + fd = open(plimitsMem, O_RDONLY); + len = read(fd, readBuf, sizeof(readBuf)); + if (len != strlen(readBuf)) { + printf("read file failed.\n"); + return LOS_NOK; + } + close(fd); + printf ("Memory used in /proc/plimits: %s\n," readBuf); + + + /* Create plimitsA "/proc/plimits/plimitsA". */ + ret = mkdir(plimitsA, 0777); + if (ret != LOS_OK) { + printf("mkdir failed.\n"); + return LOS_NOK; + } + + /* Set the number of processes that can be mounted to the plimitsA group. */ + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%d", 3); + fd = open(plimitsAPids, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + close(fd); + + /* Mount processes to the plimitsA group. */ + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%d", mainpid); + fd = open(plimitsAProcs, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + close(fd); + + /* Set the memory allocation limit in the plimitsA group. */ + memset(writeBuf, 0, sizeof(writeBuf)); + //limit memory + sprintf(writeBuf, "%d", (1024*1024*3)); + fd = open(plimitsAMemoryLimit, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + close(fd); + + /* Check the maximum memory that can be used in the plimitsA group. */ + memset(readBuf, 0, sizeof(readBuf)); + fd = open(plimitsAMemoryLimit, O_RDONLY); + len = read(fd, readBuf, sizeof(readBuf)); + if (len != strlen(readBuf)) { + printf("read file failed.\n"); + return LOS_NOK; + } + close(fd); + printf ("Maximum memory allowed for /proc/plimits/plimitsA: %s\n," readBuf); + + /* Check the processes mounted to the plimitsA group. */ + memset(readBuf, 0, sizeof(readBuf)); + fd = open(plimitsAProcs, O_RDONLY); + len = read(fd, readBuf, sizeof(readBuf)); + if (len != strlen(readBuf)) { + printf("read file failed.\n"); + return LOS_NOK; + } + close(fd); + printf ("Process mounted to /proc/plimits/plimitsA: %s\n," readBuf); + + /* Check the memory usage of the plimitsA group. */ + mem = (char*)malloc(1024*1024); + memset(mem, 0, 1024); + memset(readBuf, 0, sizeof(readBuf)); + fd = open(plimitsAMemoryUsage, O_RDONLY); + len = read(fd, readBuf, sizeof(readBuf)); + if (len != strlen(readBuf)) { + printf("read file failed.\n"); + return LOS_NOK; + } + close(fd); + printf ("Memory used by /proc/plimits/plimitsA: %s\n," readBuf); + + /* Delete the memory limiter for the plimitsA group. */ + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%s", "memory"); + fd = open(plimitsADelete, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + close(fd); + + /* Add a memory limiter to the plimitsA group. */ + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%s", "memory"); + fd = open(plimitsAAdd, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + close(fd); + + /* Delete the plimitsA group. You need to delete the memory, pids, and sched limiters first. */ + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%s", "memory"); + fd = open(plimitsADelete, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + if (len != strlen(writeBuf)) { + printf("write file failed.\n"); + return LOS_NOK; + } + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%s", "pids"); + fd = open(plimitsADelete, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + + memset(writeBuf, 0, sizeof(writeBuf)); + sprintf(writeBuf, "%s", "sched"); + fd = open(plimitsADelete, O_WRONLY); + len = write(fd, writeBuf, strlen(writeBuf)); + close(fd); + ret = rmdir(plimitsA); + if (ret != LOS_OK) { + printf("rmdir failed.\n"); + return LOS_NOK; + } + + return 0; +} +``` + + +### Verification + +The development is successful if the return result is as follows: + + +``` +Processes in the /proc/plimits group: +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 + +Memory used in /proc/plimits: 28016640 + +Maximum memory allowed for /proc/plimits/plimitsA: 3145728 + +Process mounted to /proc/plimits/plimitsA: +15 + +Memory used by /proc/plimits/plimitsA: 4096 +``` diff --git a/en/device-dev/kernel/kernel-standard-build.md b/en/device-dev/kernel/kernel-standard-build.md index 3c950570cf2ae2638fd00a68756c3cefaaf3ddce..03abfc7b80146f9710c2711dbf650419b752e6ea 100644 --- a/en/device-dev/kernel/kernel-standard-build.md +++ b/en/device-dev/kernel/kernel-standard-build.md @@ -9,8 +9,8 @@ The following uses the Hi3516D V300 board and Ubuntu x86 server as an example. Perform a full build for the project to generate the **uImage** kernel image. -``` -./build.sh --product-name hispark_taurus_standard # Build the hispark_taurus_standard image. - --build-target build_kernel # Build the uImage kernel image of hispark_taurus_standard. - --gn-args linux_kernel_version=\"linux-5.10\" # Specify the kernel version. +```bash +./build.sh --product-name hispark_taurus_standard # Build the hispark_taurus_standard image. + --build-target build_kernel # Build the uImage kernel image of hispark_taurus_standard. + --gn-args linux_kernel_version=\"linux-5.10\" # Specify the kernel version. ``` diff --git a/en/device-dev/kernel/kernel-standard-mm-eswap.md b/en/device-dev/kernel/kernel-standard-mm-eswap.md index e44534ed70119aea30ec3b406f775d39732caaa0..86417cb169bac8ae5b84e7b9bc64df7313c9f51d 100644 --- a/en/device-dev/kernel/kernel-standard-mm-eswap.md +++ b/en/device-dev/kernel/kernel-standard-mm-eswap.md @@ -1,4 +1,4 @@ -# Enhanced Swap +# ESwap ## Basic Concepts @@ -8,30 +8,33 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and ## Configuring zram and ESwap +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
+> Enable ESwap before zram is enabled. If ESwap is not used, you can enable zram alone. If a device does not have the storage device for swap-out or have the corresponding storage partition created, you can enable zram to reclaim memory using **zswapd**. + ### Enabling ESwap 1. Enable related configuration items and dependencies. To enable ESwap, you must enable the corresponding configuration items and dependencies during kernel compilation. The configuration items related to ESwap are as follows: ``` - CONFIG_HYPERHOLD=y - CONFIG_HYPERHOLD_DEBUG=y - CONFIG_HYPERHOLD_ZSWAPD=y - CONFIG_HYPERHOLD_FILE_LRU=y - CONFIG_HYPERHOLD_MEMCG=y - CONFIG_ZRAM_GROUP=y - CONFIG_ZRAM_GROUP_DEBUG=y - CONFIG_ZLIST_DEBUG=y - CONFIG_ZRAM_GROUP_WRITEBACK=y + CONFIG_HYPERHOLD=y // Enable Hyperhold + CONFIG_HYPERHOLD_DEBUG=y // Enable Hyperhold debug + CONFIG_HYPERHOLD_ZSWAPD=y // Enable the zswapd thread to reclaim Anon pages in background + CONFIG_HYPERHOLD_FILE_LRU=y // Enable Hyperhold FILE LRU + CONFIG_HYPERHOLD_MEMCG=y // Enable Memcg management in Hyperhold + CONFIG_ZRAM_GROUP=y // Enable Manage Zram objs with mem_cgroup + CONFIG_ZRAM_GROUP_DEBUG=y // Enable Manage Zram objs with mem_cgroup Debug + CONFIG_ZLIST_DEBUG=y // Enable Debug info for zram group list + CONFIG_ZRAM_GROUP_WRITEBACK=y // Enable write back grouped zram objs to Hyperhold driver ``` Enable the following dependencies: ``` - CONFIG_MEMCG=y - CONFIG_SWAP=y - CONFIG_ZSMALLOC=y - CONFIG_ZRAM=y + CONFIG_MEMCG=y // Enable memory controller + CONFIG_SWAP=y // Enable paging of anonymous memory (swap) + CONFIG_ZSMALLOC=y // Enable memory allocator for compressed pages + CONFIG_ZRAM=y // Enable compressed RAM block device support ``` 2. Create an ESwap device. @@ -56,11 +59,11 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and By default, ESwap encrypts the data swapped out. If the ESwap device created in step 2 supports inline encryption, you can disable the ESwap software encryption function. ```Bash - // Check whether hardware-based encryption is supported and enabled. If yes, disable software encryption. Otherwise, do not perform this operation. + // Check whether hardware-based encryption is supported and enabled. If yes, disable software encryption. Otherwise, do not disable software encryption. echo 0 > /proc/sys/kernel/hyperhold/soft_crypt ``` - > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+ > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **CAUTION**
> For security purposes, all swapped content must be encrypted. If the ESwap device created does not support inline encryption or the inline encryption macro is not enabled during compilation, ESwap cannot be enabled after software encryption is disabled. 4. Enable ESwap. @@ -72,9 +75,6 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and ``` -> ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
-> Enable ESwap before zram is enabled. If ESwap is not used, you can enable zram only. If a device does not have the storage device for swap-out or have the corresponding storage partition created, you can enable zram to reclaim memory using **zswapd**. - ### Enabling zram 1. Initialize zram. @@ -88,7 +88,7 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and echo 512M > /sys/block/zram0/disksize ``` - > ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> The parameters and functions of **/sys/block/zram0/group** are as follows: > > - **disable**: disables the function. @@ -115,7 +115,7 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and echo force_disable > /proc/sys/kernel/hyperhold/enable ``` - > ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
+ > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> The difference of the two commands is as follows: > > - **disable**: If there is no data in the ESwap partition, disable ESwap. Otherwise, changes ESwap to **readonly** mode. @@ -132,42 +132,46 @@ Enhanced Swap (ESwap) allows a custom partition to serve as a swap partition and ## ESwap APIs -ESwap provides APIs to control swap-in and swap-out policies and record the current status. These APIs are located in the directory to which memcg is mounted, for example, `/dev/memcg/`. +ESwap provides APIs to control swap-in and swap-out policies and record the current status. These APIs are located in the directory to which memcg is mounted, for example, **/dev/memcg/**. -| Category| API| Description| -| -------- | -------- | -------- | -| Control| [avail_buffers](#avail_buffers) | Sets the buffer range.| -| | [zswapd_single_memcg_param](#zswapd_single_memcg_param) | Sets memcg configuration.| -| | [zram_wm_ratio](#zram_wm_ratio) | Sets the zram swap-out waterline.| -| Status| [zswapd_pressure_show](#zswapd_pressure_show) | Records the current buffer and refault.| -| | [stat](#stat) | Checks the real-time status of ESwap.| -| | [zswapd_vmstat_show](#zswapd_vmstat_show) | Records events during the zswapd running.| +| Category| API| Description| Reference Value| +| -------- | -------- | -------- | -------- | +| Control| [avail_buffers](#avail_buffers) | Sets the buffer range.| 300 250 350 200 | +| | [zswapd_single_memcg_param](#zswapd_single_memcg_param) | Sets memcg configuration.| 300 40 0 0 | +| | [zram_wm_ratio](#zram_wm_ratio) | Sets the zram swap-out waterline.| 0 | +| Status| [zswapd_pressure_show](#zswapd_pressure_show) | Records the current buffer and refault.| NA | +| | [stat](#stat) | Checks the real-time status of ESwap.| NA | +| | [zswapd_vmstat_show](#zswapd_vmstat_show) | Records events during the zswapd running.| NA | -> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **CAUTION**
> Only **avail_buffers** proactively wakes up zswapd because the buffer waterline is adjusted. Other control APIs do not proactively wake up zswapd, but their configuration takes effect only after zswapd is woken up. -The APIs are described as follows: ### avail_buffers The **avail_buffers** API sets the buffer range [min_avail_buffers, high_avail_buffers]. When the current buffer is less than the value of **min_avail_buffers**, zswapd will be woken up to reclaim anonymous pages. The expected amount of memory to reclaim is the difference between the value of **high_avail_buffers** and the current system buffer value. In fact, less memory is reclaimed due to reasons such as reclamation failure. + The parameters include the following: -- **avail_buffers** indicates the expected buffer value. + +- **avail_buffers** indicates the expected buffer value. - **free_swap_threshold** indicates the threshold of the free capacity of the swap partition. After zswapd is woken up to reclaim memory, press events, such as medium press and critical press, will be recorded based on the current system status and the settings of these two parameters. -You can proactively adjust the values to trigger zswapd reclamation. -Example: + +You can proactively adjust the values to trigger zswapd reclamation. + +**Example**: + `echo 1000 950 1050 0 > /dev/memcg/memory.avail_buffers` -Default value: +**Default value**: ``` - avail_buffers: 0 - min_avail_buffers: 0 - high_avail_buffers: 0 - free_swap_threshold: 0 +avail_buffers: 0 +min_avail_buffers: 0 +high_avail_buffers: 0 +free_swap_threshold: 0 ``` -Limit: +**Limit**: 0<=min_avail_buffers<=avail_buffers<=high_avail_buffers @@ -177,25 +181,29 @@ The values are all integers. ### zswapd_single_memcg_param -The **zswapd_single_memcg_param** API sets the memcg configuration. The parameters include the following: +**zswapd_single_memcg_param** sets the memcg configuration. The parameters include the following: + - **score** indicates the current memcg reclamation priority. - **ub_mem2zram_ratio** indicates the memory compression ratio to zram. - **ub_zram2ufs_ratio** indicates the ratio of zram to ESwap. - **refault_threshold** indicates the refault threshold. + You can modify the parameters to control zram compression and ESwap. -Example: + +**Example**: + `echo 60 10 50 > memory.zswapd_single_memcg_param` -Default value: +**Default value**: ``` - memcg score: 300 - memcg ub_mem2zram_ratio: 60 - memcg ub_zram2ufs_ratio: 10 - memcg refault_threshold: 50 +memcg score: 300 +memcg ub_mem2zram_ratio: 60 +memcg ub_zram2ufs_ratio: 10 +memcg refault_threshold: 50 ``` -Limit: +**Limit**: 0<=ub_mem2zram_ratio<=100 @@ -207,17 +215,21 @@ The values are all integers. ### zram_wm_ratio -The **zram_wm_ratio** API sets the zram swap-out waterline. When the size of the compressed anonymous page in the zram partition is greater than the total size of zram multiplied by **zram_wm_ratio**, the page is swapped out to the ESwap partition. The swap is performed after zswapd is woken up by the buffer waterline. The system defaults the value **0** as **37**. You can change the value as required. -Example: +**zram_wm_ratio** sets the zram swap-out waterline. When the size of the compressed anonymous page in the zram partition is greater than the total size of zram multiplied by **zram_wm_ratio**, the page is swapped out to the ESwap partition. The swap is performed after zswapd is woken up by the buffer waterline. The system defaults the value **0** as **37**. + +You can change the value as required. + +**Example**: + `echo 30 > /dev/memcg/memory.zram_wm_ratio` -Default value: +**Default value**: ``` - zram_wm_ratio: 0 +zram_wm_ratio: 0 ``` -Limit: +**Limit**: 0<=zram_wm_ratio<=100 @@ -225,7 +237,7 @@ The value is an integer. ### zswapd_pressure_show -The **zswapd_pressure_show** API records the zswapd status. **buffer_size** indicates the current buffer size of the system, and **recent_refault** indicates the number of refaults occurred. +**zswapd_pressure_show** records the zswapd status. **buffer_size** indicates the current buffer size of the system, and **recent_refault** indicates the number of refaults occurred. ### stat @@ -235,12 +247,12 @@ In addition to **memcg.stat**, the **stat** API is added with **Anon**, **File** ### zswapd_vmstat_show -The **zswapd_vmstat_show** API records events occurred during the zswapd running. +**zswapd_vmstat_show** records events occurred during the zswapd running. ## Triggering zswapd -You can check the current buffer value by running `cat /dev/memcg/memory.zswapd_pressure_show`. For example, if the current buffer value is 1200, you can adjust the buffer range to wake up zswapd. +You can run **cat /dev/memcg/memory.zswapd_pressure_show** to check the current buffer value. For example, if the current buffer value is 1200, you can adjust the buffer range to a value greater than 1200 to wake up zswapd. ```Bash echo 1300 1250 1350 0 > /dev/memcg/memory.avail_buffers diff --git a/en/device-dev/kernel/kernel-standard-newip.md b/en/device-dev/kernel/kernel-standard-newip.md index 6ad095d8e80a657d69fea1cfdaf4ebb69841a7e4..a5681a40970b2729c488eeec59067678185de798 100644 --- a/en/device-dev/kernel/kernel-standard-newip.md +++ b/en/device-dev/kernel/kernel-standard-newip.md @@ -5,7 +5,7 @@ On basis of the traditional IP, New IP employs lightweight packet headers and variable-length, multi-semantic addresses and integrates Layer 2 and Layer 3 protocols to simplify protocols, reduce redundant bytes, and improve the energy efficiency ratio (EER), net throughput, and communication efficiency. New IP strives to implement end-to-end interconnection between heterogeneous networks to support ultimate experience of Super Device via efficient communication between devices. -The WiFi protocol packets cause low transmission efficiency due to high overheads in Layer 3 packet headers and addressing. +The Wi-Fi protocol packets cause low transmission efficiency due to high overheads in Layer 3 packet headers and addressing. ![](figures/newip-wifi-packet.png) @@ -18,54 +18,54 @@ New IP supports variable-length multi-semantic addresses (min. 1 byte) and custo New IP provides 25.9% less packet header overheads than IPv4 and 44.9% less than IPv6. -New IP provides at least 1% higher payload transmission efficiency than IPv4 and 2.33% than IPv6. +New IP provides at least 1% higher payload transmission efficiency than IPv4 and 2.33% higher than IPv6. -| Scenario | Header Overhead (Bytes) | Payload Transmission Efficiency
(WiFi MTU = 1500 Bytes, BT MTU = 255 Bytes)| -| -------------- | ------------ | ------------------------------------------- | -| IPv4 for WiFi | 30 + 8 + 20 = 58 | (1500 - 58)/1500 = 96.13% | -| IPv6 for WiFi | 30 + 8 + 40 = 78 | (1500 - 78)/1500 = 94.8% | -| NewIP for WiFi | 30 + 8 + 5 = 43 | (1500 - 43)/1500 = 97.13% | +| Scenario | Header Overhead (Bytes) | Payload Transmission Efficiency
(Wi-Fi MTU = 1500 Bytes, BT MTU = 255 Bytes)| +| --------------- | ------------ | ------------------------------------------- | +| IPv4 for Wi-Fi | 30 + 8 + 20 = 58 | (1500 - 58)/1500 = 96.13% | +| IPv6 for Wi-Fi | 30 + 8 + 40 = 78 | (1500 - 78)/1500 = 94.8% | +| New IP for Wi-Fi | 30 + 8 + 5 = 43 | (1500 - 43)/1500 = 97.13% | ## Variable-Length Header Format -The figure below shows a New IP WiFi packet header. "EtherType = 0xEADD" in the LLC header identifies the packet. **Bitmap** is a binary sequence. The value of each binary bit indicates the existence of a feature. +The following figure shows a New IP Wi-Fi packet header. "EtherType = 0xEADD" in the LLC header identifies the New IP packet. A bitmap is a binary sequence. The value of each binary bit indicates whether a field is carried in the New IP header. That is, the New IP header can be customized based on service requirements. ![](figures/newip-header.png) - **Dispatch** indicates the encapsulation type. The value **0b0** indicates the New IP encapsulation child class, which is 1 bit long (**0b** indicates that the following values are binary). -- **Bitmap** is of variable length. By default, it is seven bits following the **Dispatch** valid bit. The length of **Bitmap** can be extended contiguously. The last bit **0** of **Bitmap** indicates the end of **Bitmap**. The last bit **1** means to extend the **Bitmap** one byte until the last bit **0**. +- **Bitmap** is of variable length. By default, it is seven bits following the **Dispatch** valid bit. The length of **Bitmap** can be extended contiguously. If the last bit of **Bitmap** is **0**, it indicates the end of **Bitmap**. If the last bit is **1**, it means one more byte until the last bit **0**. - **Value** indicates the field value. The length is an integer multiple of 1 byte. The value type and length are determined by the semantic table of the header field. The **Bitmap** field is defined as follows: -| Bitmap Identifier | Bitops | Field Length (Byte) | Setting | Remarks | -| -------------------------- | ------ | ---------------- | -------------- | --------------------------------------- | -| Bitmap 1st Byte: | - | - | - | - | -| Dispatch flag | 0 | It does not indicate a specific field. | Set to **0**. | The value **0** indicates a New IP packet; **1** indicates a non-New-IP packet. | -| TTL | 1 | 1 | Set to **1**. | Indicates the number of remaining hops. | -| Total Length | 2 | 2 | Set to **0** for UDP and **1** for TCP.| Total length of a New IP packet (including the header length). | -| Next Header | 3 | 1 | Set to **1**. | Protocol type. | -| Reserve | 4 | Reserved | Set to **0**. | Reserved. | -| Dest Address | 5 | Variable length (1 to 8 bytes)| Set to **1**. | Destination address. | -| Source Address | 6 | Variable length (1 to 8 bytes)| Determined by the protocol.| Source address. | -| Flag bit, indicating whether there is the second byte| 7 | It does not indicate a specific field. | - | The value **0** indicates the end of this bitmap. The value **1** indicates another 8-bit bitmap.| -| Bitmap 2nd Byte: | - | - | - | - | -| Header Length | 0 | 1 | - | Length of the New IP packet header. | -| Reserve | 1 | Reserved | Set to **0**. | - | -| Reserve | 2 | Reserved | Set to **0**. | - | -| Reserve | 3 | Reserved | Set to **0**. | - | -| Reserve | 4 | Reserved | Set to **0**. | - | -| Reserve | 5 | Reserved | Set to **0**. | - | -| Reserve | 6 | Reserved | Set to **0**. | - | -| Flag bit, indicating whether there is the third byte| 7 | It does not indicate a specific field. | Set to **0**. | The value **0** indicates the end of this bitmap. The value **1** indicates another 8-bit bitmap.| - -The bitmap fields in New IP are processed as follows: +| Bitmap Identifier | Bitops | Length of the Field Carried (Byte) | Setting | Remarks | +| ---------------------------- | ------ | ---------------- | -------------- | --------------------------------------- | +| Bitmap first byte | - | - | - | The eight bits are from the most significant bit to the least significant bit. | +| Dispatch | 0 | - | Set to **0**. | **0**: indicates a New IP packet; **1**: indicates a non-New-IP packet. | +| Whether the packet header carries the TTL | 1 | 1 | Set to **1**. | Indicates the number of remaining hops. | +| Whether the packet header carries the total length | 2 | 2 | Set to **0** for UDP and **1** for TCP.| Total length of the new IP packet (including the header) | +| Whether the packet header carries the Next Header | 3 | 1 | Set to **1**. | Protocol type. | +| Reserve | 4 | Reserved | Set to **0**. | Reserved. | +| Whether the packet header carries the destination address | 5 | Variable length (1 to 8 bytes)| Set to **1**. | Destination address. | +| Whether the packet header carries the source address| 6 | Variable length (1 to 8 bytes)| Determined by the protocol.| Source address. | +| Flag bit, indicating whether there is the second byte | 7 | - | - | **0**: indicates the end of the bitmap. **1**: indicates another 8-bit bitmap.| +| Bitmap second byte | - | - | - | The eight bits are from the most significant bit to the least significant bit. | +| Whether the packet header carries the header Length | 0 | 1 | - | New IP header length. | +| Reserve | 1 | Reserved | Set to **0**. | - | +| Reserve | 2 | Reserved | Set to **0**. | - | +| Reserve | 3 | Reserved | Set to **0**. | - | +| Reserve | 4 | Reserved | Set to **0**. | - | +| Reserve | 5 | Reserved | Set to **0**. | - | +| Reserve | 6 | Reserved | Set to **0**. | - | +| Flag bit, indicating whether there is the third byte | 7 | - | - | **0**: indicates the end of the bitmap. **1**: indicates another 8-bit bitmap.| + +The New IP header is parsed as follows: Only the bitmap fields defined in New IP are parsed. All the bitmap fields with unknown semantics are skipped. The start position of the packet is located for parsing based on the header length. If the packet header contains bitmap fields with unknown semantics and does not contain the header length, the packet will be discarded. ## Variable-Length Address Format -New IP uses variable-length addresses. The address itself indicates the length of the address. The address encoding format is as follows: +Different from IPv4 and IPv6, which use fixed-length addresses, New IP supports variable-length addresses and parse of the address length. The packet header may not carry the address length field. The encoding format of new IP addresses is as follows: | First Byte | Semantics | Valid Range of Address | | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | @@ -88,30 +88,40 @@ New IP uses variable-length addresses. The address itself indicates the length o ## New IP Configuration -Configure related settings and dependencies during kernel compilation to enable New IP. The New IP configuration is as follows: +### Enabling New IP -```c +Only the Linux 5.10 kernel of the RK3568 development board supports the New IP kernel protocol stack. To enable New IP, search for "NEWIP" in the kernel module configuration file of the RK3568 development board and set related parameters as follows: + +``` +# kernel/linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig CONFIG_NEWIP=y // Enable the New IP kernel protocol stack. -CONFIG_NEWIP_HOOKS=y // Enable New IP processing with hooks. +CONFIG_NEWIP_HOOKS=y // Enable New IP stub functions to be dynamically registered non-disruptively. This feature must be enabled when New IP is enabled. +VENDOR_HOOKS=y // Enable the basic kernel instrumentation framework. New IP depends on this setting. It is enabled by default for the RK3568 development board. ``` -Set the following dependency: +Run the following command to check whether the New IP protocol stack is successfully enabled: -```c -VENDOR_HOOKS=y // Enable the kernel function hooking framework. +``` +find out/ -name *nip*.o +... +out/kernel/OBJ/linux-5.10/net/newip/nip_addrconf_core.o +out/kernel/OBJ/linux-5.10/net/newip/nip_hdr_decap.o +out/kernel/OBJ/linux-5.10/net/newip/nip_addr.o +out/kernel/OBJ/linux-5.10/net/newip/nip_checksum.o +out/kernel/OBJ/linux-5.10/net/newip/tcp_nip_output.o +... ``` -**NOTE** - -- Only the Linux 5.10 kernel supports the New IP kernel protocol stack. -- All native kernel code must be intrusively modified by hooks. +> **NOTE** +> +> All native kernel code must be non-disruptively modified into stub functions. For example, when New IP is added to the common process of IPv4/IPv6 stacks, you need to break into New IP stubs instead of calling New IP functions directly. After New IP is enabled, register the New IP functions with the function pointers during module initialization. Then, the New IP functions can be called in the common IPv4/IPv6 process through the function pointers. ```c /* Register the New IP ehash function with the kernel. */ register_trace_ninet_ehashfn_hook(&ninet_ehashfn_hook, NULL); -/* The following is the general entry function of IPv4 and IPv6 protocol stacks. Add the processing related to the New IP protocol stack to the general entry function. */ +/* Add the New IP stack processing to the general entry function of IPv4/IPv6 stacks. */ static u32 sk_ehashfn(const struct sock *sk) { /* IPv6 */ @@ -127,7 +137,7 @@ static u32 sk_ehashfn(const struct sock *sk) if (sk->sk_family == AF_NINET) { u32 ret = 0; - /* ehash function registered by New IP */ + /* Register the New IP ehash function. */ trace_ninet_ehashfn_hook(sock_net(sk), &sk->sk_nip_rcv_saddr, sk->sk_num, &sk->sk_nip_daddr, sk->sk_dport, &ret); return ret; @@ -141,47 +151,35 @@ static u32 sk_ehashfn(const struct sock *sk) } ``` +### Disabling New IP +To disable New IP, search for "NEWIP" in the kernel module configuration file of the RK3568 development board and delete or comment out "CONFIG_NEWIP=y" and "CONFIG_NEWIP_HOOKS=y". -Run the following command to check whether the New IP protocol stack code is successfully enabled: - -```c -find out/ -name *nip*.o -out/rk3568/obj/third_party/glib/glib/glib_source/guniprop.o -out/kernel/OBJ/linux-5.10/net/newip/nip_addrconf_core.o -out/kernel/OBJ/linux-5.10/net/newip/nip_hdr_decap.o -out/kernel/OBJ/linux-5.10/net/newip/nip_addr.o -out/kernel/OBJ/linux-5.10/net/newip/nip_checksum.o -out/kernel/OBJ/linux-5.10/net/newip/tcp_nip_output.o -... ``` - -Disable the New IP kernel protocol stack, delete **CONFIG_NEWIP** and the **out/kernel** directory, and start the build again. - -```c +# kernel/linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig # CONFIG_NEWIP is not set # CONFIG_NEWIP_HOOKS is not set ``` ## New IP APIs -The user-mode application calls the socket API to create a New IP socket and uses the New IP frame header encapsulation to receive and transmit packets. The following table lists the New IP socket functions. +The user-mode application calls **socket()** to create a New IP socket and uses the New IP frame header encapsulation to send and receive packets. The following table lists the socket APIs for New IP. -| Function | Input | Output | Return Value | Function Description | +| API | Input | Output | Return Value | Description | | -------- | ------------------------------------------------------------ | ---------------------------------------------- | ---------------- | ------------------------------------------------------------ | -| socket | int **domain**, int type, int **protocol** | NA | Socket handle **sockfd**.| Creates a **socket** instance for New IP.
**domain** must be **AF_NINET**, which indicates a New IP socket.
**protocol** can be **IPPROTO_TCP** or **IPPROTO_UDP**.
This function returns the handle of the **socket** instance created.| -| bind | int sockfd, const **struct sockaddr_nin** *myaddr, socklen_t addrlen | NA | Error code. The value is an integer. | Binds the **socket** instance to the specified IP address and port.
**myaddr->sin_family** must be **AF_NINET**.| -| listen | int socket, int backlog | NA | Error code. The value is an integer. | Listens for the New IP address and port from the server. | -| connect | int sockfd, const **struct sockaddr_nin** *addr, aocklen_t addrlen | NA | Error code. The value is an integer. | Sets up a connection between the client and the server. | +| socket | int **domain**, int type, int **protocol** | NA | Socket handle **sockfd**.| Creates a New IP socket.
**domain** must be **AF_NINET**, which indicates a New IP socket.
**protocol** can be **IPPROTO_TCP** or **IPPROTO_UDP**.
This API returns the handle of the **socket** instance created.| +| bind | int sockfd, const **struct sockaddr_nin** *myaddr, socklen_t addrlen | NA | Error code, which is an integer. | Binds the **socket** instance to the specified IP address and port.
**myaddr->sin_family** must be **AF_NINET**.| +| listen | int socket, int backlog | NA | Error code, which is an integer. | Listens for the New IP address and port from the server. | +| connect | int sockfd, const **struct sockaddr_nin** *addr, aocklen_t addrlen | NA | Error code, which is an integer. | Sets up a connection between the client and the server. | | accept | int sockfd, **struct sockaddr_nin** *address, socklen_t *address_len | NA | **sockfd**. | Accepts the connection request from the client. | -| send | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr_nin** *dst_addr, int addrlen | NA | Error code. The value is an integer. | Sends New IP TCP packets from the socket. | -| recv | int sockfd, size_t len, int flags, **struct sockaddr_nin** *src_addr, | void **buf, int* *fromlen | Error code. The value is an integer. | Receives New IP TCP packets from the socket. | -| close | int sockfd | NA | Error code. The value is an integer. | Closes the socket to release resources. | -| ioctl | int sockfd, unsigned long cmd, ... | NA | Error code. The value is an integer. | Queries or modifies the information about the New IP protocol stack. | -| sendto | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr** *dst_addr, int addrlen | NA | Error code. The value is an integer. | Sends New IP UDP packets from the socket. | -| recvfrom | int sockfd, size_t len, int flags, | void *buf, struct sockaddr *from, int *fromlen | Error code. The value is an integer. | Receives New IP UDP packets from the socket. | +| send | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr_nin** *dst_addr, int addrlen | NA | Error code, which is an integer. | Sends New IP packets via the socket using TCP. | +| recv | int sockfd, size_t len, int flags, **struct sockaddr_nin** *src_addr, | void **buf, int* *fromlen | Error code, which is an integer. | Receives New IP packets via the socket using TCP. | +| close | int sockfd | NA | Error code, which is an integer. | Closes the socket to release resources. | +| ioctl | int sockfd, unsigned long cmd, ... | NA | Error code, which is an integer. | Queries or modifies information about the New IP protocol stack. | +| sendto | int sockfd, const void *msg, int len, unsigned int flags, const **struct sockaddr** *dst_addr, int addrlen | NA | Error code, which is an integer. | Sends New IP packets via the socket using UDP. | +| recvfrom | int sockfd, size_t len, int flags, | void *buf, struct sockaddr *from, int *fromlen | Error code, which is an integer. | Receives New IP packets via the socket using UDP. | -Structure of New IP short addresses: +The structure of the New IP short address is as follows: ```c enum nip_8bit_addr_index { @@ -218,13 +216,13 @@ enum nip_32bit_addr_index { struct nip_addr_field { union { unsigned char field8[NIP_8BIT_ADDR_INDEX_MAX]; - unsigned short field16[NIP_16BIT_ADDR_INDEX_MAX]; /* big-endian */ - unsigned int field32[NIP_32BIT_ADDR_INDEX_MAX]; /* big-endian */ + unsigned short field16[NIP_16BIT_ADDR_INDEX_MAX]; /* Big-endian */ + unsigned int field32[NIP_32BIT_ADDR_INDEX_MAX]; /* Big-endian */ } u; }; struct nip_addr { - unsigned char bitlen; /* The address length is in bit (not byte) */ + unsigned char bitlen; /* The address length is in bit (not byte). */ struct nip_addr_field v; }; #pragma pack() @@ -235,27 +233,27 @@ struct nip_addr { */ #define POD_SOCKADDR_SIZE 3 struct sockaddr_nin { - unsigned short sin_family; /* [2Byte] AF_NINET */ - unsigned short sin_port; /* [2Byte] Transport layer port, big-endian */ - struct nip_addr sin_addr; /* [9Byte] NIP address */ + unsigned short sin_family; /* [2 bytes] AF_NINET */ + unsigned short sin_port; /* [2 bytes] Transport layer port, big-endian */ + struct nip_addr sin_addr; /* [9 bytes] New IP address */ - unsigned char sin_zero[POD_SOCKADDR_SIZE]; /* [3Byte] Byte alignment */ + unsigned char sin_zero[POD_SOCKADDR_SIZE]; /* [3 bytes] Byte alignment */ }; ``` ## New IP Development -Only the OpenHarmony Linux-5.10 kernel supports New IP kernel protocol stack. You must manually configure IP address and route data for New IP in user mode, and connect the two devices through the router WiFi. To implement automatic switch to the New IP kernel protocol stack for communication after the IP address and route are is configured, see the description in the blue block in the figure below. +Only the OpenHarmony Linux-5.10 kernel supports New IP kernel protocol stack. You must manually configure IP address and route data for New IP in user mode, and connect the two devices through the router Wi-Fi. If you want to automatically switch to the new IP kernel protocol stack after configuring the new IP address and route, see the description in the blue box in the following figure. ![](figures/newip-development.png) -For details about the address and route configuration, see [examples](https://gitee.com/openharmony/communication_sfc_newip/tree/master/examples). Modify the CC definition in Makefile based on the CPU you use, compile the CC definition into a binary file, and push the file to the development board. Refer to the figure above to configure the address and route data for New IP. +For details about the address and route configuration, see [examples](https://gitee.com/openharmony/kernel_common_modules_newip/tree/master/examples). Modify the CC definition in Makefile based on the CPU you use, compile the CC definition into a binary file, and push the file to the development board. Refer to the figure above to configure the address and route data for New IP. -| File | Description | -| ------------------ | ------------------------------------------------------ | -| nip_addr.c | Demo code for configuring the variable-length New IP addresses (any valid New IP address can be configured)| -| nip_route.c | Demo code for configuring the New IP route data (any valid New IP route can be configured) | -| check_nip_enable.c | Demo code for obtaining the New IP capabilities of the local device. | +| File | Description | +| ------------------ | -------------------------------------------------------- | +| nip_addr.c | Sample code for configuring variable-length New IP addresses (any valid New IP address can be configured). | +| nip_route.c | Sample code for configuring New IP route information (any valid New IP address can be configured). | +| check_nip_enable.c | Code for obtaining the New IP capabilities of the local host. | Check the New IP address and route information on device 1. @@ -277,19 +275,19 @@ Check the New IP address and route information on device 2. 02 02 2149580801 wlan0 # Route for sending packets to itself and receiving the packets. ``` -## New IP Sample Code for Receiving and Sending Packets +## Sample Code for Receiving and Sending New IP Packets -The table below lists the demo code files. For details about how to use the user-mode APIs of the New IP protocol stack, see [examples](https://gitee.com/openharmony/communication_sfc_newip/tree/master/examples). Fixed addresses and routes are configured in the demo code. You do not need to manually specify the addresses and routes when executing the binary program. +The following table lists the related sample code. For details about how to use the user-mode APIs of the New IP stack, see [examples](https://gitee.com/openharmony/kernel_common_modules_newip/tree/master/examples). Fixed addresses and routes are configured in the demo code. You do not need to manually specify the addresses and routes when executing the binary program. -| File | Description | -| --------------------- | ----------------------------- | -| nip_addr_cfg_demo.c | Demo code for configuring the New IP variable-length addresses. | -| nip_route_cfg_demo.c | Demo code for New IP route configuration. | -| nip_udp_server_demo.c | Demo code for the server that sends and receives New IP UDP packets.| -| nip_udp_client_demo.c | Demo code for the client that sends and receives New IP UDP packets.| -| nip_tcp_server_demo.c | Demo code for the server that sends and receives New IP TCP packets.| -| nip_tcp_client_demo.c | Demo code for the client that sends and receives New IP TCP packets.| -| nip_lib.c | API demo code, for example, obtaining the interface index.| +| File | Description | +| --------------------- | ------------------------------ | +| nip_addr_cfg_demo.c | Sample code for configuring variable-length IP addresses. | +| nip_route_cfg_demo.c | Sample code for configuring NEW IP routes. | +| nip_udp_server_demo.c | Sample code for the server to send and receive New IP packets using UDP.| +| nip_udp_client_demo.c | Sample code for the client to send and receive New IP packets using UDP.| +| nip_tcp_server_demo.c | Sample code for the server to send and receive New IP packets using TCP.| +| nip_tcp_client_demo.c | Sample code for the client to send and receive New IP packets using TCP.| +| nip_lib.c | API demo code, for example, obtaining the interface index. | **Basic Procedure** @@ -301,15 +299,15 @@ The table below lists the demo code files. For details about how to use the user 3. Run the **ifconfig wlan0 up** command to start the network adapter. -4. Run the **./nip_addr_cfg_demo server** command on shell of device 1 to configure a variable-length address **0xDE00** (2 bytes) for the server. Run the **./nip_addr_cfg_demo client** command on device 2 to configure a variable-length address **0x50** (1 byte) for the client. Then, run the **cat /proc/net/nip_addr** command to check the kernel address configuration. +4. Run the **./nip_addr_cfg_demo server** command on shell of device 1 to configure a variable-length address **0xDE00** (2 bytes) for the server. Run the **./nip_addr_cfg_demo client** command on shell of device 2 to configure a variable-length address **0x50** (1 byte) for the client. Run **cat /proc/net/nip_addr** to view the configuration result. -5. Run the **./nip_route_cfg_demo server** command on device 1 to configure the server route data. Run the **./nip_route_cfg_demo client** command on device 2 to configure the client route data. Then, run the **cat /proc/net/nip_route** command to check the kernel route configuration. +5. Run the **./nip_route_cfg_demo server** command on shell of device 1 to configure the server route data. Run the **./nip_route_cfg_demo client** command on shell of device 2 to configure the client route data. Then, run the **cat /proc/net/nip_route** command to check the kernel route configuration. -Now, you can send and receive UDP/TCP packets. By default, the addresses and routes configured are used for sending and receiving packets. +Now, you can send and receive packets over UDP/TCP. By default, the addresses and routes configured are used for sending and receiving packets. -**Sending and Receiving UDP Packets** +**Sending and Receiving Packets over UDP** Run the **./nip_udp_server_demo** command on the server and then the **./nip_udp_client_demo** command on the client. The client sends 10 New IP packets. After receiving the packets, the server sends them to the client. @@ -335,7 +333,7 @@ Received --1661827011 590576 NIP_UDP # 9 sock 3 success: 10/ 10/no= -**Sending and Receiving TCP Packets** +**Sending and Receiving Packets over TCP** Run the **./nip_tcp_server_demo** command on the server and then the **./nip_tcp_client_demo** command on the client. The client sends 10 New IP packets. After receiving the packets, the server sends them to the client. @@ -378,15 +376,16 @@ allowxperm thread_xxx thread_xxx:socket ioctl { 0x8933 0x8916 0x890B }; ## WireShark Packet Parsing Template -The default packet parsing rules of Wireshark cannot parse New IP packets. You can add a New IP packet parsing template to Wireshark to parse New IP packets. For details about the template, see [New IP packet parsing template](https://gitee.com/openharmony/communication_sfc_newip/blob/master/tools/wireshark_cfg_for_newip.lua). +The default packet parsing rules of Wireshark cannot parse New IP packets. You can add a New IP packet parsing template to Wireshark to parse New IP packets. For details about the template, see [New IP packet parsing template](https://gitee.com/openharmony/kernel_common_modules_newip/blob/master/tools/wireshark_cfg_for_newip.lua). The procedure is as follows: -Choose **Help** > **About Wireshark** > **Folders**, open the **Global Configuration** directory, and edit the **init.lua** file. Add **dofile (*DATA_DIR*.."newip.lua")** to the end of the file. *DATA_DIR* is the directory where the **newip.lua** plug-in is located. +1. Choose **Help** > **About Wireshark** > **Folders**, and open the **init.lua** file in the **Global Configuration** directory. +2. Add **dofile (DATA_DIR.."newip.lua")** to the end of the file. *DATA_DIR* is the path of the **newip.lua** file. ![](figures/newip-WireShark-template.png) -The following is an example of adding a New IP packet parsing template: +Example: ``` Path of the New IP packet parsing template: @@ -403,7 +402,11 @@ dofile("D:\\tools\\WireShark\\wireshark_cfg_for_newip.lua") #### ND Request -The figure below shows the format of the New IP Neighbor Discovery (ND) request packet. The header contains a 1-byte bitmap (0x76), which is followed by the TTL, total packet length, upper-layer protocol type, destination address, and source address. The New IP ND request packet contains the packet type, operation code, checksum, and request address. +The following figure shows the format of a New IP Neighbor Discovery (ND) request packet. The header contains a 1-byte bitmap (**0x76**), which is followed by the TTL, total length of the packet, upper-layer protocol type, destination address, and source address. The New IP ND request packet contains the packet type, operation code, checksum, and request address. + +> **NOTE** +> +> New IP supports variable-length addresses (1 to 8 bytes). The 1-byte address in Bitmap 1 indicates that the address carried in the current packet header is of 1 byte. ![](figures/newip-ND-request.png) @@ -411,7 +414,11 @@ The figure below shows the format of the New IP Neighbor Discovery (ND) request #### ND Response -The figure below shows the format of a New IP ND response packet. The header contains two bitmaps (**0x77** and **0x00**). Bitmap 1 is followed by the TTL, total length of the packet, upper-layer protocol type, destination address, and source address. Bitmap2 is used for byte alignment and does not carry any data. (For the rk3568 development board, the data transmitted in the link layer must be of an even number of bytes.) A New IP ND response packet contains the packet type, operation code, checksum, neighbor MAC address length, and neighbor MAC address. +The following figure shows the format of a New IP ND response packet. The New IP header contains two bitmaps (**0x77** and **0x00**). Bitmap1 is followed by the TTL, total packet length, upper-layer protocol type, destination address, and source address. Bitmap2 is used for byte alignment and does not carry any data. (For the rk3568 development board, the data transmitted in the link layer must be of an even number of bytes.) A New IP ND response packet contains the packet type, operation code, checksum, neighbor MAC address length, and neighbor MAC address. + +> **NOTE** +> +> New IP supports variable-length addresses (1 to 8 bytes). The 1-byte address in Bitmap 1 indicates that the address carried in the current packet header is of 1 byte. ![](figures/newip-ND-response.png) @@ -421,6 +428,10 @@ The figure below shows the format of a New IP ND response packet. The header con The figure below shows the format of a TCP three-way handshake SYN packet. The New IP packet header contains two bitmaps (**0x77** and **0x00**). Bitmap1 is followed by the TTL, total packet length, upper-layer protocol type, destination address, and source address. Bitmap2 is used for byte alignment and does not carry any data. (For the rk3568 development board, the data transmitted in the link layer must be of an even number of bytes.) +> **NOTE** +> +> New IP supports variable-length addresses (1 to 8 bytes). The 1-byte address in Bitmap 1 indicates that the address carried in the current packet header is of 1 byte. + ![](figures/newip-TCP-handshake.png) ![](figures/newip-TCP-handshake-parsed.png) @@ -429,6 +440,10 @@ The figure below shows the format of a TCP three-way handshake SYN packet. The N The figure below shows the TCP data format. The New IP header contains two bitmaps (**0x77** and **0x00**). Bitmap 1 is followed by the TTL, total packet length, upper-layer protocol type, destination address, and source address. Bitmap2 is used for byte alignment and does not carry any data. (For the rk3568 development board, the data transmitted in the link layer must be of an even number of bytes.) +> **NOTE** +> +> New IP supports variable-length addresses (1 to 8 bytes). The 1-byte address in Bitmap 1 indicates that the address carried in the current packet header is of 1 byte. + ![](figures/newip-TCP-packet.png) ![](figures/newip-TCP-packet-parsed.png) diff --git a/en/device-dev/kernel/kernel-standard-overview.md b/en/device-dev/kernel/kernel-standard-overview.md index 8c9fdc21f6e0eb3771f631843bb40e4dff2f42d4..619fbed21b20ef3fcc90e593e5c77b52c291e2c5 100644 --- a/en/device-dev/kernel/kernel-standard-overview.md +++ b/en/device-dev/kernel/kernel-standard-overview.md @@ -1,17 +1,18 @@ # Linux Kernel Overview +The standard-system devices come with application processors and memory greater than 128 MiB. OpenHarmony uses the Linux kernel as the base kernel so that appropriate OS kernels can be provided for devices with different resource limitations. -OpenHarmony adopts the Linux kernel as the basic kernel for standard-system devices \(reference memory ≥ 128 MiB\) so that appropriate OS kernels can be selected for the devices subject to resource limitations and therefore provide basic capabilities for upper-layer apps. ## Linux Kernel Versions -Linux kernel versions are classified into the stable version and long-term support \(LTS\) version. +- Linux kernel versions are classified into the stable version and long-term support (LTS) version. -The stable version is released approximately every 3 months to support the latest hardware, improve performance, and fix bugs. Its disadvantage is that the overall maintenance lifespan is short, making long-term stable support unavailable for software. +- The stable version is released approximately every 3 months to support the latest hardware, improve performance, and fix bugs. Its disadvantage is that the overall maintenance lifespan is short, making long-term stable support unavailable for software. -The LTS version provides long-term kernel maintenance \(in fixing bugs and security vulnerabilities\). Generally, the maintenance lifespan is six years. By contrast, non-LTS kernel versions whose maintenance lifespan ranges from six months to two years cannot cover the entire lifespan of their products and may leave the products open to security vulnerabilities. In addition, new features are not added in the LTS version update, which ensures the version stability. Therefore, LTS versions are more suitable for commercial products that pursue stability and security. -## OpenHarmony Kernel Version Selection +- The LTS version provides long-term kernel maintenance (in fixing bugs and security vulnerabilities). Generally, the maintenance lifespan is six years. By contrast, non-LTS kernel versions whose maintenance lifespan ranges from six months to two years cannot cover the entire lifespan of their products and may leave the products open to security vulnerabilities. In addition, new features are not added in the LTS version update, which ensures the version stability. Therefore, LTS versions are more suitable for commercial products that pursue stability and security. -The Linux kernel in OpenHarmony selects appropriate LTS versions as its basic versions. Currently, it supports Linux-4.19 and Linux-5.10. +## OpenHarmony Kernel Versions + +OpenHarmony uses Linux LTS versions as its base kernel. Currently, it supports Linux-4.19 and Linux-5.10. diff --git a/en/device-dev/kernel/kernel-standard-patch.md b/en/device-dev/kernel/kernel-standard-patch.md index 55448b5f893fc22fa938b8ea111fc42ce92d6068..549be494a1920ab16a199263cfd4aa6e56e0793c 100644 --- a/en/device-dev/kernel/kernel-standard-patch.md +++ b/en/device-dev/kernel/kernel-standard-patch.md @@ -1,39 +1,26 @@ # Applying Patches on Development Boards -1. Apply the HDF patches. - - Apply the HDF patches based on the kernel version in the **kernel/linux/build** repository. For details, see the method for applying the HDF patch in **kernel.mk**. - - ``` - $(OHOS_BUILD_HOME)/drivers/hdf_core/adapter/khdf/linux/patch_hdf.sh $(OHOS_BUILD_HOME) $(KERNEL_SRC_TMP_PATH) $(KERNEL_PATCH_PATH) $(DEVICE_NAME) - ``` - -2. Apply the chip driver patches. - - The following uses Hi3516D V300 as an example: - - In the **kernel/linux/build** repository, place the chip module patches in the corresponding path based on the patch path and naming rules for the chip module in **kernel.mk**. - - ``` - DEVICE_PATCH_DIR := $(OHOS_BUILD_HOME)/kernel/linux/patches/${KERNEL_VERSION}/$(DEVICE_NAME)_patch - DEVICE_PATCH_FILE := $(DEVICE_PATCH_DIR)/$(DEVICE_NAME).patch - ``` - - ``` - - ``` - -3. Modify the **config** file to be built. - - In the **kernel/linux/build** repository, place the chip module **config** file in the corresponding path based on the file path and naming rules for the chip module in **kernel.mk**. - - ``` - KERNEL_CONFIG_PATH := $(OHOS_BUILD_HOME)/kernel/linux/config/${KERNEL_VERSION} - DEFCONFIG_FILE := $(DEVICE_NAME)_$(BUILD_TYPE)_defconfig - ``` - - >![](../public_sys-resources/icon-notice.gif) **NOTICE**
- >In the OpenHarmony project build process, patches are applied after the code environment of **kernel/linux/linux-\*.\*** is copied. Before running the OpenHarmony version-level build command, ensure that the source code environment of **kernel/linux/linux-\*.\*** is available. - >After the build is complete, the kernel is generated in the kernel directory in the **out** directory. Modify the **config** file based on the kernel generated, and copy the generated **.config** file to the corresponding path in the **config** repository. Then, the configuration takes effect. - - +1. Apply HDF patches. + Apply the HDF patches based on the kernel version. For details, see [kernel.mk](https://gitee.com/openharmony/kernel_linux_build/blob/master/kernel.mk). + ```makefile + $(OHOS_BUILD_HOME)/drivers/hdf_core/adapter/khdf/linux/patch_hdf.sh $(OHOS_BUILD_HOME) $(KERNEL_SRC_TMP_PATH) $(KERNEL_PATCH_PATH) $(DEVICE_NAME) + ``` + +2. Apply the chip driver patch. The following uses Hi3516D V300 as an example. + Place the chip component patches in the related directory. For details about the patch directory and naming rules, see [kernel.mk](https://gitee.com/openharmony/kernel_linux_build/blob/master/kernel.mk). + ```makefile + DEVICE_PATCH_DIR := $(OHOS_BUILD_HOME)/kernel/linux/patches/${KERNEL_VERSION}/$(DEVICE_NAME)_patch + DEVICE_PATCH_FILE := $(DEVICE_PATCH_DIR)/$(DEVICE_NAME).patch + ``` + +3. Modify the **config** file to build. + Place the chip component **config** in the related directory. For details about the patch directory and naming rules, see [kernel.mk](https://gitee.com/openharmony/kernel_linux_build/blob/master/kernel.mk). + ```makefile + KERNEL_CONFIG_PATH := $(OHOS_BUILD_HOME)/kernel/linux/config/${KERNEL_VERSION} + DEFCONFIG_FILE := $(DEVICE_NAME)_$(BUILD_TYPE)_defconfig + ``` + + > **NOTICE**
+ > In the OpenHarmony project build process, patches are installed after "kernel/linux/linux-\*.\*" is copied. Before using the version-level build command of OpenHarmony, ensure that the "kernel/linux/linux-\*.\*" source code is available. + > + > After the build is complete, the kernel is generated in the kernel directory in the **out** directory. Modify the **config** file for the target platform based on the kernel generated, and copy the generated **.config** file to the corresponding path of the platform in the **config** repository. Then, the configuration takes effect. diff --git a/en/device-dev/kernel/kernel-standard-sched-rtg.md b/en/device-dev/kernel/kernel-standard-sched-rtg.md index 61a36ab5bb7aad13789b0ce053e3dbcd73be6b25..71bf4959b4b5772ec72aecac2945352de958bf43 100644 --- a/en/device-dev/kernel/kernel-standard-sched-rtg.md +++ b/en/device-dev/kernel/kernel-standard-sched-rtg.md @@ -48,14 +48,3 @@ STATE COMM PID PRIO CPU // Thread information, including th --------------------------------------------------------- S bash 436 120 1(0-3) ``` - -## Available APIs - -The RTG provides the device node and ioctl APIs for querying and configuring group information. The device node is in **/dev/sched_rtg_ctrl**. - -| Request | Description | -| ------------------- | ------------------- | -| CMD_ID_SET_RTG | Creates an RTG, and adds, updates, or deletes threads in the group. | -| CMD_ID_SET_CONFIG | Sets global group attributes, for example, the maximum number of real-time RTGs. | -| CMD_ID_SET_RTG_ATTR | Sets specified group attributes, for example, the thread priority. | -| CMD_ID_SET_MIN_UTIL | Sets the minimum utilization of an RTG. | diff --git a/en/device-dev/porting/Readme-EN.md b/en/device-dev/porting/Readme-EN.md index 1ce8a7106f6f9c91bf8e8a9d34db499ddf426f9b..f86cab42984033148c2f5da90ba5b47878d3d5f3 100644 --- a/en/device-dev/porting/Readme-EN.md +++ b/en/device-dev/porting/Readme-EN.md @@ -58,6 +58,14 @@ The download steps for other resources are the same as those in the mainline ver ## SoC Porting Cases - Mini System SoC Porting Cases - - [Mini-System Devices with Screens — Bestechnic SoC Porting Case](porting-bes2600w-on-minisystem-display-demo.md) - - [Combo Solution – ASR Chip Porting Case](porting-asr582x-combo-demo.md) - + - [Mini-System Devices with Screens – Bestechnic SoC Porting Case](porting-bes2600w-on-minisystem-display-demo.md) + - [Combo Solution – ASR Chip Porting Case](porting-asr582x-combo-demo.md) + - [IoT Solution - Chipsea CST85 Chip Porting Case](porting-cst85f01-combo-demo.md) + - [Mini System STM32F407 SoC Porting Case](porting-stm32f407-on-minisystem-eth.md) + - [Combo Solution – W800 Chip Porting Case](porting-w800-combo-demo.md) +- Small System SoC Porting Cases + - [Mini-System Devices – STM32MP1 SoC Porting Case](porting-stm32mp15xx-on-smallsystem.md) +- Standard System SoC Porting Cases + - [Standard System Solution – Rockchip RK3568 Porting Case](porting-dayu200-on_standard-demo.md) + - [Standard System Solution – Rockchip RK3566 Porting Case](https://gitee.com/openharmony/vendor_kaihong/blob/master/khdvk_3566b/porting-khdvk_3566b-on_standard-demo.md) + - [Standard System Solution – Yangfan Porting Case](porting-yangfan-on_standard-demo.md) diff --git a/en/device-dev/porting/figures/bearpi_hm_micro_hb_env.png b/en/device-dev/porting/figures/bearpi_hm_micro_hb_env.png new file mode 100644 index 0000000000000000000000000000000000000000..6b5ca398edb1586f6b6a732279765d6767254849 Binary files /dev/null and b/en/device-dev/porting/figures/bearpi_hm_micro_hb_env.png differ diff --git a/en/device-dev/porting/figures/bearpi_hm_micro_hb_set.png b/en/device-dev/porting/figures/bearpi_hm_micro_hb_set.png new file mode 100644 index 0000000000000000000000000000000000000000..e61f118b06ccf3ae04f2cc48f277fa90cc6817e3 Binary files /dev/null and b/en/device-dev/porting/figures/bearpi_hm_micro_hb_set.png differ diff --git a/en/device-dev/porting/figures/cst85_hb_set.png b/en/device-dev/porting/figures/cst85_hb_set.png new file mode 100644 index 0000000000000000000000000000000000000000..31f67d4b96a221ada999addbf6fe9f0daa99ff4b Binary files /dev/null and b/en/device-dev/porting/figures/cst85_hb_set.png differ diff --git a/en/device-dev/porting/figures/cst85_kconfig.png b/en/device-dev/porting/figures/cst85_kconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..fb5f517615a6966541b507fddf0afb7f3d70fe51 Binary files /dev/null and b/en/device-dev/porting/figures/cst85_kconfig.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-audio-01.png b/en/device-dev/porting/figures/dayu200/dayu200-audio-01.png new file mode 100644 index 0000000000000000000000000000000000000000..de60c43dbf4aa969bb0f4c04e64ec1a43707aa62 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-audio-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-audio-02.png b/en/device-dev/porting/figures/dayu200/dayu200-audio-02.png new file mode 100644 index 0000000000000000000000000000000000000000..4277031b00178ed2c33ac719c8c66bbd4a34f38c Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-audio-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-audio-03.png b/en/device-dev/porting/figures/dayu200/dayu200-audio-03.png new file mode 100644 index 0000000000000000000000000000000000000000..5353d5d8ff7cadcbc9a876c563618ec9c31be50b Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-audio-03.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-backlight-01.png b/en/device-dev/porting/figures/dayu200/dayu200-backlight-01.png new file mode 100644 index 0000000000000000000000000000000000000000..8147197bb13a9fe89e9f3268ad27d790284f3a2c Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-backlight-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-backlight-02.png b/en/device-dev/porting/figures/dayu200/dayu200-backlight-02.png new file mode 100644 index 0000000000000000000000000000000000000000..4823917201f2d389184fe9bdbb7493f7d7ea01b2 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-backlight-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-bt-01.png b/en/device-dev/porting/figures/dayu200/dayu200-bt-01.png new file mode 100644 index 0000000000000000000000000000000000000000..2bf182464e58219294cb38f36ed652c6391ff2a8 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-bt-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-bt-02.png b/en/device-dev/porting/figures/dayu200/dayu200-bt-02.png new file mode 100644 index 0000000000000000000000000000000000000000..bb12aa502e877ef6cb82f5531ec0fe71f29d8120 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-bt-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-camera-01.png b/en/device-dev/porting/figures/dayu200/dayu200-camera-01.png new file mode 100644 index 0000000000000000000000000000000000000000..1a4fa27e98e5bb4568be1399b57da3b987ee4fbf Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-camera-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-camera-02.png b/en/device-dev/porting/figures/dayu200/dayu200-camera-02.png new file mode 100644 index 0000000000000000000000000000000000000000..d1b12ac220bce6fbc1cd61cc4e143e654304fe89 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-camera-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-camera-03.png b/en/device-dev/porting/figures/dayu200/dayu200-camera-03.png new file mode 100644 index 0000000000000000000000000000000000000000..dce12d93b836edf2fe63bc911b4fb3a53d831946 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-camera-03.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-lcd-01.png b/en/device-dev/porting/figures/dayu200/dayu200-lcd-01.png new file mode 100644 index 0000000000000000000000000000000000000000..e04a55fc00008087491e887278c4135550e01f86 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-lcd-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-sensor-01.png b/en/device-dev/porting/figures/dayu200/dayu200-sensor-01.png new file mode 100644 index 0000000000000000000000000000000000000000..df59cdf713d811e796890434cc14613baad8fdff Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-sensor-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-tp-01.png b/en/device-dev/porting/figures/dayu200/dayu200-tp-01.png new file mode 100644 index 0000000000000000000000000000000000000000..83884ab97b8cf678c466da4d1cd71b116d1199a1 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-tp-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-tp-02.png b/en/device-dev/porting/figures/dayu200/dayu200-tp-02.png new file mode 100644 index 0000000000000000000000000000000000000000..67097e4c56ea9afeab53b7e66e0135974c892b74 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-tp-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-vibrator-01.png b/en/device-dev/porting/figures/dayu200/dayu200-vibrator-01.png new file mode 100644 index 0000000000000000000000000000000000000000..aee6e922157e6fa65b1c406a3b83d6052d57d583 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-vibrator-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-01.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-01.png new file mode 100644 index 0000000000000000000000000000000000000000..25bd7e1e94c6a74cced86e8a9a97ff9b5c096d81 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-01.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-02.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-02.png new file mode 100644 index 0000000000000000000000000000000000000000..f241edcd1c9bd663cb8a2bd922f9e9571d3919a4 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-02.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-03.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-03.png new file mode 100644 index 0000000000000000000000000000000000000000..ca2c0321f1c70b4360efc30ef50cb13dda1ec062 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-03.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-04.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-04.png new file mode 100644 index 0000000000000000000000000000000000000000..26fd0101f8393738a56be248b15bc8c0a101ced7 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-04.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-05.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-05.png new file mode 100644 index 0000000000000000000000000000000000000000..45a08d5680bcebd57c989a730a347f5ea9da6c80 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-05.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-06.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-06.png new file mode 100644 index 0000000000000000000000000000000000000000..6b783cee8eeeec48f4e8569cb9dad84ff23aaff2 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-06.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-07.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-07.png new file mode 100644 index 0000000000000000000000000000000000000000..632767e7118a9c11517c78de1e1ec6b8312ade15 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-07.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-08.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-08.png new file mode 100644 index 0000000000000000000000000000000000000000..7613843196a8fa7abf0f31606a8a96ba19daed64 Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-08.png differ diff --git a/en/device-dev/porting/figures/dayu200/dayu200-wifi-09.png b/en/device-dev/porting/figures/dayu200/dayu200-wifi-09.png new file mode 100644 index 0000000000000000000000000000000000000000..c77b75b8f6207cfa4c535faca124cf731a9cd9ec Binary files /dev/null and b/en/device-dev/porting/figures/dayu200/dayu200-wifi-09.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-Audio-ADM.png b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-ADM.png new file mode 100644 index 0000000000000000000000000000000000000000..8105adf7d8d2cd5602583aa5a8695ccd2f94c329 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-ADM.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-Audio-commond.png b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-commond.png new file mode 100644 index 0000000000000000000000000000000000000000..e8272b965d03e8a7e260815b9e23aa88eca7a827 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-commond.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-Audio-play.png b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-play.png new file mode 100644 index 0000000000000000000000000000000000000000..3d300a4a98026d466e2929c1637435bfefa8ea88 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-play.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-Audio-start.png b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-start.png new file mode 100644 index 0000000000000000000000000000000000000000..0d835341e9b4eca0907f969e5bb7097c7599f585 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-Audio-start.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-picture-uvc0.png b/en/device-dev/porting/figures/isoftstone/yangfan-picture-uvc0.png new file mode 100644 index 0000000000000000000000000000000000000000..e4748f67fe8f4932e8a9f810c6a8b0affc488eac Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-picture-uvc0.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-picture-v4l2_main.png b/en/device-dev/porting/figures/isoftstone/yangfan-picture-v4l2_main.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7ed14902b007643639a48aeaf37ab9cc3f4b86 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-picture-v4l2_main.png differ diff --git a/en/device-dev/porting/figures/isoftstone/yangfan-print-01.png b/en/device-dev/porting/figures/isoftstone/yangfan-print-01.png new file mode 100644 index 0000000000000000000000000000000000000000..cd9d45c9ba85642542bf4f0d56215b4494bb3192 Binary files /dev/null and b/en/device-dev/porting/figures/isoftstone/yangfan-print-01.png differ diff --git a/en/device-dev/porting/figures/niobe407_boot.png b/en/device-dev/porting/figures/niobe407_boot.png new file mode 100644 index 0000000000000000000000000000000000000000..c823e31c71d7888137916e2b68b2a383b8a40ff1 Binary files /dev/null and b/en/device-dev/porting/figures/niobe407_boot.png differ diff --git a/en/device-dev/porting/figures/niobe407_hb_env.png b/en/device-dev/porting/figures/niobe407_hb_env.png new file mode 100644 index 0000000000000000000000000000000000000000..4614acd8a01c3dafeef581e0f55c7be8a2ca18f6 Binary files /dev/null and b/en/device-dev/porting/figures/niobe407_hb_env.png differ diff --git a/en/device-dev/porting/figures/niobe407_hb_set.png b/en/device-dev/porting/figures/niobe407_hb_set.png new file mode 100644 index 0000000000000000000000000000000000000000..12d5d844fe8ab1e954a2e0195525950c38e34fb6 Binary files /dev/null and b/en/device-dev/porting/figures/niobe407_hb_set.png differ diff --git a/en/device-dev/porting/figures/niobe407_menuconfig.png b/en/device-dev/porting/figures/niobe407_menuconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..589d6e9790e50812c0f787f27503a60d9113901f Binary files /dev/null and b/en/device-dev/porting/figures/niobe407_menuconfig.png differ diff --git a/en/device-dev/porting/figures/w800_select.png b/en/device-dev/porting/figures/w800_select.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b545820a4a07a106c9879a8ab8fe20586a0d0f Binary files /dev/null and b/en/device-dev/porting/figures/w800_select.png differ diff --git a/en/device-dev/porting/porting-bes2600w-on-minisystem-display-demo.md b/en/device-dev/porting/porting-bes2600w-on-minisystem-display-demo.md index 6683c254d51b424f49158c29c8b3492f148b5027..fdf4be08997d2ed8083d77b6ba75d2e8007343e0 100644 --- a/en/device-dev/porting/porting-bes2600w-on-minisystem-display-demo.md +++ b/en/device-dev/porting/porting-bes2600w-on-minisystem-display-demo.md @@ -1,6 +1,4 @@ - - -# Mini-System Devices with Screens — Bestechnic SoC Porting Case +# Mini-System Devices with Screens – Bestechnic SoC Porting Case This document exemplifies the porting procedure for a development board on a mini-system device with a screen – an intelligent switch panel. It uses the BES multi-modal V200Z-R development board powered by the Bestechnic BES2600W SoC as an example. Components such as `ace_engine_lite`, `graphic_ui`, `aafwk_lite`, `appexecfwk_lite`, and `HDF` are adapted based on the OpenHarmony LiteOS-M kernel. This example uses the board-SoC separation solution as the porting architecture, the Newlib C or Musl C library as the toolchain, and GN and Kconfig graphical configuration for LiteOS-M kernel compilation. @@ -1395,7 +1393,7 @@ To adapt the `aafwk` subsystem, you need to add the `aafwk_lite` component in th For details about the `aafwk_lite` use cases, see the `vendor/bestechnic/display_demo/tests/ability` directory, which includes the `launcher` and `js app` applications. The function invocation process of the applications is described as follows: 1. `launcher` application: Use `InstallLauncher` to install the `native ui` application, whose `BundleName` is `"com.example.launcher"`. After `AbilityMgrSliteFeature` is started, `AbilityMgrHandler::StartLauncher()` is invoked to start the `launcher` application. - + 2. `StartJSApp` application: Use `StartAbility` to start any `Want` and pass `want data` to `JS_APP_PATH`, `SetWantData(&want, JS_APP_PATH, strlen(JS_APP_PATH) + 1)`. @@ -1422,7 +1420,7 @@ For details about product compatibility specifications, see [Introduction to Pro ### XTS Test Cases -For details about the `XTS` test cases, see [XTS](../subsystems/subsys-xts-guide.md). To adapt the `XTS` subsystem, you need to add the `xts_acts`/`xts_tools` component in the `config.json` file, as shown below: +For details about the `XTS` test cases, see [XTS](../device-test/xts.md). To adapt the `XTS` subsystem, you need to add the `xts_acts`/`xts_tools` component in the `config.json` file, as shown below: { "subsystem": "xts", diff --git a/en/device-dev/porting/porting-cst85f01-combo-demo.md b/en/device-dev/porting/porting-cst85f01-combo-demo.md new file mode 100644 index 0000000000000000000000000000000000000000..0fe636d2a8ec38572c5a1437becab8600655c793 --- /dev/null +++ b/en/device-dev/porting/porting-cst85f01-combo-demo.md @@ -0,0 +1,855 @@ +# IoT Solution - Chipsea CST85 Chip Porting Case + +This document describes how to port the OpenHarmony LiteOS-M mini system on the cst85_wblink development board based on the Chipsea CST85 chip. In this document, Wi-Fi connection and XTS test samples are developed, and the adaptation of components such as wifi_lite, lwIP, startup, Utils, XTS, and HDF is implemented based on the OpenHarmony LiteOS-M kernel. The porting architecture uses the Board and SoC separation solution and the Newlib C library as the toolchain. The LiteOS-M kernel is compiled in gn+Kconfig graphical configuration mode. + +## Compilation and Building Adaptation + +### Directory Planning + +This 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). + +``` +device +├── board --- Board vendor directory. +│ └── chipsea --- Board vendor name Chipsea. +│ └── cst85_wblink --- Board name cst85_wblink. +└── soc --- SoC vendor directory. + └── chipsea --- SoC vendor name Chipsea. + └── cst85 --- SoC series name CST85. +``` + +The planned product demo directory is as follows: + +``` +vendor +└── chipsea --- Vendor of the product demo, which is Chipsea here. + ├── iotlink_demo --- Product name: Wi-Fi sample. + └── xts_demo --- Product name: XTS test sample. +``` + +### Product Definition +The vendor/chipsea/iotlink_demo describes the kernel, board, and subsystem information used by the product. The kernel, board model, and board vendor are required by the precompilation command and must be planned in advance. The information entered here corresponds to the planned directory. Example: +``` +{ + "product_name": "iotlink_demo", --- Product name. + "version": "3.0", --- OS version: 3.0 + "device_company": "chipsea", --- Board vendor: Chipsea + "board": "cst85_wblink", --- Board name: cst85_wblink + "kernel_type": "liteos_m", --- Kernel type: liteos_m + "kernel_version": "3.0.0", --- Kernel version: 3.0.0 + "subsystems": [] --- Subsystems +} +``` + +### Board Configuration +In the directory associated with the product definition, for example, **/device/board/chipsea/cst85_wblink**, you need to place the **config.gni** file in the **liteos_m** directory. This configuration file describes the board information, including the CPU, toolchain, kernel, and compile_flags. Example: +``` +# Kernel type +kernel_type = "liteos_m" + +# Kernel version +kernel_version = "3.0.0" + +# Board CPU type +board_cpu = "cortex-m4" + +# Toolchain: arm-none-eabi +board_toolchain = "arm-none-eabi" + +# Path of the toolchain. You can use the system path '''' or customize the path. +board_toolchain_path = "" + +# Compilation parameters about the board +board_cflags = [] + +# Link parameters about the board +board_ld_flags = [] + +# Header files about the board +board_include_dirs = [] + +# Board adapter dir for OHOS components. +board_adapter_dir = "${ohos_root_path}device/soc/chipsea" +``` + +### Precompilation +After the product directory, product definition, and board configuration are correctly configured, enter the precompilation command **hb set** in the root directory of the project. Then you can find the related product in the displayed list. + +![ohos_config.json](figures/cst85_hb_set.png) + +After you select a product and press **Enter**, a `ohos_config.json` file will be generated in the root directory. The file will list the product information to be compiled. + +``` +{ + "root_path": "/home/openharmony", + "board": "cst85_wblink", + "kernel": "liteos_m", + "product": "iotlink_demo", + "product_path": "/home/openharmony/vendor/chipsea/iotlink_demo", + "device_path": "/home/openharmony/device/board/chipsea/cst85_wblink/liteos_m", + "device_company": "chipsea", + "os_level": "mini", + "version": "3.0", + "patch_cache": null, + "product_json": "/home/openharmony/vendor/chipsea/iotlink_demo/config.json", + "target_cpu": null, + "target_os": null, + "out_path": "/home/openharmony/out/cst85_wblink/iotlink_demo" +} +``` + + +## Kernel Porting + +### Kconfig Adaptation + +During the compilation of **//kernel/liteos_m**, you need to use the `Kconfig` file for configurations in the corresponding board and SoC directories. Let's see the related configurations in the board and SoC directories. + +The following uses `//device/board/chipsea` as an example for the `Kconfig` in the board directory: +``` +device/board/chipsea +├── cst85_wblink --- Board configuration directory **cst85_wblink**. +│ ├── Kconfig.liteos_m.board --- Board configuration options. +│ ├── Kconfig.liteos_m.defconfig.board --- Default board configuration items. +│ └── liteos_m +│ └── config.gni --- Board configuration file. +├── Kconfig.liteos_m.boards --- Board configurations of the board vendor. +└── Kconfig.liteos_m.defconfig.boards --- Board configurations of the board vendor. +``` + +In `cst85_wblink/Kconfig.liteos_m.board`, configure that **BOARD_CST85_WBLINK** can be selected only when **SOC_CST85F01** is selected. +``` +config BOARD_CST85_WBLINK + bool "select board cst85_wblink" + depends on SOC_CST85F01 +``` + +The following uses `//device/soc/chipsea` as an example for the `Kconfig` in the SoC directory: + +``` +device/soc/chipsea/ +├── cst85 --- CST85 series. +│ ├── Kconfig.liteos_m.defconfig.cst85f01 --- Default CST85F01 SoC configuration. +│ ├── Kconfig.liteos_m.defconfig.series --- Default configuration of the CST85 series. +│ ├── Kconfig.liteos_m.series --- Configuration of the CST85 series. +│ └── Kconfig.liteos_m.soc --- CST85 SoC configuration. +├── Kconfig.liteos_m.defconfig --- Default SoC configuration. +├── Kconfig.liteos_m.series --- Series configuration. +└── Kconfig.liteos_m.soc --- SoC configuration. +``` + +The `cst85/Kconfig.liteos_m.series` configuration is as follows: + +``` +config SOC_SERIES_CST85 + bool "Chipsea CST85 Series" + select ARM + select SOC_COMPANY_CHIPSEA + select CPU_CORTEX_M4 + help + Enable support for Chipsea CST85 series +``` + +**SOC_CST85F01** can be selected in **cst85/Kconfig.liteos_m.soc** only when **SOC_SERIES_CST85** is selected. + +``` +choice + prompt "Chipsea CST85 series SoC" + depends on SOC_SERIES_CST85 + +config SOC_CST85F01 + bool "SoC CST85F01" + +endchoice +``` + +To compile **BOARD_CST85_WBLINK**, select **SOC_COMPANY_CHIPSEA**, **SOC_SERIES_CST85** and **SOC_CST85F01**. You can run the `make menuconfig` command in `kernel/liteos_m` for configurations. + +![cst85_kconfig.json](figures/cst85_kconfig.png) + +The configured file is saved to `//vendor/chipsea/iotlink_demo/kernel_configs/debug.config` by default. You can also directly configure **debug.config**. + +``` +LOSCFG_SOC_SERIES_CST85=y +LOSCFG_KERNEL_BACKTRACE=y +LOSCFG_KERNEL_CPUP=y +LOSCFG_PLATFORM_EXC=y +``` + +### Modular Compilation + +The compilation of `Board` and `SoC` adopts the modular compilation method, starting from **kernel/liteos_m/BUILD.gn** and increasing by level. The adaptation process of this solution is as follows: + +1. Create the **BUILD.gn** file in `//device/board/chipsea` and add the following content to the file: + + ``` + if (ohos_kernel_type == "liteos_m") { + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ + "cst85_wblink" + ] + } + } + ``` + + In the preceding **BUILD.gn** file, **cst85_wblink** is the module name organized by directory level. + +2. Create the **BUILD.gn** file in `//device/soc/chipsea` in the same way and add the following content to the file by directory level: + + ``` + if (ohos_kernel_type == "liteos_m") { + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ + "cst85", + "hals", + ] + } + } + ``` + +3. In the `//device/soc/chipsea` module at each level, add the **BUILD.gn** file and compile the module. The following uses `//device/soc/chipsea/cst85/liteos_m/sdk/bsp/arch/BUILD.gn` as an example: + + ``` + import("//kernel/liteos_m/liteos.gni") + module_name = "sdk_bsp_arch" + + kernel_module(module_name) { + sources = [ + "boot/armgcc_4_8/boot_startup.S", + "boot/armgcc_4_8/exception.S", + "boot/fault_handler.c", + + "cmsis/cmsis_nvic.c", + + "ll/ll.c", + + "main/arch_main.c", + ] + + include_dirs = [ + "boot", + "boot/armgcc_4_8", + ] + + deps = [ + "//base/startup/bootstrap_lite/services/source:bootstrap", + ] + } + + config("public") { + include_dirs = [ + ".", + "boot", + "compiler", + "cmsis", + "ll", + ] + } + ``` + + To organize links and some compilation options, set the following parameters in **config("public")**: + + ``` + config("public") { + include_dirs = [] --- Common header file. + ldflags = [] --- Link parameters, including the Id file. + libs = [] --- Link library. + defines = [] --- Definitions. + } + ``` + + ![](../public_sys-resources/icon-note.gif) **NOTE** + It is recommended that common parameter options and header files not be repeatedly filled in each component. + +### Kernel Startup Adaptation + +The kernel startup adaptation file is stored in `//device/soc/chipsea/cst85/liteos_m/sdk/modules/rtos/src/rtos.c`. + +The general idea of kernel startup adaptation is as follows: + +1. Use `OsVectorInit();` to initialize the interrupt vector and initialize the interrupt processing function. +2. Use `osKernelInitialize` to initialize the kernel. +3. Create a thread for OS component platform initialization using `OHOS_SystemInit`. +4. Use `DeviceManagerStart()` for HDF initialization. +5. The kernel starts to schedule the `LOS_Start` thread. + +This section describes step 3 in detail. Other steps are used to call kernel functions and are not described here. + +Initialize necessary actions before **OHOS_SystemInit** is started in step 3, as shown below: + +``` +... + LOS_KernelInit(); + DeviceManagerStart(); + OHOS_SystemInit(); + LOS_Start(); + +.... +``` + +### Interrupt Adaptation +To ensure the normal running of LiteOS-M, two interrupt service routines must be redirected to the ISRs specified by LiteOS-M: HalPendSV and OsTickerHandler. This depends on whether LiteOS-M takes over the interrupt vector table when LiteOS-M is adapted. +``` +/** + * @ingroup los_config + * Configuration item for using system defined vector base address and interrupt handlers. + * If LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT is set to 0, vector base address will not be + * modified by system. In arm, it should be noted that PendSV_Handler and SysTick_Handler should + * be redefined to HalPendSV and OsTickHandler respectably in this case, because system depends on + * these interrupt handlers to run normally. What's more, LOS_HwiCreate will not register handler. + */ +#ifndef LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT +#define LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT 1 +#endif +``` + +#### Whether the OS Takes Over Interrupt Vectors +You can configure the **target_config.h** file to determine whether to take over LiteOS. The options are: **1**: yes; **0**: no. +``` +#define LOSCFG_USE_SYSTEM_DEFINED_INTERRUPT 0 +``` + + +If this parameter is set to **1**, LiteOS changes **SCB->VTOR** to **g_hwiForm**. Therefore, the ArchHwiCreate API of the LITEOS needs to be called to configure the original ISRs of the SoC to the new interrupt vector table **g_hwiForm** during startup, and the interrupt service routines of PendSV and SysTicke are redirected to HalPendSV and OsTickerHandler. Otherwise, the original ISRs of the SoC do not respond. + +If this parameter is set to **0**, the original interrupt vector table of the SoC is used. For CST85F01, the interrupt vector table is **__vectors_start___** (**NVIC_Vectors_Init** copies the content of **__isr_vector** to this table). To adapt LiteOS, you must redirect the interrupt service routines of PendSV and SysTick to HalPendSV and OsTickHandler. Otherwise, the system cannot run. + +In this example, LiteOS is not allowed to take over interrupt processing. Therefore, you need to redirect the interrupt service routines of PendSV and SysTick to HalPendSV and OsTickHandler during startup. +``` +#ifdef CFG_LITEOS +static void OsVectorInit(void) +{ + NVIC_SetVector(PendSV_IRQn, (uint32_t)HalPendSV); + NVIC_SetVector(SysTick_IRQn, (uint32_t)OsTickHandler); +} +#endif +``` + +#### Interrupt Vector Table Address Alignment +As described in the documents related to Cortex-M, the minimum address of the interrupt vector table is 32-byte aligned, that is, 0x80. +For example, if 21 more interrupts are required, a total of 37 interrupts are required because there are already 16 system interrupts. One 0x80 is insufficient for 37\*4 entries, and two 0x80s, that is, 0x100, are required. + +In CST85F01 adaptation, the maximum number of interrupt vectors is 128 (defined in **target_config.h**). +``` +#define LOSCFG_PLATFORM_HWI_LIMIT 128 +``` +Here, 128 interrupts and system interrupts are required, that is, a total of 144 (128 + 16) interrupts, and 144\*4 entries are required. These entries need to be covered by four 0x80s, that is, 0x200 alignment. Otherwise, the system restarts. +Therefore, the interrupt alignment needs to be overridden to 0x200. +``` +#ifndef LOSCFG_ARCH_HWI_VECTOR_ALIGN +#define LOSCFG_ARCH_HWI_VECTOR_ALIGN 0x200 +#endif +``` + +### LittleFS Adaptation + +In the XTS test, the syspara test involves the read and write of files in the KV storage. Therefore, a file system needs to be adapted to store the KV to a certain position in the flash memory. Therefore, the LittleFS is adapted. + +During the adaptation, an adaptation API needs to be added to `device/soc/chipsea/cst85/liteos_m/components/drivers/littlefs`. + +``` + #define LFS_DEFAULT_START_ADDR 0x081E3000 ---LittleFS start address + #define LFS_DEFAULT_BLOCK_SIZE 4096 --- Block size + #define LFS_DEFAULT_BLOCK_COUNT 25 ---Number of blocks + +``` + +Finally, the LittleFS API of the kernel is implemented in `device/soc/chipsea/cst85/liteos_m/components/drivers/littlefs/hal_vfs.c`. + +``` +int32_t hal_vfs_init(void) +{ + VfsOps = malloc(sizeof(struct lfs_manager)); + if (VfsOps == NULL) { + printf("+++ hal_vfs_init: NO memory!!\n"); + return -1; + } else { + memset(VfsOps, 0, sizeof(struct lfs_manager)); + } + + VfsOps->LfsOps.read = lfs_block_read; // Flash read API + VfsOps->LfsOps.prog = lfs_block_write; // Flash write API + VfsOps->LfsOps.erase = lfs_block_erase; // Flash erase API + VfsOps->LfsOps.sync = lfs_block_sync; + VfsOps->LfsOps.read_size = 256; + VfsOps->LfsOps.prog_size = 256; + VfsOps->LfsOps.cache_size = 256; + VfsOps->LfsOps.lookahead_size = 16; + VfsOps->LfsOps.block_cycles = 500; + VfsOps->start_addr = LFS_DEFAULT_START_ADDR; + VfsOps->LfsOps.block_size = LFS_DEFAULT_BLOCK_SIZE; + VfsOps->LfsOps.block_count = LFS_DEFAULT_BLOCK_COUNT; + + SetDefaultMountPath(0,"/data"); + if (LOS_FsMount(NULL, "/data", "littlefs", 0, VfsOps) != FS_SUCCESS) { + printf("+++ hal_vfs_init: Mount littlefs failed!\n"); + free(VfsOps); + return -1; + } + + if (LOS_Mkdir("/data", 0777) != 0 ) { + printf("+++ hal_vfs_init: Make dir failed!\n"); + } + + flash_user_data_addr_length_set(LFS_DEFAULT_START_ADDR, + LFS_DEFAULT_BLOCK_SIZE * LFS_DEFAULT_BLOCK_COUNT); + + printf("+++ hal_vfs_init: Mount littlefs success!\n"); + return 0; +} + +``` + + + +### C Library Adaptation + +In a mini system, the C library adaptation is complex. For details about the design idea, see [LiteOS-M Kernel Supports Smooth Switching Between musl and newlib](https://gitee.com/arvinzzz/ohos_kernel_design_specification/blob/master/liteos_m/%E6%94%AF%E6%8C%81newlib/%E5%86%85%E6%A0%B8%E9%80%82%E9%85%8Dnewlib%E6%96%B9%E6%A1%88%E6%80%9D%E8%B7%AF.md). The built-in `newlib` C library is used for system porting. Select **LOSCFG_LIBC_NEWLIB=y** in `vendor/chipsea/iotlink_demo/kernel_configs/debug.config`. + + +### printf Adaptation + +To easily use standard functions in the C library to output information, implement adaptation to output the standard function information to the hardware (serial port). Therefore, the printf function is adapted. + +Add the `wrap` link option of the printf function to `//device/board/chipsea/cst85_wblink/liteos_m/config.gni`. + +``` +board_ld_flags += [ + "-Wl,--wrap=printf", +] +``` + +Implement *_wrap* printf in `device/soc/chipsea/cst85/liteos_m/sdk/bsp/wrapper/lite_sys.c`. + + +### HDF Adaptation of GPIO +To easily use the HDF to use GPIO functions, adapt the HDF for the GPIO. + +1. The chip driver adaptation file is stored in the `//drivers/adapter/platform` directory. Add the **gpio_chipsea.c** and **gpio_chipsea.h** files to the **gpio** directory, and add compilation conditions of the new driver file to **BUILD.gn**. + + ``` + if (defined(LOSCFG_SOC_COMPANY_CHIPSEA)) { + sources += [ "gpio_chipsea.c" ] + } + ``` + +2. Describe the driver description file in **gpio_chipsea.c** as follows: + + ``` + struct HdfDriverEntry g_gpioDriverEntry = { + .moduleVersion = 1, + .moduleName = "HDF_PLATFORM_GPIO", + .Bind = GpioDriverBind, + .Init = GpioDriverInit, + .Release = GpioDriverRelease, + }; + + HDF_INIT(g_gpioDriverEntry); + ``` + +3. Add the GPIO hardware description file **gpio.hcs** to **cst85/liteos_m/components/hdf_config/device_info.hcs**. The mapped gpio0 controls the programmable LED on the card. The hcs file content is as follows: + + ``` + root { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_gpio :: device { + gpio0 :: deviceNode { + policy = 0; + priority = 100; + moduleName = "HDF_PLATFORM_GPIO"; + serviceName = "HDF_PLATFORM_GPIO"; + deviceMatchAttr = "gpio_config"; + } + } + } + ``` + + +## OpenHarmony Subsystem Adaptation + +### Communication Subsystem + +In the communication subsystem, the wifi_lite component needs to be enabled and adapt related APIs. + +The configuration items of the wifi_lite component are as follows: + +``` +"subsystem": "communication", +"components": [ + { "component": "wifi_lite", "features":[] } + ] +``` + +The implementation related to Wi-Fi is under `//device/soc/chipsea/hals/communication/wifi_lite/wifiservice/wifi_device.c`. + +``` +...... +WifiErrorCode Scan(void) +{ + WIFI_STATE_INVALID_CHECK(WIFI_INACTIVE); + + int testNum = MEMP_NUM_NETCONN; + dbg("testNum %d\r\n", testNum); + ChipseaWifiMsg msg = { + .eventId = WIFI_START_SCAN, + .payLoad = 0, + }; + + if (WifiCreateLock() != WIFI_SUCCESS) { + return ERROR_WIFI_NOT_AVAILABLE; + } + if (rtos_queue_write(g_wifiData.wifiQueue, &msg, 1, false) != 0) { + dbg("wifiDevice:rtos_queue_write err\r\n"); + WifiUnlock(); + return ERROR_WIFI_NOT_AVAILABLE; + } + WifiUnlock(); + return WIFI_SUCCESS; +} + +...... +int GetSignalLevel(int rssi, int band) +{ + if (band == HOTSPOT_BAND_TYPE_2G) { + if (rssi >= RSSI_LEVEL_4_2_G) + return RSSI_LEVEL_4; + if (rssi >= RSSI_LEVEL_3_2_G) + return RSSI_LEVEL_3; + if (rssi >= RSSI_LEVEL_2_2_G) + return RSSI_LEVEL_2; + if (rssi >= RSSI_LEVEL_1_2_G) + return RSSI_LEVEL_1; + } + + if (band == HOTSPOT_BAND_TYPE_5G) { + if (rssi >= RSSI_LEVEL_4_5_G) + return RSSI_LEVEL_4; + if (rssi >= RSSI_LEVEL_3_5_G) + return RSSI_LEVEL_3; + if (rssi >= RSSI_LEVEL_2_5_G) + return RSSI_LEVEL_2; + if (rssi >= RSSI_LEVEL_1_5_G) + return RSSI_LEVEL_1; + } + return ERROR_WIFI_INVALID_ARGS; +} + +``` + +### Kernel Subsystem + +For the kernel subsystem, you need to configure the lwIP component closely related to Wi-Fi, use the third-party lwIP component of the community, and specify the directory for adapting the third-party lwIP and Wi-Fi systems. + +By default, `lwip` is configured in the `LiteOS-M kernel` directory. Therefore, the compilation function is available. You can specify the `lwip` compilation directory in the `kernel` component. The sample code is as follows: + +``` + { + "subsystem": "kernel", + "components": [ + { + "component": "liteos_m", + "features": [ + "ohos_kernel_liteos_m_lwip_path = \"//device/soc/chipsea/cst85/liteos_m/sdk/modules/lwip-2.1\"" + --- Specify that adaptation is performed in the chip vendor directory. + ] + } + ] + }, +``` + +The `//device/soc/chipsea/cst85/liteos_m/sdk/modules/lwip-2.1/BUILD.gn` file describes the compilation of `lwip` as follows: + +``` +import("//kernel/liteos_m/liteos.gni") +import("$LITEOSTHIRDPARTY/lwip/lwip.gni") +import("$LITEOSTOPDIR/components/net/lwip-2.1/lwip_porting.gni") + +module_switch = defined(LOSCFG_NET_LWIP_SACK) +module_name = "lwip" +kernel_module(module_name) { + sources = LWIP_PORTING_FILES + LWIPNOAPPSFILES - + [ "$LWIPDIR/api/sockets.c" ] + [ "porting/src/ethernetif.c" ] --- Add the **ethernetif.c** file for Ethernet network adapter initialization and adaptation. + defines = [ "LITEOS_LWIP=1" ] + defines += [ "CHECKSUM_BY_HARDWARE=1" ] +} + +config("public") { + defines = [ "_BSD_SOURCE=1" ] + include_dirs = + [ "porting/include" ] + LWIP_PORTING_INCLUDE_DIRS + LWIP_INCLUDE_DIRS +} + +``` + +In the `//device/soc/chipsea/cst85/liteos_m/sdk/modules/lwip-2.1/porting/include/lwip/lwipopts.h` file, the original `lwip` configuration items remain unchanged, the DSoftBus depends on these configuration items, and a hardware adaptation configuration item is added as follows: + +``` +#ifndef _PORTING_LWIPOPTS_H_ +#define _PORTING_LWIPOPTS_H_ + +#include_next "lwip/lwipopts.h" --- Original configuration items remain unchanged. + +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_CHECKSUM_ON_COPY 0 +#define CHECKSUM_GEN_UDP 0 --- New hardware adaptation item. + +#endif /* _PORTING_LWIPOPTS_H_ */ + +``` + +In the `//device/soc/chipsea/cst85/liteos_m/sdk/modules/lwip-2.1/porting/net_al.c` file, the adaptation to the initialization of the `Ethernet` network adapter is described as follows: + +``` +static err_t net_if_init(struct netif *net_if) +{ + err_t status = ERR_OK; + struct fhost_vif_tag *vif = (struct fhost_vif_tag *)net_if->state; + + #if LWIP_NETIF_HOSTNAME + { + /* Initialize interface hostname */ + net_if->hostname = "CsWlan"; + } + #endif /* LWIP_NETIF_HOSTNAME */ + + net_if->name[ 0 ] = 'w'; + net_if->name[ 1 ] = 'l'; + + net_if->output = etharp_output; + net_if->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP; + net_if->hwaddr_len = ETHARP_HWADDR_LEN; + net_if->mtu = LLC_ETHER_MTU; + net_if->linkoutput = net_if_output; + memcpy(net_if->hwaddr, &vif->mac_addr, ETHARP_HWADDR_LEN); + + return status; +} + +``` + +### Startup Subsystem + +To run application frameworks such as XTS or APP_FEATURE_INIT, the bootstrap_lite and syspara_lite components of the startup subsystem are adapted. + +Add the corresponding configuration items to the `vendor/chipsea/wblink_demo/config.json` file: + +``` +{ + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite" --- bootstrap_lite component + }, + { + "component": "syspara_lite", --- syspara_lite component + "features": [ + "enable_ohos_startup_syspara_lite_use_posix_file_api = true" + ] + } + ] +}, +``` + +When adapting the **bootstrap_lite** component, you need to manually add the following content to the link script file `//device/soc/chipsea/cst85/liteos_m/sdk/bsp/out/cst85f01/cst85f01.ld`: + +``` + __zinitcall_bsp_start = .; + KEEP (*(.zinitcall.bsp0.init)) + KEEP (*(.zinitcall.bsp1.init)) + KEEP (*(.zinitcall.bsp2.init)) + KEEP (*(.zinitcall.bsp3.init)) + KEEP (*(.zinitcall.bsp4.init)) + __zinitcall_bsp_end = .; + __zinitcall_device_start = .; + KEEP (*(.zinitcall.device0.init)) + KEEP (*(.zinitcall.device1.init)) + KEEP (*(.zinitcall.device2.init)) + KEEP (*(.zinitcall.device3.init)) + KEEP (*(.zinitcall.device4.init)) + __zinitcall_device_end = .; + __zinitcall_core_start = .; + KEEP (*(.zinitcall.core0.init)) + KEEP (*(.zinitcall.core1.init)) + KEEP (*(.zinitcall.core2.init)) + KEEP (*(.zinitcall.core3.init)) + KEEP (*(.zinitcall.core4.init)) + __zinitcall_core_end = .; + __zinitcall_sys_service_start = .; + KEEP (*(.zinitcall.sys.service0.init)) + KEEP (*(.zinitcall.sys.service1.init)) + KEEP (*(.zinitcall.sys.service2.init)) + KEEP (*(.zinitcall.sys.service3.init)) + KEEP (*(.zinitcall.sys.service4.init)) + __zinitcall_sys_service_end = .; + __zinitcall_sys_feature_start = .; + KEEP (*(.zinitcall.sys.feature0.init)) + KEEP (*(.zinitcall.sys.feature1.init)) + KEEP (*(.zinitcall.sys.feature2.init)) + KEEP (*(.zinitcall.sys.feature3.init)) + KEEP (*(.zinitcall.sys.feature4.init)) + __zinitcall_sys_feature_end = .; + __zinitcall_run_start = .; + KEEP (*(.zinitcall.run0.init)) + KEEP (*(.zinitcall.run1.init)) + KEEP (*(.zinitcall.run2.init)) + KEEP (*(.zinitcall.run3.init)) + KEEP (*(.zinitcall.run4.init)) + __zinitcall_run_end = .; + __zinitcall_app_service_start = .; + KEEP (*(.zinitcall.app.service0.init)) + KEEP (*(.zinitcall.app.service1.init)) + KEEP (*(.zinitcall.app.service2.init)) + KEEP (*(.zinitcall.app.service3.init)) + KEEP (*(.zinitcall.app.service4.init)) + __zinitcall_app_service_end = .; + __zinitcall_app_feature_start = .; + KEEP (*(.zinitcall.app.feature0.init)) + KEEP (*(.zinitcall.app.feature1.init)) + KEEP (*(.zinitcall.app.feature2.init)) + KEEP (*(.zinitcall.app.feature3.init)) + KEEP (*(.zinitcall.app.feature4.init)) + __zinitcall_app_feature_end = .; + __zinitcall_test_start = .; + KEEP (*(.zinitcall.test0.init)) + KEEP (*(.zinitcall.test1.init)) + KEEP (*(.zinitcall.test2.init)) + KEEP (*(.zinitcall.test3.init)) + KEEP (*(.zinitcall.test4.init)) + __zinitcall_test_end = .; + __zinitcall_exit_start = .; + KEEP (*(.zinitcall.exit0.init)) + KEEP (*(.zinitcall.exit1.init)) + KEEP (*(.zinitcall.exit2.init)) + KEEP (*(.zinitcall.exit3.init)) + KEEP (*(.zinitcall.exit4.init)) + __zinitcall_exit_end = .; +``` + +Adding the preceding content is because external APIs provided by `bootstrap_init` will be saved to the link segment. For details, see `//utils/native/lite/include/ohos_init.h`. + +The following table lists the automatic initialization macros of bootstrap. + +| API | Description | +| ---------------------- | -------------------------------- | +| SYS_SERVICE_INIT(func) | Entry for initializing and starting a core system service.| +| SYS_FEATURE_INIT(func) | Entry for initializing and starting a core system feature.| +| APP_SERVICE_INIT(func) | Entry for initializing and starting an application-layer service. | +| APP_FEATURE_INIT(func) | Entry for initializing and starting an application-layer feature. | + +The **lib** file compiled using the loaded components needs to be manually add to the forcible link. + +​If the `bootstrap_lite` component is configured in `vendor/chipsea/wblink_demo/config.json`: + +``` + { + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite" + }, + ... + ] + }, +``` + +​The `bootstrap_lite` component compiles the `//base/startup/bootstrap_lite/services/source/bootstrap_service.c` file. In this file, `SYS_SERVICE_INIT` is used to inject the `Init` function symbol to `__zinitcall_sys_service_start` and `__zinitcall_sys_service_end`. +``` +static void Init(void) +{ + static Bootstrap bootstrap; + bootstrap.GetName = GetName; + bootstrap.Initialize = Initialize; + bootstrap.MessageHandle = MessageHandle; + bootstrap.GetTaskConfig = GetTaskConfig; + bootstrap.flag = FALSE; + SAMGR_GetInstance()->RegisterService((Service *)&bootstrap); +} +SYS_SERVICE_INIT(Init); --- Forcible link to the generated lib file is required if **SYS_INIT** is used for startup. +``` + +In the `//base/startup/bootstrap_lite/services/source/BUILD.gn` file, add the file to the compilation sources. + +``` +static_library("bootstrap") { + sources = [ + "bootstrap_service.c", + "system_init.c", + ] + .... +``` + +Since the `Init` function does not support explicit calls, you need to forcibly link it to the final image. Configure **ld_flags** in the `device/board/chipsea/cst85_wblink/config.gni` file as follows: + +``` + board_ld_flags += [ + "-Wl,--whole-archive", + "-lexample", + "-lhiview_lite", + "-lhilog_lite", + "-lhievent_lite", + "-lbroadcast", + "-lbootstrap", + "-Wl,--no-whole-archive", + ] + +``` + +### Utils Subsystem + +To adapt the `utils` subsystem, you need to add the `kv_store`, `js_builtin`, `timer_task`, and `kal_timer` components to the `config.json` file. + +``` +{ + "subsystem": "utils", + "components": [ + { + "component": "kv_store", + "features": [ + "enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true" + ] + }, + + ] +}, +``` + +Similar to the adaptation of the `syspara_lite` component, when the `kv_store` component is adapted, the key-value pair is written to the file. In the mini system, file operation APIs include `POSIX` and `HalFiles`. For access to the file system in the kernel, use the `POSIX` API, which means you need to add `enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true` to the `features` field. If you are using the `HalFiles` API, no modification is required. + + +### XTS Subsystem + +For the adaptation to the XTS subsystem, for example, `//vendor/chipsea/xts_demo/config.json`, you need to add the following component options: + +``` +"subsystem": "xts", +"components": [ + { "component": "xts_acts", "features": + [ + "config_ohos_xts_acts_utils_lite_kv_store_data_path = \"/data\"", + "enable_ohos_test_xts_acts_use_thirdparty_lwip = true" + ] + }, + { "component": "xts_tools", "features":[] } +] +``` +The XTS lib needs to be forcibly linked in `device/board/chipsea/cst85_wblink/liteos_m/config.gni`. + +``` + board_ld_flags += [ + "-Wl,--whole-archive", + "-lhctest", + "-lmodule_ActsParameterTest", + "-lmodule_ActsBootstrapTest", + "-lmodule_ActsDfxFuncTest", + "-lmodule_ActsKvStoreTest", + "-lmodule_ActsSamgrTest", + "-lmodule_ActsWifiServiceTest", + "-lmodule_ActsDsoftbusMgrTest", + ] +``` diff --git a/en/device-dev/porting/porting-dayu200-on_standard-demo.md b/en/device-dev/porting/porting-dayu200-on_standard-demo.md new file mode 100644 index 0000000000000000000000000000000000000000..3cf6e1884fdbe5b91c11c0384fd5610a77545f1c --- /dev/null +++ b/en/device-dev/porting/porting-dayu200-on_standard-demo.md @@ -0,0 +1,3497 @@ +# Standard System Solution – Rockchip RK3568 Porting Case + +This 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. + +## Product Configuration and Directory Planning + +### Product Configuration + +Create 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: + +``` +{ + "device_name": "rk3568", + "device_company": "rockchip", + "target_os": "ohos", + "target_cpu": "arm", + "kernel_version": "", + "device_build_path": "device/board/hihope/rk3568", + "enable_ramdisk": true, // Specifies whether to support ramdisk secondary boot. + "build_selinux": true // Indicates whether SELinux permission management is supported. +} +``` + +Create 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: + +``` +{ + "product_name": "rk3568", + "product_company" : "hihope", + "product_device": "rk3568", + "version": "2.0", + "type": "standard", + "parts":{ + "ace:ace_engine_standard":{}, + "ace:napi":{}, + ... + "xts:phone_tests":{} + } +} +``` + +The main configurations are as follows: + +1. **product_device**: SoC used by the product. +2. **type**: system type. In this example, set it to **standard**. +3. **parts**: subsystem to enable. A subsystem can be treated as an independently built functional block. + +You can find predefined subsystems in **//build/subsystem_config.json**. You can also customize subsystems. + +You 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. + +### Directory Planning + +This 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: + +``` +device +├── board --- Board vendor directory +│ └── hihope --- Board vendor +│ └── rk3568 --- Board name, RK3568, which contains driver service code +└── soc --- SoC vendor directory + └── rockchip --- SoC vendor: Rockchip + └── rk3568 --- SoC series: RK3568, mainly solutions provided by the chip vendor and closed-source libraries + + +``` + +``` +vendor +└── hihope + └── rk3568 --- Product name: product, HCS, and demo +``` + +## **Kernel Startup** + +### Secondary Boot + +Unlike 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**. + +RK3568 adaptation is to pack **ramdisk** compiled in the mainline version into **boot_linux.img**. The procedure is as follows: + +1. Enable secondary boot. + + Set **enable_ramdisk** in **productdefine/common/device/rk3568.json**. + + ``` + { + "device_name": "rk3568", + "device_company": "hihope", + "target_os": "ohos", + "target_cpu": "arm", + "kernel_version": "", + "device_build_path": "device/hihope/build", + "enable_ramdisk": true, + "build_selinux": true + } + ``` + +2. Pack the **ramdsik.img** file compiled in the mainline version to **boot_linux.img**. + + View the configuration as follows: + + 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**: + + ``` + function make_extlinux_conf() + { + dtb_path=$1 + uart=$2 + image=$3 + + echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF} + echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF} + echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF} + if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then + echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF} + fi + cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4" + echo " ${cmdline}" >> ${EXTLINUX_CONF} + } + ``` + +### Packing + +Add 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: + +``` +genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img +``` + +For details about modification for calling **make-boot.sh**, see the following: + +https://gitee.com/openharmony/build/pulls/569/files + +### INIT Configuration + +For details about the init configuration, see the [specifications of the startup subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/startup.md). + +## **Audio** + +### Overall structure of the RK3568 audio module + +![dayu200-audio-01.png](figures/dayu200/dayu200-audio-01.png) + +### Introduction to ADM Adaptation Solution + +#### ADM framework adaptation of the RK3568 platform + +![](figures/dayu200/dayu200-audio-02.png) + +1. ADM Drivers adapter + + 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. + +2. ADM Drivers impl + + 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. + +3. Linux Drivers + + 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. + +#### Directory Structure + +``` +./device/board/hihope/rk3568/audio_drivers +├── codec +│ └── rk809_codec +│ ├── include +│ │ ├── rk809_codec_impl.h +│ │ └── rk817_codec.h +│ └── src +│ ├── rk809_codec_adapter.c +│ ├── rk809_codec_linux_driver.c +│ └── rk809_codec_ops.c +├── dai +│ ├── include +│ │ ├── rk3568_dai_linux.h +│ │ └── rk3568_dai_ops.h +│ └── src +│ ├── rk3568_dai_adapter.c +│ ├── rk3568_dai_linux_driver.c +│ └── rk3568_dai_ops.c +├── dsp +│ ├── include +│ │ └── rk3568_dsp_ops.h +│ └── src +│ ├── rk3568_dsp_adapter.c +│ └── rk3568_dsp_ops.c +├── include +│ ├── audio_device_log.h +│ └── rk3568_audio_common.h +└── soc + ├── include + │ └── rk3568_dma_ops.h + └── src + ├── rk3568_dma_adapter.c + └── rk3568_dma_ops.c +``` + +### Detailed Process of Adapting RK3568 to ADM + +#### Audio Framework Sorting + +Sort out the audio structure of the target platform and specify the data stream and control stream path. + +1. 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. +2. Sort out the hardware information such as the I2S channel ID, corresponding pin ID, I2C channel ID, and address based on the schematic diagram. +3. 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). + +#### ADM Structure + +The following figure shows the ADM structure. Audio Peripheral Drivers and Platform Drivers are required for platform adaptation. + +![dayu200-audio-03.png](figures/dayu200/dayu200-audio-03.png) + +Based 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. + +| Driver to Adapt| ADM Module| API File Path | +| -------------- | ----------- | ---------------------------------------------------- | +| RK809 driver | Accessory | drivers/framework/include/audio/audio_accessory_if.h | +| DMA driver | platform | drivers/framework/include/audio/audio_platform_if.h | +| I2S driver | DAI | drivers/framework/include/audio/audio_dai_if.h.h | + +#### Driver Code Framework Setup + +##### Configuring the HCS File + +Register the driver node under **audio** in the **device_info.hcs** file. + +```c + audio :: host { + hostName = "audio_host"; + priority = 60; + device_dai0 :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "DAI_RK3568"; + serviceName = "dai_service"; + deviceMatchAttr = "hdf_dai_driver"; + } + } + device_codec :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "CODEC_RK809"; + serviceName = "codec_service_0"; + deviceMatchAttr = "hdf_codec_driver"; + } + } + device_codec_ex :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "CODEC_RK817"; + serviceName = "codec_service_1"; + deviceMatchAttr = "hdf_codec_driver_ex"; + } + } + device_dsp :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "DSP_RK3568"; + serviceName = "dsp_service_0"; + deviceMatchAttr = "hdf_dsp_driver"; + } + } + device_dma :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "DMA_RK3568"; + serviceName = "dma_service_0"; + deviceMatchAttr = "hdf_dma_driver"; + } + } + ...... + } + +``` + +Select 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. + +For 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). + +##### Codec/Accessory Module + +1. Register the driver with the HDF framework. The code snippet is as follows. The **moduleName** is the same as that in the HCS file. + + ``` + struct HdfDriverEntry g_codecDriverEntry = { + .moduleVersion = 1, + .moduleName = "CODEC_HI3516", + .Bind = CodecDriverBind, + .Init = CodecDriverInit, + .Release = CodecDriverRelease, + }; + HDF_INIT(g_codecDriverEntry); + ``` + +2. Fill the Codec module with: + + **g_codecData**: operation function set and private data set of the codec device. + + **g_codecDaiDeviceOps**: codec DAI device operation function set, including APIs for starting transmission and setting parameters. + + **g_codecDaiData**: operation function set and private data set of the digital audio API of the codec. + +3. Implement the bind, init, and release functions. + +4. Verification + + Add debug logs to the bind and init functions, compile the version, and obtain system logs. + + ``` + [ 1.548624] [E/"rk809_codec_adapter"] [Rk809DriverBind][line:258]: enter + [ 1.548635] [E/"rk809_codec_adapter"] [Rk809DriverBind][line:260]: success + [ 1.548655] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:270]: enter + [ 1.549050] [E/"rk809_codec_adapter"] [GetServiceName][line:226]: enter + [ 1.549061] [E/"rk809_codec_adapter"] [GetServiceName][line:250]: success + [ 1.549072] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:316]: g_chip->accessory.drvAccessoryName = codec_service_1 + [ 1.549085] [E/audio_core] [AudioSocRegisterDai][line:86]: Register [accessory_dai] success. + [ 1.549096] [E/audio_core] [AudioRegisterAccessory][line:120]: Register [codec_service_1] success. + [ 1.549107] [E/"rk809_codec_adapter"] [Rk809DriverInit][line:323]: success! + ``` + + + +##### DAI Module + +1. 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. + + ```c + struct HdfDriverEntry g_daiDriverEntry = { + .moduleVersion = 1, + .moduleName = "DAI_RK3568", + .Bind = DaiDriverBind, + .Init = DaiDriverInit, + .Release = DaiDriverRelease, + }; + HDF_INIT(g_daiDriverEntry); + ``` + +2. Fill the DAI module with: + + ```c + struct AudioDaiOps g_daiDeviceOps = { + .Startup = Rk3568DaiStartup, + .HwParams = Rk3568DaiHwParams, + .Trigger = Rk3568NormalTrigger, + }; + + struct DaiData g_daiData = { + .Read = Rk3568DeviceReadReg, + .Write = Rk3568DeviceWriteReg, + .DaiInit = Rk3568DaiDeviceInit, + .ops = &g_daiDeviceOps, + }; + ``` + +3. Implement the bind, init, and release functions. + +4. Verification + + Add debug logs to the bind and init functions, compile the version, and obtain system logs. + + ``` + [ 1.549193] [I/device_node] launch devnode dai_service + [ 1.549204] [E/HDF_LOG_TAG] [DaiDriverBind][line:38]: entry! + [ 1.549216] [E/HDF_LOG_TAG] [DaiDriverBind][line:55]: success! + [ 1.549504] [E/audio_core] [AudioSocRegisterDai][line:86]: Register [dai_service] success. + [ 1.549515] [E/HDF_LOG_TAG] [DaiDriverInit][line:116]: success. + ``` + +##### Platform Module + +1. 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. + + ``` + struct HdfDriverEntry g_platformDriverEntry = { + .moduleVersion = 1, + .moduleName = "DMA_RK3568", + .Bind = PlatformDriverBind, + .Init = PlatformDriverInit, + .Release = PlatformDriverRelease, + }; + HDF_INIT(g_platformDriverEntry); + ``` + +2. Fill the DMA module with: + + ```c + struct AudioDmaOps g_dmaDeviceOps = { + .DmaBufAlloc = Rk3568DmaBufAlloc, + .DmaBufFree = Rk3568DmaBufFree, + .DmaRequestChannel = Rk3568DmaRequestChannel, + .DmaConfigChannel = Rk3568DmaConfigChannel, + .DmaPrep = Rk3568DmaPrep, + .DmaSubmit = Rk3568DmaSubmit, + .DmaPending = Rk3568DmaPending, + .DmaPause = Rk3568DmaPause, + .DmaResume = Rk3568DmaResume, + .DmaPointer = Rk3568PcmPointer, + }; + + struct PlatformData g_platformData = { + .PlatformInit = AudioDmaDeviceInit, + .ops = &g_dmaDeviceOps, + }; + ``` + +3. Implement the bind, init, and release functions. + +4. Verification + + Add debug logs to the bind and init functions, compile the version, and obtain system logs. + + ``` + [ 1.548469] [E/rk3568_platform_adapter] [PlatformDriverBind][line:42]: entry! + [ 1.548481] [E/rk3568_platform_adapter] [PlatformDriverBind][line:58]: success! + [ 1.548492] [E/rk3568_platform_adapter] [PlatformDriverInit][line:100]: entry. + [ 1.548504] [E/rk3568_platform_adapter] [PlatformGetServiceName][line:67]: entry! + [ 1.548515] [E/rk3568_platform_adapter] [PlatformGetServiceName][line:91]: success! + [ 1.548528] [E/audio_core] [AudioSocRegisterPlatform][line:63]: Register [dma_service_0] success. + [ 1.548536] [E/rk3568_platform_adapter] [PlatformDriverInit][line:119]: success. + ``` + +#### Driver Adaptation + +##### Codec/Accessory Module + +1. Read the DTS file to obtain the corresponding device node, and use the native driver registration function of Linux to obtain the corresponding device. + + ``` + static int rk817_platform_probe(struct platform_device *pdev) { + rk817_pdev = pdev; + dev_info(&pdev->dev, "got rk817-codec platform_device"); + return 0; + } + + static struct platform_driver rk817_codec_driver = { + .driver = { + .name = "rk817-codec", // codec node in dts file + .of_match_table = rk817_codec_dt_ids, + }, + .probe = rk817_platform_probe, + .remove = rk817_platform_remove, + }; + ``` + +2. Encapsulate the functions for reading and writing registers. + Use the **regmap** function of Linux based on the obtained device. You do not need to obtain the base address of the module. + Obtain the regmap code snippet of RK817. + + ``` + g_chip = devm_kzalloc(&rk817_pdev->dev, sizeof(struct Rk809ChipData), GFP_KERNEL); + if (!g_chip) { + AUDIO_DEVICE_LOG_ERR("no memory"); + return HDF_ERR_MALLOC_FAIL; + } + g_chip->pdev = rk817_pdev; + + struct rk808 *rk808 = dev_get_drvdata(g_chip->pdev->dev.parent); + if (!rk808) { + AUDIO_DEVICE_LOG_ERR("%s: rk808 is NULL\n", __func__); + ret = HDF_FAILURE; + RK809ChipRelease(); + return ret; + } + g_chip->regmap = devm_regmap_init_i2c(rk808->i2c, + &rk817_codec_regmap_config); + if (IS_ERR(g_chip->regmap)) { + AUDIO_DEVICE_LOG_ERR("failed to allocate regmap: %ld\n", PTR_ERR(g_chip->regmap)); + RK809ChipRelease(); + return ret; + } + ``` + + Code snippet of read and write functions of the register + + ``` + int32_t Rk809DeviceRegRead(uint32_t reg, uint32_t *val) + { + if (regmap_read(g_chip->regmap, reg, val)) { + AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg); + return HDF_FAILURE; + } + + return HDF_SUCCESS; + } + + int32_t Rk809DeviceRegWrite(uint32_t reg, uint32_t value) { + if (regmap_write(g_chip->regmap, reg, value)) { + AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value); + return HDF_FAILURE; + } + + return HDF_SUCCESS; + } + + int32_t Rk809DeviceRegUpdatebits(uint32_t reg, uint32_t mask, uint32_t value) { + if (regmap_update_bits(g_chip->regmap, reg, mask, value)) { + AUDIO_DRIVER_LOG_ERR("update register bits fail: [%04x] = %04x", reg, value); + return HDF_FAILURE; + } + + return HDF_SUCCESS; + } + ``` + +3. Define the register Initialization function. + + 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. + + RK809RegDefaultInit code snippet + + ```c + int32_t RK809RegDefaultInit(struct AudioRegCfgGroupNode **regCfgGroup) + { + int32_t i; + struct AudioAddrConfig *regAttr = NULL; + + if (regCfgGroup == NULL || regCfgGroup[AUDIO_INIT_GROUP] == NULL || + regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem == NULL || regCfgGroup[AUDIO_INIT_GROUP]->itemNum <= 0) { + AUDIO_DEVICE_LOG_ERR("input invalid parameter."); + + return HDF_ERR_INVALID_PARAM; + } + + regAttr = regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem; + + for (i = 0; i < regCfgGroup[AUDIO_INIT_GROUP]->itemNum; i++) { + Rk809DeviceRegWrite(regAttr[i].addr, regAttr[i].value); + } + + return HDF_SUCCESS; + } + ``` + +4. Encapsulate the read and write functions of the control API. + + Set the control read/write functions to **RK809CodecReadReg** and **RK809CodecWriteReg**. + + ```c + struct CodecData g_rk809Data = { + .Init = Rk809DeviceInit, + .Read = RK809CodecReadReg, + .Write = RK809CodecWriteReg, + }; + + struct AudioDaiOps g_rk809DaiDeviceOps = { + .Startup = Rk809DaiStartup, + .HwParams = Rk809DaiHwParams, + .Trigger = RK809NormalTrigger, + }; + + struct DaiData g_rk809DaiData = { + .DaiInit = Rk809DaiDeviceInit, + .ops = &g_rk809DaiDeviceOps, + }; + ``` + + Encapsulate the read and write functions of the control API. + + 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: + + ```c + int32_t RK809CodecReadReg(unsigned long virtualAddress,uint32_t reg, uint32_t *val) + { + if (val == NULL) { + AUDIO_DRIVER_LOG_ERR("param val is null."); + return HDF_FAILURE; + } + if (Rk809DeviceRegRead(reg, val)) { + AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg); + return HDF_FAILURE; + } + ADM_LOG_ERR("read reg 0x[%02x] = 0x[%02x]",reg,*val); + return HDF_SUCCESS; + } + + int32_t RK809CodecWriteReg(unsigned long virtualAddress,uint32_t reg, uint32_t value) + { + if (Rk809DeviceRegWrite(reg, value)) { + AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value); + return HDF_FAILURE; + } + ADM_LOG_ERR("write reg 0x[%02x] = 0x[%02x]",reg,value); + return HDF_SUCCESS; + } + ``` + +5. For other OPS functions: + + - **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. + - **Rk809DaiStartup**: Read the HCS file, and configure the control register of the codec/accessory. + - **Rk809DaiHwParams**: Configure the corresponding register based on the audio attributes (such as the sampling rate, format, and channel) delivered by the HAL. + - **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. + +##### DAI (i2s) Module + +1. Read and write registers. + + 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. + + ```c + int32_t Rk3568DeviceReadReg(unsigned long regBase, uint32_t reg, uint32_t *val) + { + AUDIO_DEVICE_LOG_ERR("entry"); + (void)regBase; + struct device_node *dmaOfNode = of_find_node_by_path("/i2s@fe410000"); + if(dmaOfNode == NULL) { + AUDIO_DEVICE_LOG_ERR("of_node is NULL."); + } + struct platform_device *platformdev = of_find_device_by_node(dmaOfNode); + struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&platformdev->dev); + + (void)regBase; + if (regmap_read(i2s_tdm->regmap, reg, val)) { + AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", reg); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + int32_t Rk3568DeviceWriteReg(unsigned long regBase, uint32_t reg, uint32_t value) + { + AUDIO_DEVICE_LOG_ERR("entry"); + (void)regBase; + struct device_node *dmaOfNode = of_find_node_by_path("/i2s@fe410000"); + if(dmaOfNode == NULL) { + AUDIO_DEVICE_LOG_ERR("of_node is NULL."); + } + struct platform_device *platformdev = of_find_device_by_node(dmaOfNode); + struct rk3568_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&platformdev->dev); + if (regmap_write(i2s_tdm->regmap, reg, value)) { + AUDIO_DEVICE_LOG_ERR("write register fail: [%04x] = %04x", reg, value); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + +2. For other OPS functions: + + - Rk3568DaiDeviceInit + + Original framework, which reads the **DAI_config.hcs** parameter list and works with HwParams to set parameters. + + + - Rk3568DaiHwParams + + Configure the I2S MCLK/BCLK/LRCLK clocks. + + + 1. Calculate the MCLK based on different sampling rates. + + ```c + int32_t RK3568I2sTdmSetSysClk(struct rk3568_i2s_tdm_dev *i2s_tdm, const struct AudioPcmHwParams *param) + { + /* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */ + uint32_t sampleRate = param->rate; + uint32_t mclk_parent_freq = 0; + switch (sampleRate) { + case AUDIO_DEVICE_SAMPLE_RATE_8000: + case AUDIO_DEVICE_SAMPLE_RATE_16000: + case AUDIO_DEVICE_SAMPLE_RATE_24000: + case AUDIO_DEVICE_SAMPLE_RATE_32000: + case AUDIO_DEVICE_SAMPLE_RATE_48000: + case AUDIO_DEVICE_SAMPLE_RATE_64000: + case AUDIO_DEVICE_SAMPLE_RATE_96000: + mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_192000; + break; + case AUDIO_DEVICE_SAMPLE_RATE_11025: + case AUDIO_DEVICE_SAMPLE_RATE_22050: + case AUDIO_DEVICE_SAMPLE_RATE_44100: + + mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_176400; + break; + default: + AUDIO_DEVICE_LOG_ERR("Invalid LRCK freq: %u Hz\n", sampleRate); + return HDF_FAILURE; + } + i2s_tdm->mclk_tx_freq = mclk_parent_freq; + i2s_tdm->mclk_rx_freq = mclk_parent_freq; + + return HDF_SUCCESS; + } + ``` + + 2. Calculate the BCLK/LRclk frequency division coefficient based on the obtained MCLK. + + - Rk3568NormalTrigger + + Complete a series of configurations based on the input and output types and commands (start/stop/pause/resume). + + + 1. Start and stop the MCLK. + 2. Start and stop DMA transfer. + 3. Start and stop transmission. + See the code for detailed implementation, and refer to the API functions of the native Linux I2S driver. + + ```c + // Start or restore the process. + if (streamType == AUDIO_RENDER_STREAM) { + clk_prepare_enable(i2s_tdm->mclk_tx); + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_TDE_ENABLE, + I2S_DMACR_TDE_ENABLE); + } else { + clk_prepare_enable(i2s_tdm->mclk_rx); + regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, + I2S_DMACR_RDE_ENABLE, + I2S_DMACR_RDE_ENABLE); + if (regmap_read(i2s_tdm->regmap, I2S_DMACR, &val)) { + AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", I2S_DMACR); + return ; + } + AUDIO_DEVICE_LOG_ERR("i2s reg: 0x%x = 0x%x ", I2S_DMACR, val); + } + + if (atomic_inc_return(&i2s_tdm->refcount) == 1) { + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START); + if (regmap_read(i2s_tdm->regmap, I2S_XFER, &val)) { + AUDIO_DEVICE_LOG_ERR("read register fail: [%04x]", I2S_XFER); + return ; + } + AUDIO_DEVICE_LOG_ERR("i2s reg: 0x%x = 0x%x ", I2S_XFER, val); + } + ``` +##### Platform (DMA) Module + +For other OPS functions: + +1. Rk3568DmaBufAlloc/Rk3568DmaBufFree + + 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. + +2. Rk3568DmaRequestChannel + + Use the native Linux DMA API function to obtain the DMA transfer channel **dma_request_slave_channel**. + + ``` + dmaRtd->dmaChn[streamType] = dma_request_slave_channel(dmaDevice, dmaChannelNames[streamType]); + ``` + +3. Rk3568DmaConfigChannel + + ``` + // Set channel parameters. + // Set voice playing channel parameters. + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.dst_addr = I2S1_ADDR + I2S_TXDR; + slave_config.dst_maxburst = 8; + // Set recording channel parameters. + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.src_addr = I2S1_ADDR + I2S_RXDR; + slave_config.src_maxburst = 8; + + // Use the native Linux DMA API function to configure the DMA channel. + ret = dmaengine_slave_config(dmaChan, &slave_config); + if (ret != 0) { + AUDIO_DEVICE_LOG_ERR("dmaengine_slave_config failed"); + return HDF_FAILURE; + } + ``` + +4. Rk3568DmaSubmit/Rk3568DmaPending + + 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. + +5. Rk3568PcmPointer + + After step 4 is complete, the ADM framework calls Rk3568PcmPointer to cyclically write CirBuf and calculate the pointer. + + ``` + + dma_chn = dmaRtd->dmaChn[DMA_TX_CHANNEL]; + buf_size = data->renderBufInfo.cirBufSize; + dmaengine_tx_status(dma_chn, dmaRtd->cookie[DMA_TX_CHANNEL], &dma_state); + if (dma_state.residue) { + currentPointer = buf_size - dma_state.residue; + *pointer = BytesToFrames(data->pcmInfo.frameSize, currentPointer); + } else { + *pointer = 0; + } + ``` + +6. Rk3568DmaPause + + Use the native Linux DMA API function **dmaengine_terminate_async** to stop DMA transfer. + + ``` + dmaengine_terminate_async(dmaChan); + ``` + +7. Rk3568DmaResume + + Restart DMA transfer. You can perform operations related to **Rk3568DmaSubmit/Rk3568DmaPending**. + +##### FAQs for Adaptation + +1. After the audio plays for a period of time, the audio stops playing and there is a sharp and small sound. + Cause: After the playback stops, the components related to the codec are not powered off. + Solution: Register the **trigger** function of the codec. When the received command is **Stop**, power off the codec. + +2. After the audio plays for a period of time and stops, no sound can be heard when the playback is resumed. + Cause: The **PAUSE** API function of the DMA driver does not stop DMA transfer. + 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**. + +3. Noise occurs during playback. + Cause: The pointer position during DMA data transfer is incorrect. + 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**. + +4. The audio can be played, but the MCLK pin does not have clock signals. + Cause: The pin-ctrl in the DTS file is not configured with the MCLK pin. + Solution: Modify the DTS file. + +### Camera + +**Basic Concepts** + +The 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: + +1. HDI implementation layer: implements standard device APIs of OHOS cameras. + +2. Framework layer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. + +3. Adaptation layer: shields the differences between bottom-layer chips and OSs for multi-platform adaptation. + +### Camera Driver Framework + +#### Source Code Framework + +The camera driver framework is stored in **drivers_peripheral**, and the source code directory is **drivers/peripheral/camera**. + +``` +|-- README_zh.md +|-- figures +| -- logic-view-of-modules-related-to-this-repository_zh.png +|-- hal +| |-- BUILD.gn # Entry for building the camera driver framework +| |-- adapter # Platform adaptation layer +| |-- buffer_manager +| |-- camera.gni # Global variables used by the component +| |-- device_manager +| |-- hdi_impl +| |-- include +| |-- init #demo sample +| |-- pipeline_core +| |-- test # Test code +| |-- utils +|-- hal_c # Dedicated C API for HiSilicon +| |-- BUILD.gn +| |-- camera.gni +| |-- hdi_cif +| |-- include +|-- interfaces # HDI APIs + |-- hdi_ipc +|-- hdi_passthrough + |-- include +``` + +The 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**. + + ├── hdi_impl + │ └── camera_host_config.hcs + └── pipeline_core + ├── config.hcs + ├── ipp_algo_config.hcs + └── params.hcs +The code repository related to the camera chipset of RK3568 is **device_hihope**. Path: device/board/hihope/rk3568/camera/ +``` +├── BUILD.gn +├── demo +│ └── include +│ └── project_camera_demo.h +├── device_manager +│ ├── BUILD.gn +│ ├── include +│ │ ├── imx600.h +│ │ ├── project_hardware.h +│ │ └── rkispv5.h +│ └── src +│ ├── imx600.cpp +│ └── rkispv5.cpp +├── driver_adapter +│ └── test +│ ├── BUILD.gn +│ ├── unittest +│ │ ├── include +│ │ │ └── utest_v4l2_dev.h +│ │ └── src +│ │ └── utest_v4l2_dev.cpp +│ └── v4l2_test +│ └── include +│ └── project_v4l2_main.h +└── pipeline_core + ├── BUILD.gn + └── src + ├── ipp_algo_example + │ └── ipp_algo_example.c + └── node + ├── rk_codec_node.cpp + └── rk_codec_node.h +``` + #### Camera Driver Framework Configuration + +Path of the RK3568 configuration file: "vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs". + +For other platforms, refer to the RK3568 adaptation. + +``` + hdi_server :: host { + hostName = "camera_host"; + priority = 50; + caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; + camera_device :: device { + device0 :: deviceNode { + policy = 2; + priority = 100; + moduleName = "libcamera_hdi_impl.z.so"; + serviceName = "camera_service"; + } + } + ... + } +``` + +Parameter description: + +**Host**: A host node is an independent process. If an independent process is required, add a host node. + +**Policy**: service publish policy. Set this parameter to **2** for the HDI service. + +**moduleName**: name of the driver implementation library. + +**serviceName**: service name, which must be globally unique. + +Entry for implementing the Camera_host driver + +File path: drivers/peripheral/camera/interfaces/hdi_ipc/server/src/camera_host_driver.cpp + +Dispatch device service messages. + +**cmd Id:** command ID of the request. + +**Data:** pointer to other services or I/O requests. + +**Reply:** pointer to the content of the returned message. + +``` +static int32_t CameraServiceDispatch(struct HdfDeviceIoClient *client, int cmdId, + struct HdfSBuf *data, struct HdfSBuf *reply) +{ + HdfCameraService *hdfCameraService = CONTAINER_OF(client->device->service, HdfCameraService, ioservice); + return CameraHostServiceOnRemoteRequest(hdfCameraService->instance, cmdId, data, reply); + } +``` + +Bind a device service: initializes the device service object and resource object. + +``` +int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject) +{ + HDF_LOGI("HdfCameraHostDriverBind enter!"); + if (deviceObject == nullptr) { + HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !"); + return HDF_FAILURE; +} +``` + +Driver initialization function: detects and initializes the driver. + +``` +int HdfCameraHostDriverInit(struct HdfDeviceObject *deviceObject) +{ + return HDF_SUCCESS; +} +``` + +Driver resource release function: releases the bound device service object. + +``` + void HdfCameraHostDriverRelease(HdfDeviceObject *deviceObject) + { + if (deviceObject == nullptr || deviceObject->service == nullptr) { + HDF_LOGE("%{public}s deviceObject or deviceObject->service is NULL!", __FUNCTION__); + return; + } + HdfCameraService *hdfCameraService = CONTAINER_OF(deviceObject->service, HdfCameraService, ioservice); + if (hdfCameraService == nullptr) { + HDF_LOGE("%{public}s hdfCameraService is NULL!", __FUNCTION__); + return; + } +``` + +Define the driver descriptor: registers the driver code with the driver framework. + + + struct HdfDriverEntry g_cameraHostDriverEntry = { + .moduleVersion = 1, + .moduleName = "camera_service", + .Bind = HdfCameraHostDriverBind, + .Init = HdfCameraHostDriverInit, + .Release = HdfCameraHostDriverRelease, + }; + + +#### Camera Configuration + +In 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. + + + hc_gen("build_camera_host_config") { + sources = [ rebase_path( + "$camera_product_name_path/hdf_config/uhdf/camera/hdi_impl/camera_host_config.hcs") ] + } + + ohos_prebuilt_etc("camera_host_config.hcb") { + deps = [ ":build_camera_host_config" ] + hcs_outputs = get_target_outputs(":build_camera_host_config") + source = hcs_outputs[0] + relative_install_dir = "hdfconfig" + install_images = [ chipset_base_dir ] + subsystem_name = "hdf" + part_name = "camera_device_driver" + } + +### Camera Adaptation + +#### New Product Platform Adaptation + + +In 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. + + if (defined(ohos_lite)) { + import("//build/lite/config/component/lite_component.gni") + import( + "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") + } else { + import("//build/ohos.gni") + if ("${product_name}" == "ohos-arm64") { + import( + "//drivers/peripheral/camera/hal/adapter/chipset/rpi/rpi3/device/camera/product.gni") + } else if ("${product_name}" == "Hi3516DV300") { + import( + "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") + } else if ("${product_name}" == "watchos") { + import( + "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") + } else { + import( + "//device/board/${product_company}/${device_name}/camera/product.gni") + } + } + +The **product.gni** file in the following path specifies the path for compiling the code related to different chipsets: + +``` + device/${product_company}/${device_name}/camera/ +``` + +The following is the **product.gni** file content of RK3568: + + camera_device_name_path = "//device/board/${product_company}/${device_name}" + is_support_v4l2 = true + if (is_support_v4l2) { + is_support_mpi = false + defines += [ "SUPPORT_V4L2" ] + chipset_build_deps = "$camera_device_name_path/camera/:chipset_build" + camera_device_manager_deps = + "$camera_device_name_path/camera/src/device_manager:camera_device_manager" + camera_pipeline_core_deps = + "$camera_device_name_path/camera/src/pipeline_core:camera_pipeline_core" + } + +Three 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**. + +#### Framework Adaptation + + ![dayu200-camera-01.png](figures/dayu200/dayu200-camera-01.png) +​ + +Take 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. + 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. + +A 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. + +The controller in deviceManager directly interacts with the driver adaptation layer. + +Based 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. + +The following directories are added: + +- **vendor/hihope/rk3568/hdf_config/uhdf/camera/**: HCS configuration file directory of the current chip product. + +- **device/hihope/rk3568/camera/**: code adaptation directory of the current chip product. + +- **drivers/peripheral/camera/hal/adapter/platform/v4l2**: common platform code. + +#### HCS Configuration File Adaptation + +``` + ├── hdi_impl + │ └── camera_host_config.hcs + └── pipeline_core + ├── config.hcs + ├── ipp_algo_config.hcs + └── params.hcs +``` + +Take the RK3568 development board as an example. The HCS file must be stored in the corresponding path. + +``` + vendor/${product_company}/${product_name}/ hdf_config/uhdf/camera/ +``` + + ``` + template ability { + logicCameraId = "lcam001"; + physicsCameraIds = [ + "CAMERA_FIRST", + "CAMERA_SECOND" + ]; + metadata { + aeAvailableAntiBandingModes = [ + "OHOS_CONTROL_AE_ANTIBANDING_MODE_OFF", + "OHOS_CONTROL_AE_ANTIBANDING_MODE_50HZ", + "OHOS_CONTROL_AE_ANTIBANDING_MODE_60HZ", + "OHOS_CONTROL_AE_ANTIBANDING_MODE_AUTO" + ]; + + ``` + +The **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**. + +``` + normal_preview :: pipeline_spec { + name = "normal_preview"; + v4l2_source :: node_spec { + name = "v4l2_source#0"; + status = "new"; + out_port_0 :: port_spec { + name = "out0"; + peer_port_name = "in0"; + peer_port_node_name = "sink#0"; + direction = 1; + width = 0; + height = 0; + format = 0; + } + } + sink :: node_spec { + name = "sink#0"; + status = "new"; + stream_type = "preview"; + in_port_0 :: port_spec { + name = "in0"; + peer_port_name = "out0"; + peer_port_node_name = "v4l2_source#0"; + direction = 0; + } + } + } +``` + + The **config.hcs** file under **pipeline_core** uses the pipeline connection mode. Nodes of each flow and connection modes are classified by scenario. + +The 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. + + 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. + +When 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**. + + + root { + module = ""; + template stream_info { + id = 0; + name = ""; + } + template scene_info { + id = 0; + name = ""; + } + preview :: stream_info { + id = 0; + name = "preview"; + } + video :: stream_info { + id = 1; + name = "video"; + } + + +The **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. + +#### Chipset and Platform Adaptation + +The 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: + + drivers/peripheral/camera/hal/adapter/platform + ├── mpp + │ └── src + │ ├── device_manager + │ └── pipeline_core + └── v4l2 + └── src + ├── device_manager + ├── driver_adapter + └── pipeline_core + +The **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**. + +Nodes 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**. + + + namespace OHOS::Camera { + class V4L2SourceNode : public SourceNode { + public: + V4L2SourceNode(const std::string& name, const std::string& type); + ~V4L2SourceNode() override; + RetCode Init(const int32_t streamId) override; + RetCode Start(const int32_t streamId) override; + RetCode Flush(const int32_t streamId) override; + RetCode Stop(const int32_t streamId) override; + RetCode GetDeviceController(); + void SetBufferCallback() override; + RetCode ProvideBuffers(std::shared_ptr frameSpec) override; + + private: + std::mutex requestLock_; + std::map> captureRequests_ = {}; + std::shared_ptr sensorController_ = nullptr; + std::shared_ptr deviceManager_ = nullptr; + }; + } // namespace OHOS::Camera + + **Init**: initializes modules. + +**Start**: enables functions, such as start stream. + + **Stop**: disables functions. + + **GetDeviceController** is the controller API for obtaining the deviceManager. + +Chipset 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. + +``` + device/board/hihope/rk3568/camera + ├── BUILD.gn + ├── camera_demo + │ └── project_camera_demo.h + ├── include + │ └── device_manager + ├── product.gni + └── src + ├── device_manager + ├── driver_adapter + └── pipeline_core +``` + +The **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. + +The 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. + + ![img](figures/dayu200/dayu200-camera-02.png) + +Key APIs that need to be implemented in DeviceManager: + +``` + class SensorController : public IController { + public: + SensorController(); + explicit SensorController(std::string hardwareName); + virtual ~SensorController(); + RetCode Init(); + RetCode PowerUp(); + RetCode PowerDown(); + RetCode Configure(std::shared_ptr meta); + RetCode Start(int buffCont, DeviceFormat& format); + RetCode Stop(); + RetCode SendFrameBuffer(std::shared_ptr buffer); + void SetNodeCallBack(const NodeBufferCb cb); + void SetMetaDataCallBack(const MetaDataCb cb); + void BufferCallback(std::shared_ptr buffer); + void SetAbilityMetaDataTag(std::vector abilityMetaDataTag); + } +``` + + **PowerUp** is used for power-on. You can call this API to power on a device for OpenCamera. + **PowerDown** is used for power-off. You can call this API to power off a device for CloseCamera. + 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. + 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. + **Stop** and **Start** are reverse operations. You can call **Stop** for a stop operation. + **SendFrameBuffer** is an API for delivering the buffer of each frame. All buffer interaction operations with the driver are performed through this API. + SetNodeCallBack is a pipeline. This API is used to set the buffer callback function to DeviceManager. + SetMetaDataCallBack is a metadata callback. This API is used to report the metadata obtained from the bottom layer to the upper layer. + BufferCallback is used to upload the filled data buffer of each frame. This API is used to report the buffer to the pipeline. + **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. + + For details about other APIs, see **drivers/peripheral/camera/hal/adapter/platform/v4l2/src/device_manager/**. + +#### IPP Adaptation + +The 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: +vendor/${product_company}/${product_name}/hdf_config/uhdf/camera/pipeline_core/ipp_algo_config.hcs + +``` + root { + module="sample"; + ipp_algo_config { + algo1 { + name = "example"; + description = "example algorithm"; + path = "libcamera_ipp_algo_example.z.so"; + mode = "IPP_ALGO_MODE_NORMAL"; + } + } + } + +``` + + **name**: algorithm plugin name. + **description**: functions of the algorithm plugin. + **path**: path of the algorithm plugin. + **mode**: running mode of the algorithm plugin. + +The 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. + +``` + enum IppAlgoMode { + IPP_ALGO_MODE_BEGIN, + IPP_ALGO_MODE_NORMAL = IPP_ALGO_MODE_BEGIN, + IPP_ALGO_MODE_BEAUTY, + IPP_ALGO_MODE_HDR, + IPP_ALGO_MODE_END + }; +``` + +The 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: + + typedef struct IppAlgoFunc { + int (*Init)(IppAlgoMeta* meta); + int (*Start)(); + int (*Flush)(); + int (*Process)(IppAlgoBuffer* inBuffer[], int inBufferCount, IppAlgoBuffer* outBuffer, IppAlgoMeta* meta); + int (*Stop)(); + } IppAlgoFunc; + + 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. + 2) **Start**: called by the ippnode for the start operation. + 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. + 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**. + 5) **Stop**: called by the ippnode for the stop operation. + + +``` +typedef struct IppAlgoBuffer { + void* addr; + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int size; + int id; + } IppAlgoBuffer; +``` + +In 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. +For 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. +For 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. +For 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. + +For details about the port configuration of the ippnode, see the description of **config.hcs** in section 3.3. + +#### V4L2 Driver Adaptation Instance + +This section describes how to adapt the RK3568 development board in the v4l2 framework. + + 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. + +``` + drivers/peripheral/camera/hal/adapter/platform/v4l2/src/ + ├── device_manager + │ ├── enumerator_manager.cpp + │ ├── flash_controller.cpp + │ ├── flash_manager.cpp + │ ├── idevice_manager.cpp + │ ├── include + │ ├── isp_controller.cpp + │ ├── isp_manager.cpp + │ ├── sensor_controller.cpp + │ ├── sensor_manager.cpp + │ └── v4l2_device_manager.cpp + ├── driver_adapter + │ ├── BUILD.gn + │ ├── include + │ ├── main_test + │ └── src + └── pipeline_core + └── nodes +``` + + Distinguish the code related to the V4L2 chipset and place it in the **device/ ${product_company}/${device_name} /camera** directory. + +``` + ├── BUILD.gn + ├── camera_demo + │ └── project_camera_demo.h + ├── include + │ └── device_manager + ├── product.gni + └── src + ├── device_manager + ├── driver_adapter + └── pipeline_core +``` + +The **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: + +``` + namespace OHOS::Camera { + std::vector hardware = { + {CAMERA_FIRST, DM_M_SENSOR, DM_C_SENSOR, (std::string) "rkisp_v5"}, + {CAMERA_FIRST, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, + {CAMERA_FIRST, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"}, + {CAMERA_SECOND, DM_M_SENSOR, DM_C_SENSOR, (std::string) "Imx600"}, + {CAMERA_SECOND, DM_M_ISP, DM_C_ISP, (std::string) "isp"}, + {CAMERA_SECOND, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"} + }; + } // namespace OHOS::Camera +``` + +You 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**. + +``` + camera_product_name_path = "//vendor/${product_company}/${product_name}" + camera_device_name_path = "//device/board/${product_company}/${device_name}" + is_support_v4l2 = true + if (is_support_v4l2) { + is_support_mpi = false + defines += [ "SUPPORT_V4L2" ] + chipset_build_deps = "$camera_device_name_path/camera/:chipset_build" + camera_device_manager_deps = + "$camera_device_name_path/camera/src/device_manager:camera_device_manager" + camera_pipeline_core_deps = + "$camera_device_name_path/camera/src/pipeline_core:camera_pipeline_core" + } +``` + +When **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. + + ``` + import("//build/ohos.gni") + if ("${product_name}" == "ohos-arm64") { + import( + "//drivers/peripheral/camera/hal/adapter/chipset/rpi/rpi3/device/camera/product.gni") + } else if ("${product_name}" == "Hi3516DV300") { + import( + "//device/soc/hisilicon/common/hal/media/camera/hi3516dv300/linux_standard/camera/product.gni") + ``` + + 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**. + + print("product_name : , ${product_name}") + group("camera_hal") { + if (is_standard_system) { + deps = [ + "$camera_path/../interfaces/hdi_ipc/client:libcamera_client", + "buffer_manager:camera_buffer_manager", + "device_manager:camera_device_manager", + "hdi_impl:camera_hdi_impl", + "init:ohos_camera_demo", + "pipeline_core:camera_pipeline_core", + "utils:camera_utils", + ] + deps += [ "${chipset_build_deps}" ] + } + + +The 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**. + + ├── icamera_device_callback.h + ├── icamera_device.h + ├── icamera_host_callback.h + ├── icamera_host.h + ├── ioffline_stream_operator.h + ├── istream_operator_callback.h + ├── istream_operator.h + +During 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**. + +### FAQs of Camera Adaptation + +#### Modifying the SUBWINDOW_TYPE and Display Format + +Change the RGBA888 display mode from **video** to **normal** (specified by **SUBWINDOW_TYPE**). + +The 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**. + + + #ifdef PRODUCT_RK + previewSurface_->SetUserData(SURFACE_FORMAT, std::to_string(PIXEL_FMT_RGBA_8888)); + previewSurface_->SetUserData(CameraStandard::CameraManager::surfaceFormat, + std::to_string(OHOS_CAMERA_FORMAT_RGBA_8888)); + #else + previewSurface_->SetUserData(SURFACE_FORMAT, std::to_string(PIXEL_FMT_YCRCB_420_SP)); + previewSurface_->SetUserData(CameraStandard::CameraManager::surfaceFormat, + std::to_string(OHOS_CAMERA_FORMAT_YCRCB_420_SP)); + #endif + +The following content in the **foundation/multimedia/camera_standard/services/camera_service/src/hstream_repeat.cpp** file is compiled in **libcamera_service.z.so**: + +``` +void HStreamRepeat::SetStreamInfo(std::shared_ptr streamInfo) + { + int32_t pixelFormat; + auto it = g_cameraToPixelFormat.find(format_); + if (it != g_cameraToPixelFormat.end()) { + pixelFormat = it->second; + } else { + #ifdef RK_CAMERA + pixelFormat = PIXEL_FMT_RGBA_8888; + #else + pixelFormat = PIXEL_FMT_YCRCB_420_SP; + #endif +``` + +In 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**. + + #ifdef PRODUCT_RK + option->SetWindowType(SUBWINDOW_TYPE_NORMAL); + #else + option->SetWindowType(SUBWINDOW_TYPE_VIDEO); + #endif + +#### Adding rk_codec_node + +RGB 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**. +Add the **rk_codec_node** connection model to HCS. +Modify the **vendor/hihope/rk3568/hdf_config/uhdf/camera/pipeline_core/config.hcs** file. + + + normal_preview_snapshot :: pipeline_spec { + name = "normal_preview_snapshot"; + v4l2_source :: node_spec { + name = "v4l2_source#0"; + status = "new"; + out_port_0 :: port_spec { + name = "out0"; + peer_port_name = "in0"; + peer_port_node_name = "fork#0"; + direction = 1; + } + } + fork :: node_spec { + name = "fork#0"; + status = "new"; + in_port_0 :: port_spec { + name = "in0"; + peer_port_name = "out0"; + peer_port_node_name = "v4l2_source#0"; + direction = 0; + } + out_port_0 :: port_spec { + name = "out0"; + peer_port_name = "in0"; + peer_port_node_name = "RKCodec#0"; + direction = 1; + } + out_port_1 :: port_spec { + name = "out1"; + peer_port_name = "in0"; + peer_port_node_name = "RKCodec#1"; + direction = 1; + } + } + RKCodec_1 :: node_spec { + name = "RKCodec#0"; + status = "new"; + in_port_0 :: port_spec { + name = "in0"; + peer_port_name = "out0"; + peer_port_node_name = "fork#0"; + direction = 0; + } + out_port_0 :: port_spec { + name = "out0"; + peer_port_name = "in0"; + peer_port_node_name = "sink#0"; + direction = 1; + } + } + RKCodec_2 :: node_spec { + name = "RKCodec#1"; + + +The 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. + +Add 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. + + + ohos_shared_library("camera_pipeline_core") { + sources = [ + "$camera_device_name_path/camera/src/pipeline_core/node/rk_codec_node.cpp", + "$camera_path/adapter/platform/v4l2/src/pipeline_core/nodes/uvc_node/uvc_node.cpp", + "$camera_path/adapter/platform/v4l2/src/pipeline_core/nodes/v4l2_source_node/v4l2_source_node.cpp", + deps = [ + "$camera_path/buffer_manager:camera_buffer_manager", + "$camera_path/device_manager:camera_device_manager", + "//device/soc/rockchip/hardware/mpp:libmpp", + "//device/soc/rockchip/hardware/rga:librga", + "//foundation/multimedia/camera_standard/frameworks/native/metadata:metadata", + "//third_party/libjpeg:libjpeg_static", + + + Main APIs of the **openharmony/device/board/hihope/rk3568/camera/src/pipeline_core/node/rk_codec_node.cpp** file: + + + void RKCodecNode::DeliverBuffer(std::shared_ptr& buffer) + { + if (buffer == nullptr) { + CAMERA_LOGE("RKCodecNode::DeliverBuffer frameSpec is null"); + return; + } + + int32_t id = buffer->GetStreamId(); + CAMERA_LOGE("RKCodecNode::DeliverBuffer StreamId %{public}d", id); + if (buffer->GetEncodeType() == ENCODE_TYPE_JPEG) { + Yuv420ToJpeg(buffer); + } else if (buffer->GetEncodeType() == ENCODE_TYPE_H264) { + Yuv420ToH264(buffer); + } else { + Yuv420ToRGBA8888(buffer); + } + +The 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. + +#### Inconsistent H.264 Frame and Audio Timestamps + +Issue: 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. + +Solution: Find the method of obtaining the audio timestamp of the audio module. + +``` + int32_t AudioCaptureAsImpl::GetSegmentInfo(uint64_t &start) + { + CHECK_AND_RETURN_RET(audioCapturer_ != nullptr, MSERR_INVALID_OPERATION); + AudioStandard::Timestamp timeStamp; + auto timestampBase = AudioStandard::Timestamp::Timestampbase::MONOTONIC; + CHECK_AND_RETURN_RET(audioCapturer_->GetAudioTime(timeStamp, timestampBase), MSERR_UNKNOWN); + CHECK_AND_RETURN_RET(timeStamp.time.tv_nsec >= 0 && timeStamp.time.tv_sec >= 0, MSERR_UNKNOWN); + if (((UINT64_MAX - timeStamp.time.tv_nsec) / SEC_TO_NANOSECOND) <= static_cast(timeStamp.time.tv_sec)) { + MEDIA_LOGW("audio frame pts too long, this shouldn't happen"); + } + start = timeStamp.time.tv_nsec + timeStamp.time.tv_sec * SEC_TO_NANOSECOND; + MEDIA_LOGI("timestamp from audioCapturer: %{public}" PRIu64 "", start); + return MSERR_OK; + } +``` + +In the **audio_capture_as_impl.cpp** file, and the camera module uses **CLOCK_REALTIME**, that is, the actual system time. + + + mppStatus_ = 1; + buf_size = ((MpiEncTestData *)halCtx_)->frame_size; + + ret = hal_mpp_encode(halCtx_, dma_fd, (unsigned char *)buffer->GetVirAddress(), &buf_size); + SearchIFps((unsigned char *)buffer->GetVirAddress(), buf_size, buffer); + + buffer->SetEsFrameSize(buf_size); + clock_gettime(CLOCK_MONOTONIC, &ts); + timestamp = ts.tv_nsec + ts.tv_sec * TIME_CONVERSION_NS_S; + buffer->SetEsTimestamp(timestamp); + CAMERA_LOGI("RKCodecNode::Yuv420ToH264 video capture on\n"); + + Solution: Change the time type in **rk_codec_node.cpp** of camera HAL to **CLOCK_MONOTONIC**. + +#### Linux 4.19 Match Error After the Value of time_t Is Changed to 64-Bit + +Background: 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. + + TYPEDEF _Int64 time_t; + TYPEDEF _Int64 suseconds_t; + + The specific error and temporary solution are as follows: + + 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: + +``` +V4L2AllocBuffer error:ioctl VIDIOC_QUERYBUF failed: Not a tty + +``` + + RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr& frameSpec) + { + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[1] = {}; + CAMERA_LOGD("V4L2AllocBuffer\n"); + + if (frameSpec == nullptr) { + CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n"); + return RC_ERROR; + } + + switch (memoryType_) { + case V4L2_MEMORY_MMAP: + // to do something + break; + case V4L2_MEMORY_USERPTR: + buf.type = bufferType_; + buf.memory = memoryType_; + buf.index = (uint32_t)frameSpec->buffer_->GetIndex(); + + if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf.m.planes = planes; + buf.length = 1; + } + CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index); + + if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { + CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno)); + return RC_ERROR; + 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. + +``` + #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) + #define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers) + #define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer) + #define VIDIOC_G_FBUF _IOR('V', 10, struct v4l2_framebuffer) +``` + + 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. + + long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) + { + struct video_device *vdev = video_devdata(file); + long ret = -ENOIOCTLCMD; + + if (!file->f_op->unlocked_ioctl) + return ret; + + if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) + ret = do_video_ioctl(file, cmd, arg); + else if (vdev->fops->compat_ioctl32) + ret = vdev->fops->compat_ioctl32(file, cmd, arg); + + 4. In this case, a value of **VIDIOC_QUERYBUF** considered by the kernel is defined in the kernel. + + #define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) + #define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) + #define VIDIOC_QUERYBUF32_TIME32 _IOWR('V', 9, struct v4l2_buffer32_time32) +5. 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. + +``` + struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + struct timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; +``` +6. 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: + +``` + struct timeval1 { + long tv_sec; + long tv_usec; + } + struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + struct timeval1 timestamp; + struct v4l2_timecode timecode; +``` + + Solution: + + 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. + 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. + +``` +struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + #ifdef __KERNEL__ + struct __kernel_v4l2_timeval timestamp; + #else + struct timeval timestamp; + #endif + struct v4l2_timecode timecode; + } + + struct __kernel_v4l2_timeval { + long long ._sec; + #if defined(__sparc__) && defined(__arch64__) + int tv_usec; + int __pad; + #else + long long tv_usec; + #endif + }; +``` +#### H.264 Key Frame Obtaining and Reporting + + 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**. + ![1647875911244](figures/dayu200/dayu200-camera-03.png) + + + IDR frame analysis is coded in the **rk_cedec_node.cpp** file. + + static constexpr uint32_t nalBit = 0x1F; + #define NAL_TYPE(value) ((value) & nalBit) + void RKCodecNode::SearchIFps(unsigned char* buf, size_t bufSize, std::shared_ptr& buffer) + { + size_t nalType = 0; + size_t idx = 0; + size_t size = bufSize; + constexpr uint32_t nalTypeValue = 0x05; + + if (buffer == nullptr || buf == nullptr) { + CAMERA_LOGI("RKCodecNode::SearchIFps parameter == nullptr"); + return; + } + + for (int i = 0; i < bufSize; i++) { + int ret = findStartCode(buf + idx, size); + if (ret == -1) { + idx += 1; + size -= 1; + } else { + nalType = NAL_TYPE(buf[idx + ret]); + CAMERA_LOGI("ForkNode::ForkBuffers nalu == 0x%{public}x buf == 0x%{public}x \n", nalType, buf[idx + ret]); + +Each 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. + + ``` + int RKCodecNode::findStartCode(unsigned char *data, size_t dataSz) + { + constexpr uint32_t dataSize = 4; + constexpr uint32_t dataBit2 = 2; + constexpr uint32_t dataBit3 = 3; + + if (data == nullptr) { + CAMERA_LOGI("RKCodecNode::findStartCode parameter == nullptr"); + return -1; + } + + if ((dataSz > dataSize) && (data[0] == 0) && (data[1] == 0) && \ + (data[dataBit2] == 0) && (data[dataBit3] == 1)) { + return 4; // 4:start node + } + + return -1; + } + ``` +After 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. + +## TP + +### TP Driver Model + +This 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: + +- **Input Manager**: manages input devices, including enabling and disabling input devices and obtaining the device list. +- **Input Reporter**: reports input events, including registering and unregistering data reporting callbacks. +- **Input Controller**: controls input devices, including obtaining the device information and device type, and setting power supply status. + +**Figure 1** HDI architecture of the input module + +![dayu200-tp-01.png](figures/dayu200/dayu200-tp-01.png) + +The source code directory structure is as follows: + +``` +/drivers/peripheral/input +├── hal # HAL code +│ └── include # HAL header files +│ └── src # HAL code implementation +├── interfaces # Driver capability APIs provided for upper-layer services +│ └── include # APIs exposed externally +├── test # Test code +│ └── unittest # Unit test code +``` + +For details, see [README](https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md) of the input subsystem. + +### TP HDF Driver Adaptation + +#### Files and Directories Involved in the TP Driver + +By default, the DAYU 200 platform supports the GT5688 TP IC. + +Files and directories involved in porting the touch driver on the development board: + +1. Makefile: drivers\adapter\khdf\linux\model\input\Makefile + +2. vendor\hihope\rk3568\hdf_config\khdf\device_info\device_info.hcs + +3. vendor\hihope\rk3568\hdf_config\khdf\input\input_config.hcs + +4. drivers\framework\model\input\driver\touchscreen + +The adaptation of the TP driver involves the TP driver and HCS configuration. + +The 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. + +The following figure shows the framework of the HDF input module from the perspective of functions. + +![dayu200-tp-02.png](figures/dayu200/dayu200-tp-02.png) + +Due 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. + +Before adaptation, you need to determine the resources required by the TP. + +For hardware resources, the TP module requires the following resources on the host: + +1. Interrupt pin + +2. Reset pin + +3. Used I2C group and address of the slave device + +4. TP initialization firmware (usually provided by IC vendors) + +5. Touchscreen resolution + +For software resources, TP adaptation on the HDF depends on the following HDF basic modules: + +1. HDF GPIO subsystem: sets GPIO pins and interrupt resources. + +2. HDF I2C subsystem: used for I2C communication. + +3. Input model + +The component driver is used based on the following structures: + +``` +static struct TouchChipOps g_gt911ChipOps = { + .Init = ChipInit, + .Detect = ChipDetect, + .Resume = ChipResume, + .Suspend = ChipSuspend, + .DataHandle = ChipDataHandle, + .UpdateFirmware = UpdateFirmware, + .SetAbility = SetAbility, +}; +``` + +**ChipInit** initializes the component driver. + +**ChipDetect** checks the component validity after initialization. + +**SetAbility** sets key attributes. + +**ChipDataHandle** parses key values. + +**UpdateFirmware** upgrades firmware. + +**ChipSuspend** suspends components. + +**ChipResume** resumes components. + +Implement the preceding API callbacks based on the component features and register the structure with the input model. + +#### HCS Configuration + +Add a new component node to **device_info.hcs**. + +``` +device_touch_chip :: device { + device0 :: deviceNode { + policy = 0; + priority = 180; + preload = 0; // The driver is loaded by default. + permission = 0660; + moduleName = "HDF_TOUCH_GT911";// The value must be the same as that in the component driver. + serviceName = "hdf_touch_gt911_service"; + deviceMatchAttr = "zsj_gt911_5p5"; + } + } +``` + +Add component features to **input_config.hcs**. + +``` +chipConfig { + template touchChip { + match_attr = ""; + chipName = "gt911"; + vendorName = "zsj"; + chipInfo = "AAAA11222"; // 4-ProjectName, 2-TP IC, 3-TP Module + /* 0:i2c 1:spi*/ + busType = 0; + deviceAddr = 0x5D; + /* 0:None 1:Rising 2:Failing 4:High-level 8:Low-level */ + irqFlag = 2; + maxSpeed = 400; + chipVersion = 0; //parse Coord TypeA + powerSequence { + /* [type, status, dir , delay] + 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int + 0:off or low 1:on or high 2:no ops + 0:input 1:output 2:no ops + meanings delay xms, 20: delay 20ms + */ + powerOnSeq = [4, 0, 1, 5, + 3, 0, 1, 10, + 3, 1, 1, 60, + 4, 2, 0, 50]; + suspendSeq = [3, 0, 2, 10]; + resumeSeq = [3, 1, 2, 10]; + powerOffSeq = [3, 0, 2, 10, + 1, 0, 2, 20]; + } + } +``` + +## Display Adaptation + +The following tasks need to be performed for display adaptation: graphics service HDI API adaptation, GPU adaptation, and LCD driver adaptation. + +### Display HDI + +[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**. + +#### Gralloc Adaptation + +The 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) + +The 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. + +``` +const char *g_drmFileNode = "/dev/dri/card0"; +``` + +In 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. + +``` +... + InitBufferHandle(bo, fd, info, priBuffer); + priBuffer->hdl.phyAddr = GetPhysicalAddr(grallocManager->drmFd, fd); + *buffer = &priBuffer->hdl; +... +``` + +#### Display Device Adaptation + +The display device module provides functions such as display device management, layer management, and hardware acceleration. + +OpenHarmony 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. + +If the development board does not support hardware composition, skip the initialization of the GFX in the **drm_display.cpp** file. + +``` +drivers_peripheral/blob/master/display/hal/default_standard/src/display_device/drm/drm_display.cpp +int32_t DrmDisplay::Init() +{ + ... + ... + ret = HdiDisplay::Init(); + DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("init failed")); + auto preComp = std::make_unique(); + DISPLAY_CHK_RETURN((preComp == nullptr), DISPLAY_FAILURE, + DISPLAY_LOGE("can not new HdiGfxComposition errno %{public}d", errno)); + ret = preComp->Init(); // GFX initialization, which needs to be skipped. + DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not init HdiGfxComposition")); // Or do not check the return value. + + ... +} +``` + +In 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. + +``` +int32_t HdiGfxComposition::SetLayers(std::vector &layers, HdiLayer &clientLayer) +{ + DISPLAY_LOGD("layers size %{public}zd", layers.size()); + mClientLayer = &clientLayer; + mCompLayers.clear(); + for (auto &layer : layers) { + if (CanHandle(*layer)) { +#if 0 // CPU composition. + layer->SetDeviceSelect(COMPOSITION_CLIENT); +#else + if ((layer->GetCompositionType() != COMPOSITION_VIDEO) && + (layer->GetCompositionType() != COMPOSITION_CURSOR)) { + layer->SetDeviceSelect(COMPOSITION_DEVICE); + } else { + layer->SetDeviceSelect(layer->GetCompositionType()); + } +#endif + mCompLayers.push_back(layer); + } + } + DISPLAY_LOGD("composer layers size %{public}zd", mCompLayers.size()); + return DISPLAY_SUCCESS; +} +``` + +#### Test and Verification + +hello_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. + +Code path: + +``` +foundation/graphic/graphic/rosen/samples/composer/ +├── BUILD.gn +├── hello_composer.cpp +├── hello_composer.h +├── layer_context.cpp +├── layer_context.h +└── main.cpp +``` + +The verification is as follows: + +1. Disable the render service. + + ``` + service_control stop render_service + ``` + +2. Disable the foundation process. + + ``` + service_control stop foundation + ``` + +3. Run **hello_composer** to test related APIs. + + ``` + ./hello_composer + ``` + +Test 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. + +Code path: **/drivers/peripheral/display/test/unittest/standard** + +``` +├── BUILD.gn +├── common +│ ├── display_test.h +│ ├── display_test_utils.cpp +│ └── display_test_utils.h +├── display_device +│ ├── hdi_composition_check.cpp +│ ├── hdi_composition_check.h +│ ├── hdi_device_test.cpp +│ ├── hdi_device_test.h +│ ├── hdi_test_device_common.h +│ ├── hdi_test_device.cpp +│ ├── hdi_test_device.h +│ ├── hdi_test_display.cpp +│ ├── hdi_test_display.h +│ ├── hdi_test_layer.cpp +│ ├── hdi_test_layer.h +│ ├── hdi_test_render_utils.cpp +│ └── hdi_test_render_utils.h +└── display_gralloc + ├── display_gralloc_test.cpp + └── display_gralloc_test.h +``` + +### GPU + +Compiler Clang + +``` +prebuilts/clang/ohos/linux-x86_64/llvm +``` + +musl Library + +``` +./build.sh --product-name rk3568 --build-target musl_all +``` + +After the compilation is complete, the following header files and libraries are generated in the **out/{product_name}/obj/third_party/musl/usr/lib** directory: + +``` +32-bit: arm-linux-ohos + +64-bit: aarch64-linux-ohos +``` + +Source code directory: + +``` +third_party/musl +``` + +GPU compilation parameter reference: + +``` +TARGET_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" +``` + +## LCD + +By default, the DAYU 200 platform supports an LCD screen with the MIPI API. + +LCD 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. + +The following figure shows the hierarchy of the HDF display driver model. + +![640](figures/dayu200/dayu200-lcd-01.png) + +The 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. + +Therefore, the LCD adaptation mainly lies in the adaptation of the LCD panel component driver. + +The adaptation of the component driver consists of two parts: panel driver and HCS configuration. + +The following files are involved: + +``` +drivers/framework/model/display/driver/panel + +vendor/hihope/rk3568/hdf_config/khdf/device_info + +vendor/hihope/rk3568/hdf_config/khdf/input +``` + +### Panel Driver + +The component driver is used based on the following APIs: + +``` +struct PanelData { + struct HdfDeviceObject *object; + int32_t (*init)(struct PanelData *panel); + int32_t (*on)(struct PanelData *panel); + int32_t (*off)(struct PanelData *panel); + int32_t (*prepare)(struct PanelData *panel); + int32_t (*unprepare)(struct PanelData *panel); + struct PanelInfo *info; + enum PowerStatus powerStatus; + struct PanelEsd *esd; + struct BacklightDev *blDev; + void *priv; +}; +``` + +Instantiate the data structure in the initialization API of the driver. + + panelSimpleDev->panel.init = PanelSimpleInit; + panelSimpleDev->panel.on = PanelSimpleOn; + panelSimpleDev->panel.off = PanelSimpleOff; + panelSimpleDev->panel.prepare = PanelSimplePrepare; + panelSimpleDev->panel.unprepare = PanelSimpleUnprepare; +PanelSimpleInit initializes panel software. + +PanelSimpleOn turns on the screen. + +PanelSimpleOff turns off the screen. + +PanelSimplePrepare initializes the hardware time sequence for turning on the screen. + +PanelSimpleUnprepare initializes the hardware time sequence for turning off the screen. + +After instantiation, use the **RegisterPanel** API to register the panel driver with the display model. + +Note that the LCD on DAYU 200 uses the DRM display framework. + +### HCS Configuration + +``` +device4 :: deviceNode { + policy = 0; + priority = 100; + preload = 0; + moduleName = "LCD_PANEL_SIMPLE"; + } +``` + +## Backlight + +The backlight driver model is developed based on the HDF framework. + +![dayu200-backlight-01.png](figures/dayu200/dayu200-backlight-01.png) + +The RK3568 backlight is implemented by controlling the duty cycle by using PWM4. + +Code path of the native backlight driver: + +```c +linux-5.10/drivers/video/backlight/pwm_bl.c +linux-5.10/drivers/video/backlight/backlight.c +linux-5.10/drivers/pwm/pwm-rockchip.c +``` + +To use the backlight driver on the HDF framework, disable the native driver. + +```c +# CONFIG_BACKLIGHT_PWM is not set +``` + +### HDF Implementation + +Code Path + +```c +drivers/framework/model/display/driver/backlight/hdf_bl.c +``` + +HDF BL Entry Function + +```c +static int32_t BacklightInit(struct HdfDeviceObject *object) +{ + if (object == NULL) { + HDF_LOGE("%s: object is null!", __func__); + return HDF_FAILURE; + } + HDF_LOGI("%s success", __func__); + return HDF_SUCCESS; +} + +struct HdfDriverEntry g_blDevEntry = { + .moduleVersion = 1, + .moduleName = "HDF_BL", + .Init = BacklightInit, + .Bind = BacklightBind, +}; + +HDF_INIT(g_blDevEntry); +``` + +Code path: + +```c +drivers/framework/model/display/driver/backlight/pwm_bl.c +``` + +HDF PWM Entry Function + +```c +struct HdfDriverEntry g_pwmBlDevEntry = { + .moduleVersion = 1, + .moduleName = "PWM_BL", + .Init = BlPwmEntryInit, +}; + +HDF_INIT(g_pwmBlDevEntry); +``` + +The backlight control APIs are as follows: + +```c +static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness) +{ + int32_t ret; + uint32_t duty; + struct BlPwmDev *blPwmDev = NULL; + + blPwmDev = ToBlDevPriv(blDev); + if (blPwmDev == NULL) { + HDF_LOGE("%s blPwmDev is null", __func__); + return HDF_FAILURE; + } + if (blPwmDev->props.maxBrightness == 0) { + HDF_LOGE("%s maxBrightness is 0", __func__); + return HDF_FAILURE; + } + if (brightness == 0) { + return PwmDisable(blPwmDev->pwmHandle); + } + duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness; + ret = PwmSetDuty(blPwmDev->pwmHandle, duty); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret); + return HDF_FAILURE; + } + return PwmEnable(blPwmDev->pwmHandle); +} + +static struct BacklightOps g_blDevOps = { + .updateBrightness = BlPwmUpdateBrightness, +}; +``` + +Actually, the API implemented by the HDF PWM is used to connect to the kernel PWM. + +![dayu200-backlight-02.png](figures/dayu200/dayu200-backlight-02.png) + +Register the backlight with the LCD HDF driver. + +Code Path + +```c +drivers/framework/model/display/driver/panel/ili9881c_boe.c +``` + +```c +ili9881cBoeDev->panel.blDev = GetBacklightDev("hdf_pwm"); +if (ili9881cBoeDev->panel.blDev == NULL) { + HDF_LOGE("%s GetBacklightDev fail", __func__); + goto FAIL; +} +``` + +### HCS Configuration + +HCS configuration of the driver + +```c +device_pwm_bl :: device { + device0 :: deviceNode { + policy = 0; + priority = 95; + preload = 0; + moduleName = "PWM_BL"; + deviceMatchAttr = "pwm_bl_dev"; + } +} +device_backlight :: device { + device0 :: deviceNode { + policy = 2; + priority = 90; + preload = 0; + permission = 0660; + moduleName = "HDF_BL"; + serviceName = "hdf_bl"; + } +} +``` + +HCS configuration for PWM backlight + +```c +root { + backlightConfig { + pwmBacklightConfig { + match_attr = "pwm_bl_dev"; + pwmDevNum = 1; + pwmMaxPeriod = 25000; + backlightDevName = "hdf_pwm"; + minBrightness = 0; + defBrightness = 127; + maxBrightness = 255; + } + } +} +``` + +### Testing + +Run **cat /sys/kernel/debug/pwm** to check whether the HDF PWM has applied for PWM4. + +If the application is successful, the following information is displayed: + +**requested**: The application is successful. + +**enabled**: PWM4 is enabled successfully. + +```c +# cat /sys/kernel/debug/pwm + +platform/fe6e0000.pwm, 1 PWM device + pwm-0 ((null) ): requested enabled period: 25000 ns duty: 9705 ns polarity: normal +``` + +## **Wi-Fi** + +### HDF-based Wi-Fi Approach + +Get 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. + +Next, 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. + +HDF Wi-Fi framework + +​ ![image-20220320160720306](figures/dayu200/dayu200-wifi-01.png) + +### Code Process Analysis of the AP6275s Driver + +#### Analysis of the Driver Module Initialization Process + +![dayu200-wifi-02.png](figures/dayu200/dayu200-wifi-02.png) + +The 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. + +l Create the wlan0 object of the net_device network API. + +**dhd_allocate_if()** calls **alloc_etherdev()** to create a **net_device** object, that is, the wlan0 network API. + +l Register wlan0 with the kernel protocol stack. + +Call the **dhd_register_if()** function. Here, call **register_netdev(net)** to register the wlan0 network API with the protocol stack. + +### HDF Wi-Fi Framework Adaptation Through Code Modification + +To 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. + +The 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. + +The files and directories involved in the adaptation and porting of the AP6275s Wi-Fi 6 driver are as follows: + +1). Compile configuration files. + +drivers/adapter/khdf/linux/model/network/wifi/Kconfig + +drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile + +2). Access the Wi-Fi driver source code directory. + +The native driver code is stored in: + +linux-5.10/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd_wifi6/ + +The code files added and modified on the native driver are stored in the following directory: + +device/hihope/rk3568/wifi/bcmdhd_wifi6/ + +Directory structure: + +``` +./device/hihope/rk3568/wifi/bcmdhd_wifi6/hdf +├── hdf_bdh_mac80211.c +├── hdf_driver_bdh_register.c +├── hdfinit_bdh.c +├── hdf_mac80211_ap.c +├── hdf_mac80211_sta.c +├── hdf_mac80211_sta.h +├── hdf_mac80211_sta_event.c +├── hdf_mac80211_sta_event.h +├── hdf_mac80211_p2p.c +├── hdf_public_ap6275s.h +├── net_bdh_adpater.c +├── net_bdh_adpater.h +``` + +**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**. + +#### Driver File Compilation + +The 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: + +1. Initialize the driver module that adapts the HDF WLAN framework. + + The following figure shows the code process. + + ![dayu200-wifi-03.png](figures/dayu200/dayu200-wifi-03.png) + + The code is stored in **device/hihope/rk3568/wifi/bcmdhd_wifi6/hdf_driver_bdh_register.c**. + + ``` + struct HdfDriverEntry g_hdfBdh6ChipEntry = { + .moduleVersion = 1, + .Bind = HdfWlanBDH6DriverBind, + .Init = HdfWlanBDH6ChipDriverInit, + .Release = HdfWlanBDH6ChipRelease, + .moduleName = "HDF_WLAN_CHIPS" + }; + HDF_INIT(g_hdfBdh6ChipEntry); + ``` + + 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. + +2. Implement the HDF WLAN Base control-side APIs. + + The code is stored in **hdf_bdh_mac80211.c**. + + ``` + static struct HdfMac80211BaseOps g_bdh6_baseOps = { + .SetMode = BDH6WalSetMode, + .AddKey = BDH6WalAddKey, + .DelKey = BDH6WalDelKey, + .SetDefaultKey = BDH6WalSetDefaultKey, + .GetDeviceMacAddr = BDH6WalGetDeviceMacAddr, + .SetMacAddr = BDH6WalSetMacAddr, + .SetTxPower = BDH6WalSetTxPower, + .GetValidFreqsWithBand = BDH6WalGetValidFreqsWithBand, + .GetHwCapability = BDH6WalGetHwCapability, + .SendAction = BDH6WalSendAction, + .GetIftype = BDH6WalGetIftype, + }; + ``` + + The preceding APIs are called in STA, AP, and P2P modes. + +3. Implement APIs in HDF WLAN STA mode. + + The following figure shows the call process in STA mode. + ![image-20220320161412663](figures/dayu200/dayu200-wifi-04.png) + + The code is stored in **hdf_mac80211_sta.c**. + + ``` + struct HdfMac80211STAOps g_bdh6_staOps = { + .Connect = HdfConnect, + .Disconnect = HdfDisconnect, + .StartScan = HdfStartScan, + .AbortScan = HdfAbortScan, + .SetScanningMacAddress = HdfSetScanningMacAddress, + }; + ``` + +4. Implement APIs in HDF WLAN AP mode. + + The following figure shows the call process in AP mode. + + ![image-20220320161432068](figures/dayu200/dayu200-wifi-05.png) + +The code is stored in **hdf_mac80211_ap.c**. + +``` +struct HdfMac80211APOps g_bdh6_apOps = { + .ConfigAp = WalConfigAp, + .StartAp = WalStartAp, + .StopAp = WalStopAp, + .ConfigBeacon = WalChangeBeacon, + .DelStation = WalDelStation, + .SetCountryCode = WalSetCountryCode, + .GetAssociatedStasCount = WalGetAssociatedStasCount, + .GetAssociatedStasInfo = WalGetAssociatedStasInfo +}; +``` + +5. Implement APIs in HDF WLAN P2P mode. + + The following figure shows the call process in P2P mode. + + ![image-20220320161442845](figures/dayu200/dayu200-wifi-06.png) + + ``` + struct HdfMac80211P2POps g_bdh6_p2pOps = { + .RemainOnChannel = WalRemainOnChannel, + .CancelRemainOnChannel = WalCancelRemainOnChannel, + .ProbeReqReport = WalProbeReqReport, + .AddIf = WalAddIf, + .RemoveIf = WalRemoveIf, + .SetApWpsP2pIe = WalSetApWpsP2pIe, + .GetDriverFlag = WalGetDriverFlag, + }; + ``` + +6. Implement the event reporting APIs of the HDF WLAN framework. + + 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**. + + The HDF WLAN APIs for reporting events are as follows. + +| API in Header File hdf_wifi_event.h | Description | +| ----------------------------------- | ------------------------ | +| HdfWifiEventNewSta() | Reports a new STA event. | +| HdfWifiEventDelSta () | Reports an STA deletion event. | +| HdfWifiEventInformBssFrame () | Reports a BSS scanning event. | +| HdfWifiEventScanDone () | Reports a scanning completion event. | +| HdfWifiEventConnectResult () | Reports a connection result event. | +| HdfWifiEventDisconnected () | Reports a disconnection event. | +| HdfWifiEventMgmtTxStatus () | Reports sending status events. | +| HdfWifiEventRxMgmt () | Reports a receiving status event. | +| HdfWifiEventCsaChannelSwitch () | Reports a CSA frequency band switching event. | +| HdfWifiEventTimeoutDisconnected ()| Reports a connection timeout event. | +| HdfWifiEventEapolRecv () | Reports an EAPOL receive event. | +| HdfWifiEventResetResult () | Reports a WLAN driver reset result event.| +| HdfWifiEventRemainOnChannel () | Reports a channel holding event. | +| HdfWifiEventCancelRemainOnChannel | Reports a channel unholding event. | + +### Summary of All Key Issues + +#### Method of Enabling the AP Mode During AP Module Debugging + +Use the BusyBox and hostapd to configure the AP function as follows: + +``` +ifconfig wlan0 up +ifconfig wlan0 192.168.12.1 netmask 255.255.255.0 +busybox udhcpd /data/udhcpd.conf +./hostapd -d /data/hostapd.conf +``` + +#### Method of Enabling the ATA Mode During STA Module Debugging + +``` +wpa_supplicant -iwlan0 -c /data/l2tool/wpa_supplicant.conf -d & +./busybox udhcpc -i wlan0 -s /data/l2tool/dhcpc.sh +``` + +#### Solution to the Failure to Report the Hotspot Scanning Event to wap_supplicant + +When 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. + +In this case, use **wpa_supplicant -iwlan0 -c /data/wpa_supplicant.conf &** to start the application in the background. + +#### WPA2PSK Authentication Failure and Timeout + +According 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. + +#### Location and Analysis of Connection Failure in P2P Mode + +During 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. + + ![image-20220320161303057](figures/dayu200/dayu200-wifi-07.png) + +Check 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. + +Correct frame content + + ![image-20220320161314006](figures/dayu200/dayu200-wifi-08.png) + +Incorrect frame content + + ![image-20220320161318995](figures/dayu200/dayu200-wifi-09.png) + +Analyze 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)**. + +### Connection Success Log + +#### Connection Success Log in STA Mode + +``` +WPA: Key negotiation completed with 50:eb:f6:02:8e6:d4 [PTK=CCMP GTK=CCMP] + 06 wlan0: State: GROUP_HANDSHAKEc -> COMPLETED +wlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=] +WifiWpaReceived eEapol done +``` + +#### Connection Success Log in AP Mode + +``` +wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port +wlan0: STA 96:27:b3:95:b7:6e WPA: pairwise key handshake completed (RSN) +WifiWpaReceiveEapol done +``` + +#### Connection Success Log in P2P Mode + +``` +P2P: cli_channels: +EAPOL: External notificationtion - portValid=1 +EAPOL: External notification:tion - EAP success=1 +EAPOL: SUPP_PAE entering state AUTHENTIwCATING +EAPOL: SUPP_BE enterilng state SUCCESS +EAP: EAP ent_ering state DISABLED +EAPOL: SUPP_PAE entering state AUTHENTICATED +EAPOL:n Supplicant port status: Authoorized +EAPOL: SUPP_BE entertaining IDLE +WifiWpaReceiveEapol donepleted - result=SUCCESS + +\# ifconfig + +lo Link encap:Local Loopback + inet addr:127.0.0.1 Mask:255.0.0.0 + inet6 addr: ::1/128 Scope: Host + UP LOOPBACK RUNNING MTU:65536 Metric:1 + RX packets:12 errors:0 dropped:0 overruns:0 frame:0 + TX packets:12 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:565 TX bytes:565 + +wlan0 Link encap:Ethernet HWaddr 10:2c:6b:11:61:e0 Driver bcmsdh_sdmmc + inet6 addr: fe80::122c:6bff:fe11:61e0/64 Scope: Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 TX bytes:0 + +p2p0 Link encap:Ethernet HWaddr 12:2c:6b:11:61:e0 + inet6 addr: fe80::102c:6bff:fe11:61e0/64 Scope: Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 TX bytes:0 + +p2p-p2p0-0 Link encap:Ethernet HWaddr 12:2c:6b:11:21:e0 Driver bcmsdh_sdmmc + inet6 addr: fe80::102c:6bff:fe11:21e0/64 Scope: Link + UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + RX packets:0 errors:0 dropped:9 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 + collisions:0 txqueuelen:1000 + RX bytes:0 TX bytes:0 +``` + +## Bluetooth + +### HCI API + +The 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. + +![dayu200-bt-01.png](figures/dayu200/dayu200-bt-01.png) + +HCI 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. + +### Hardware Connection + +According 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. + +![](figures/dayu200/dayu200-bt-02.png) + +Pins 28-36 are UART (serial port). In addition, several pins are used for power supply and hibernation control. + +### Bluetooth VENDORLIB Adaptation + +#### What Is vendorlib? + +vendorlib 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: + +1. Provide a channel (file descriptor of the serial port) between Bluetooth chips for the protocol stack. + +2. Provide specific control methods for specific chips. + +#### Interpretation of vendorlib at the Code Level + +**bt_vendor_lib.h**: + +``` +foundation/communication/bluetooth/services/bluetooth_standard/hardware/include +``` + +This file defines the interaction APIs between the protocol stack and **vendor_lib**, which are divided into two groups: + +1. Implemented by vendorlib and called by the protocol stack + +```c +typedef struct { + /** + * Set to sizeof(bt_vendor_interface_t) + */ + size_t size; + /** + * Caller will open the interface and pass in the callback routines + * to the implementation of this interface. + */ + int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr); + + /** + * Vendor specific operations + */ + int (*op)(bt_opcode_t opcode, void* param); + + /** + * Closes the interface + */ + void (*close)(void); +} bt_vendor_interface_t; +``` + +The basic process of starting the protocol stack is as follows: + +1.1. The protocol stack dynamically opens **libbt_vendor.z.so** and calls the **init** function to initialize **vendorlib**. + +1.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. + +2. Implemented by the protocol stack and called by **vendorlib** (callback function) + +```c +typedef struct { + /** + * set to sizeof(bt_vendor_callbacks_t) + */ + size_t size; + + /* notifies caller result of init request */ + init_callback init_cb; + + /* buffer allocation request */ + malloc_callback alloc; + + /* buffer free request */ + free_callback dealloc; + + /* hci command packet transmit request */ + cmd_xmit_callback xmit_cb; +} bt_vendor_callbacks_t; +``` + +**init_cb** is called after **BT_OP_INIT** is complete. + +**alloc/dealloc** is used to apply for or release message controls when HCI messages are sent. + +**xmit_cb** sends HCI commands. + +Important functions implemented by **vendor_lib**: + +1. **init** function + +```c +static int init(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr) +{ + /* * ... */ + userial_vendor_init(); + upio_init(); + + vnd_load_conf(VENDOR_LIB_CONF_FILE); + + /* store reference to user callbacks */ + bt_vendor_cbacks = (bt_vendor_callbacks_t *)p_cb; + /* This is handed over from the stack */ + return memcpy_s(vnd_local_bd_addr, BD_ADDR_LEN, local_bdaddr, BD_ADDR_LEN); +} +``` + +The **init** function is the first function called by **vendorlib**. **vendorlib** only needs to save the callback and MAC addresses of the protocol stack. + +2. **BT_OP_POWER_ON** + +This 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. + +```c +int upio_set_bluetooth_power(int on) +{ + int sz; + int fd = -1; + int ret = -1; + char buffer = '0'; + + switch (on) { + case UPIO_BT_POWER_OFF: + buffer = '0'; + break; + + case UPIO_BT_POWER_ON: + buffer = '1'; + break; + default: + return 0; + } + + /* check if we have rfkill interface */ + if (is_rfkill_disabled()) { + return 0; + } + + if (rfkill_id == -1) { + if (init_rfkill()) { + return ret; + } + } + + fd = open(rfkill_state_path, O_WRONLY); + if (fd < 0) { + return ret; + } + + sz = write(fd, &buffer, 1); + /* ... */ + return ret; +} +``` + +3. **BT_OP_HCI_CHANNEL_OPEN** processing + +```c +case BT_OP_HCI_CHANNEL_OPEN: { // BT_VND_OP_USERIAL_OPEN + int(*fd_array)[] = (int(*)[])param; + int fd, idx; + fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg); + if (fd != -1) { + for (idx = 0; idx < HCI_MAX_CHANNEL; idx++) + (*fd_array)[idx] = fd; + retval = 1; + } + /* retval contains numbers of open fd of HCI channels */ + break; +``` + +The **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. + +The 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**. + +4. **BT_OP_INIT** processing + +The 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. + +After 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: + +After calling **BT_OP_INIT**, the protocol stack waits for the semaphore. The semaphore is set by the **init_cb** function. + +```c +static int HciInitHal() +{ + int result = BT_NO_ERROR; + + g_waitHdiInit = SemaphoreCreate(0); + int ret = g_hdiLib->hdiInit(&g_hdiCallbacks); + if (ret == SUCCESS) { + SemaphoreWait(g_waitHdiInit); + } +} +``` + +### vendorlib Porting Problem + +1. Name of the .so file of vendorlib + + 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). + +2. Firmware problem + + 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: + + 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. + + 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: + +```c +/ Hardware Configuration State */ +enum { + HW_CFG_START = 1, + HW_CFG_SET_UART_CLOCK, + HW_CFG_SET_UART_BAUD_1, + HW_CFG_READ_LOCAL_NAME, + HW_CFG_DL_MINIDRIVER, + HW_CFG_DL_FW_PATCH, + HW_CFG_SET_UART_BAUD_2, + HW_CFG_SET_BD_ADDR, + HW_CFG_READ_BD_ADDR +}; +``` + +Initialize the state machine after receiving the **BT_OP_INIT** message, send the **HCI_REST** command, and switch the state to **HW_CFG_START**. + +```c +void hw_config_start(void) +{ + HC_BT_HDR *p_buf = NULL; + uint8_t *p; + hw_cfg_cb.state = 0; + hw_cfg_cb.fw_fd = -1; + hw_cfg_cb.f_set_baud_2 = FALSE; + + if (bt_vendor_cbacks) { + p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE + + HCI_CMD_PREAMBLE_SIZE); + } + + if (p_buf) { + p_buf->event = MSG_STACK_TO_HC_HCI_CMD; + p_buf->offset = 0; + p_buf->layer_specific = 0; + p_buf->len = HCI_CMD_PREAMBLE_SIZE; + + p = (uint8_t *)(p_buf + 1); + UINT16_TO_STREAM(p, HCI_RESET); + *p = 0; + + hw_cfg_cb.state = HW_CFG_START; + bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf); + } else { + if (bt_vendor_cbacks) { + HILOGE("vendor lib fw conf aborted [no buffer]"); + bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL); + } + } +} +``` + +After 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. + +For details, see the **hw_config_cback** function. + +3. Pay attention to inter-system API differences. + + 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. + +Certain system: + +```c +/* define callback of the cmd_xmit_cb + * + +The callback function which HCI lib will call with the return of command + +complete packet. Vendor lib is responsible for releasing the buffer passed + +in at the p_mem parameter by calling dealloc callout function. +*/ +typedef void (*tINT_CMD_CBACK)(void* p_mem); +typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void* p_buf, tINT_CMD_CBACK p_cback); +``` + +OpenHarmony: + +```c +/** + +hci command packet transmit callback + +Vendor lib calls cmd_xmit_cb function in order to send a HCI Command + +packet to BT Controller. +* + +The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of + +HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command + +packet. */ + +typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf); +``` + +As 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. + +```c +void hw_process_event(HC_BT_HDR *p_buf) +{ + uint16_t opcode; + uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; + STREAM_TO_UINT16(opcode, p); + switch (opcode) { + case HCI_VSC_WRITE_BD_ADDR: + #if (USE_CONTROLLER_BDADDR == TRUE) + case HCI_READ_LOCAL_BDADDR: + #endif + case HCI_READ_LOCAL_NAME: + case HCI_VSC_DOWNLOAD_MINIDRV: + case HCI_VSC_WRITE_FIRMWARE: + case HCI_VSC_LAUNCH_RAM: + case HCI_RESET: + case HCI_VSC_WRITE_UART_CLOCK_SETTING: + case HCI_VSC_UPDATE_BAUDRATE: + hw_config_cback(p_buf); + break; +``` + +The 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. + +4. snoop log + + 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. + +## Sensor + + Sensor driver model developed based on the HDF driver framework + +![dayu200-sensor-01.png](figures/dayu200/dayu200-sensor-01.png) + +RK3568 supports the accel sensor. The OpenHarmony mainline version has the overall driver framework. You only need to implement the specific component driver. + +### Implementation of the mcx5566xa HDF Driver + +The 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. + +The configuration file is stored in **kernel/linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig**. + +```c +# CONFIG_GS_MXC6655XA is not set +``` + +Code path: + +```c +drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.c +drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.h +``` + +Compile the macros. + +```c +CONFIG_DRIVERS_HDF_SENSOR_ACCEL_MXC6655XA=y +``` + +Implementation of the MXC6655XA accelerometer driver entry function + +```c +struct HdfDriverEntry g_accelMxc6655xaDevEntry = { + .moduleVersion = 1, + .moduleName = "HDF_SENSOR_ACCEL_MXC6655XA", + .Bind = Mxc6655xaBindDriver, + .Init = Mxc6655xaInitDriver, + .Release = Mxc6655xaReleaseDriver, +}; + +HDF_INIT(g_accelMxc6655xaDevEntry); +``` + +Next, let's look at the differentiated adaptation function. + +```c +struct AccelOpsCall { +int32_t (*Init)(struct SensorCfgData *data); +int32_t (*ReadData)(struct SensorCfgData *data); +}; +``` + +API for obtaining data on the X, Y, and Z axises: + +```c +int32_t ReadMxc6655xaData(struct SensorCfgData *cfg, struct SensorReportEvent *event) +{ + int32_t ret; + struct AccelData rawData = { 0, 0, 0 }; + static int32_t tmp[ACCEL_AXIS_NUM]; + + CHECK_NULL_PTR_RETURN_VALUE(cfg, HDF_ERR_INVALID_PARAM); + CHECK_NULL_PTR_RETURN_VALUE(event, HDF_ERR_INVALID_PARAM); + + ret = ReadMxc6655xaRawData(cfg, &rawData, &event->timestamp); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: MXC6655XA read raw data failed", __func__); + return HDF_FAILURE; + } + + event->sensorId = SENSOR_TAG_ACCELEROMETER; + event->option = 0; + event->mode = SENSOR_WORK_MODE_REALTIME; + + rawData.x = rawData.x * MXC6655XA_ACC_SENSITIVITY_2G; + rawData.y = rawData.y * MXC6655XA_ACC_SENSITIVITY_2G; + rawData.z = rawData.z * MXC6655XA_ACC_SENSITIVITY_2G; + + tmp[ACCEL_X_AXIS] = (rawData.x * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; + tmp[ACCEL_Y_AXIS] = (rawData.y * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; + tmp[ACCEL_Z_AXIS] = (rawData.z * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT; + + ret = SensorRawDataToRemapData(cfg->direction, tmp, sizeof(tmp) / sizeof(tmp[0])); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: MXC6655XA convert raw data failed", __func__); + return HDF_FAILURE; + } + + event->dataLen = sizeof(tmp); + event->data = (uint8_t *)&tmp; + + return ret; +} +``` + +Initialization: + +```c +static int32_t InitMxc6655xa(struct SensorCfgData *data) +{ + int32_t ret; + + CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM); + ret = SetSensorRegCfgArray(&data->busCfg, data->regCfgGroup[SENSOR_INIT_GROUP]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: MXC6655XA sensor init config failed", __func__); + return HDF_FAILURE; + } + return HDF_SUCCESS; +} +``` + +### HCS Configuration + +HCS configuration of the MXC6655XA accel sensor driver: + +```c +device_sensor_mxc6655xa :: device { + device0 :: deviceNode { + policy = 1; + priority = 120; + preload = 0; + permission = 0664; + moduleName = "HDF_SENSOR_ACCEL_MXC6655XA"; + serviceName = "hdf_accel_mxc6655xa"; + deviceMatchAttr = "hdf_sensor_accel_mxc6655xa_driver"; + } +} +``` + +Register group configuration information of the MXC6655XA accel sensor: + +```c +#include "../sensor_common.hcs" +root { + accel_mxc6655xa_chip_config : sensorConfig { + match_attr = "hdf_sensor_accel_mxc6655xa_driver"; + sensorInfo :: sensorDeviceInfo { + sensorName = "accelerometer"; + vendorName = "memsi_mxc6655xa"; // max string length is 16 bytes + sensorTypeId = 1; // enum SensorTypeTag + sensorId = 1; // user define sensor id + power = 230; + } + sensorBusConfig :: sensorBusInfo { + busType = 0; // 0:i2c 1:spi + busNum = 5; + busAddr = 0x15; + regWidth = 1; // 1byte + } + sensorIdAttr :: sensorIdInfo { + chipName = "mxc6655xa"; + chipIdRegister = 0x0f; + chipIdValue = 0x05; + } + sensorDirection { + direction = 5; // chip direction range of value:0-7 + /* 1:negative 0:positive + 0:AXIS_X 1:AXIS_Y 2:AXIS_Z + */ + /* sign[AXIS_X], sign[AXIS_Y], sign[AXIS_Z], map[AXIS_X], map[AXIS_Y], map[AXIS_Z] */ + convert = [ + 0, 0, 0, 0, 1, 2, + 1, 0, 0, 1, 0, 2, + 0, 0, 1, 0, 1, 2, + 0, 1, 0, 1, 0, 2, + 1, 0, 1, 0, 1, 2, + 0, 0, 1, 1, 0, 2, + 0, 1, 1, 0, 1, 2, + 1, 1, 1, 1, 0, 2 + ]; + } + sensorRegConfig { + /* regAddr: register address + value: config register value + len: size of value + mask: mask of value + delay: config register delay time (ms) + opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit + calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift + shiftNum: shift bits + debug: 0-no debug 1-debug + save: 0-no save 1-save + */ + /* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */ + initSeqConfig = [ + 0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0, + 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 + ]; + enableSeqConfig = [ + 0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0, + 0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0, + 0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0 + ]; + disableSeqConfig = [ + 0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0 + ]; + } + } +} +``` + +### Testing + +The three-axis data of the sensor can be obtained during the UT test. + +Test code path: + +```c +drivers/peripheral/sensor/test/unittest/common/hdf_sensor_test.cpp +``` + +Build UT code: + +```c +./build.sh --product-name rk3568 --build-target hdf_test_sensor +``` + +Push **hdf_test_sensor.bin** to the **system/bin** directory, add the execute permission to the file, and execute the file. + +If the following information is displayed, the sensor test is successful. + +```c +SensorTestDataCallback enter +sensor id :[1], data[1]: 0.001877 +sensor id :[1], data[2]: 0.160823 +sensor id :[1], data[3]: 0.046122 +``` + +## Vibrator + +### Vibrator Model + +The 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. + +**Figure 1** Architecture of the vibrator driver module + +![dayu200-vibrator-01.png](figures/dayu200/dayu200-vibrator-01.png) + +The RK3568 supports the linear vibrator. The OpenHarmony mainline version has the overall driver framework. You only need to implement the specific component driver. + +### HDF Driver Implementation + +Code path: + +```c +drivers/framework/model/misc/vibrator/driver/chipset/vibrator_linear_driver.c +``` + +Implementation of the linear vibrator accelerometer driver entry function: + +```c +struct HdfDriverEntry g_linearVibratorDriverEntry = { + .moduleVersion = 1, + .moduleName = "HDF_LINEAR_VIBRATOR", + .Bind = BindLinearVibratorDriver, + .Init = InitLinearVibratorDriver, + .Release = ReleaseLinearVibratorDriver, +}; + +HDF_INIT(g_linearVibratorDriverEntry); +``` + +### HCS Configuration + +HCS configuration of the driver: + +```c + vibrator :: host { + hostName = "vibrator_host"; + device_vibrator :: device { + device0 :: deviceNode { + policy = 2; + priority = 100; + preload = 0; + permission = 0664; + moduleName = "HDF_VIBRATOR"; + serviceName = "hdf_misc_vibrator"; + deviceMatchAttr = "hdf_vibrator_driver"; + } + } + device_linear_vibrator :: device { + device0 :: deviceNode { + policy = 1; + priority = 105; + preload = 0; + permission = 0664; + moduleName = "HDF_LINEAR_VIBRATOR"; + serviceName = "hdf_misc_linear_vibrator"; + deviceMatchAttr = "hdf_linear_vibrator_driver"; + } + } + } +``` + +HCS configuration of the linear vibrator: + +```c +root { + linearVibratorConfig { + boardConfig { + match_attr = "hdf_linear_vibrator_driver"; + vibratorChipConfig { + busType = 1; // 0:i2c 1:gpio + gpioNum = 154; + startReg = 0; + stopReg = 0; + startMask = 0; + } + } + } +} +``` + +### UT Test + +Test code path: + +```c +drivers/peripheral/misc/vibrator/test/unittest/common/hdf_vibrator_test.cpp +``` + +Build UT code: + +```c +./build.sh --product-name rk3568 --build-target hdf_test_vibrator +``` + +Push **hdf_test_vibrator.bin** to the **system/bin** directory, add the execute permission to the file, and execute the file. + +``` +[ RUN ] HdfVibratorTest.CheckVibratorInstanceIsEmpty +[ OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms) +[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration001 +[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration001 (2001 ms) +[ RUN ] HdfVibratorTest.ExecuteVibratorEffect001 +[ OK ] HdfVibratorTest.ExecuteVibratorEffect001 (5001 ms) +``` diff --git a/en/device-dev/porting/porting-stm32f407-on-minisystem-eth.md b/en/device-dev/porting/porting-stm32f407-on-minisystem-eth.md new file mode 100644 index 0000000000000000000000000000000000000000..c4d71f3b846798bfd2675719f6ba7f7b8d14ecd7 --- /dev/null +++ b/en/device-dev/porting/porting-stm32f407-on-minisystem-eth.md @@ -0,0 +1,1287 @@ +# Mini System STM32F407 SoC Porting Case + +This document describes how to port the OpenHarmony LiteOS-M mini system on the Talkweb [Niobe407](https://gitee.com/openharmony-sig/device_board_talkweb) development board based on the `STM32F407IGT6` chip, to provide development board solutions for transportation and industrial fields. The porting architecture uses the `Board` and `SoC` separation solution and the `Newlib C` library of the `arm gcc` toolchain to implement adaptation of subsystems and components such as `lwip`, `littlefs`, and `hdf`. The matching application sample code is developed to support graphical configuration of compilation options through KConfig. + +## Preparing for Adaptation + +- Download the graphic tool [stm32cubemx](https://www.st.com/en/development-tools/stm32cubemx.html). +- Prepare the Ubuntu 20.04 system environment and install the [arm-none-eabi-gcc](https://gitee.com/openharmony/device_board_talkweb/blob/master/niobe407/docs/software/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E5%9B%BA%E4%BB%B6%E7%BC%96%E8%AF%91.md#6%E5%AE%89%E8%A3%85%E4%BA%A4%E5%8F%89%E7%BC%96%E8%AF%91%E5%B7%A5%E5%85%B7%E9%93%BE) cross compilation toolchain. +### Generating an Available Project + +Generate the Makefile project of the `STM32F407IGT6` chip by using the STM32CubeMX tool. You are advised to complete configurations as follows: + +- Retain the default system settings. +- Set **SYSCLK** to 168 MHz in the clock configuration, to maximize the chip performance. +- Configure USART1 as the debugging serial port, to generate debugging information during adaptation. +- Set **Toolchain/IDE** to **Makefile** in STM32CubeMX configurations. + +The generated project directory is as follows: + +``` +├── Core +│ ├── Inc +│ │ ├── main.h +│ │ ├── stm32f4xx_hal_conf.h +│ │ └── stm32f4xx_it.h +│ └── Src +│ ├── main.c --- Main function +│ ├── stm32f4xx_hal_msp.c --- Weak function configuration file in the HAL library +│ ├── stm32f4xx_it.c --- Interrupt callback function file +│ └── system_stm32f4xx.c --- System +├── Drivers +│ ├── CMSIS --- CMSIS API +│ └── STM32F4xx_HAL_Driver --- HAL library driver +├── Makefile --- Makefile compilation +├── STM32F407IGTx_FLASH.ld --- Link file +├── startup_stm32f407xx.s --- Startup file +└── stm32f407_output.ioc --- STM32CubeMX project file +``` + +### Verifying the Generated Project + +Copy the generated project to Ubuntu, go to the project directory, and run the **make** command to compile the project. Ensure that the compilation is successful. + +``` +arm-none-eabi-gcc build/main.o build/stm32f4xx_it.o build/stm32f4xx_hal_msp.o build/stm32f4xx_hal_tim.o build/stm32f4xx_hal_tim_ex.o build/stm32f4xx_hal_uart.o build/stm32f4xx_hal_rcc.o build/stm32f4xx_hal_rcc_ex.o build/stm32f4xx_hal_flash.o build/stm32f4xx_hal_flash_ex.o build/stm32f4xx_hal_flash_ramfunc.o build/stm32f4xx_hal_gpio.o build/stm32f4xx_hal_dma_ex.o build/stm32f4xx_hal_dma.o build/stm32f4xx_hal_pwr.o build/stm32f4xx_hal_pwr_ex.o build/stm32f4xx_hal_cortex.o build/stm32f4xx_hal.o build/stm32f4xx_hal_exti.o build/system_stm32f4xx.o build/startup_stm32f407xx.o -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nano.specs -TSTM32F407IGTx_FLASH.ld -lc -lm -lnosys -Wl,-Map=build/stm32f407_output.map,--cref -Wl,--gc-sections -o build/stm32f407_output.elf +arm-none-eabi-size build/stm32f407_output.elf + text data bss dec hex filename + 5000 20 1636 6656 1a00 build/stm32f407_output.elf +arm-none-eabi-objcopy -O ihex build/stm32f407_output.elf build/stm32f407_output.hex +arm-none-eabi-objcopy -O binary -S build/stm32f407_output.elf build/stm32f407_output.bin +``` + +After the compilation is complete, a .bin file is generated. To ensure that the program can run successfully on the development board, you need to initialize the serial port in the main function and output a string through the serial port. If the following information is generated during running, the development board is started successfully: +``` +printf("hello world!!\r\n"); +``` + +To generate logs using the **printf** function to the serial port, override the **_write** function. The sample code is as follows: + +```c +#include + +int _write(int fd, char *ptr, int len) +{ + return HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF); +} +``` +Recompile the code and burn it to the development board for verification. + +## Compilation and Building +### Directory Planning + +The chip adaptation directory is planned as follows: + +``` +device +├── board --- Board vendor directory +│ └── talkweb --- Board vendor: Talkweb +│ └── niobe407 --- Board name, which is the same as the product name +└── soc --- SoC vendor directory + └── st --- SoC vendor + └── stm32f4xx --- SoC series: STM32F4xx, including SoC code +``` + +The planned product demo directory is as follows: + +``` +vendor +└── talkweb --- Vendor of the product sample + └── niobe407 --- Product name: niobe407 +``` + +Obtain the [OpenHarmony source code](../get-code/sourcecode-acquire.md) and create directories as planned. + +### Precompilation Adaptation + +This part describes adaptation using the `hb set` command to set environment variables such as the root directory, board directory, product directory, and board vendor, for subsequent adaptation compilation. + +To be specific: + +1. Add the `config.json` file to the `vendor/talkweb/niobe407` directory to describe the board and kernel information used by the product sample. The sample code is as follows: + +``` +{ + "product_name": "niobe407", --- Product name displayed when using the **hb set** command. + "type": "mini", --- System type, which can be mini, small, or standard. + "version": "3.0", --- Version of the system, which can be 1.0, 2.0, or 3.0. + "device_company": "talkweb", --- Board vendor name, which is used to find the **/device/board/talkweb** directory during compilation. + "board": "niobe407", --- Board name, which is used to find the **/device/board/talkweb/niobe407** directory during compilation. + "kernel_type": "liteos_m", --- Kernel type. The OpenHarmony supports multiple kernels, and one board may adapt multiple kernels. Therefore, you need to specify a kernel for compilation. + "kernel_version": "3.0.0", --- Kernel version. One board may adapt multiple Linux kernel versions. Therefore, you need to specify a kernel version for compilation. + "subsystems": [ ] --- Subsystem to be built. +} +``` + +2. Create the `board` directory under `//device/board/talkweb/niobe407` and add the `config.gni` file to the created directory to describe the compilation configuration information of the product. + +``` +# Kernel type, e.g. "linux", "liteos_a", "liteos_m". +kernel_type = "liteos_m" --- Kernel type, which corresponds to **kernel_type** in **config.json**. + +# Kernel version. +kernel_version = "3.0.0" --- Kernel version, which corresponds to **kernel_version** in **config.json**. +``` + +3. Check whether the `hb set` configuration is correct. If the following information is displayed after you enter `hb set`, the configuration is correct. + + ![hb set](figures/niobe407_hb_set.png) + +4. Run the `hb env` command to view the selected precompilation environment variables. + + ![hb env](figures/niobe407_hb_env.png) + +5. Introduction to **hb** + + `hb` is a Python script tool provided by OpenHarmony to facilitate code building and compilation. The source code of `hb` is stored in the `//build/lite` repository directory. When the `hb set` command is executed, the script traverses `config.json` in the `//vendor//` directory and provides product compilation options. In the **config.json** file, `product_name` indicates the product name, and `device_company` and `board` are used to associate the `//device/board//` directory and match the `/config.gni` file in the directory. The `` directory name can be any name, however, you are advised to name it as the adapted kernel name (for example, **liteos_m**, **liteos_a**, or **linux**). If multiple `config.gni` files are matched, the **hb** command matches the `kernel_type` and `kernel_version` fields with the fields in the `config.json` file in `vendor/` to determine the `config.gni` file to be compiled. + +So far, the precompilation adaptation is complete. However, the project cannot be compiled by running `hb build`. You need to prepare for the subsequent `LiteOS-M` kernel porting. + +## Kernel Porting + +Kernel porting requires `LiteOS-M Kconfig` adaptation, `gn` compilation and building, and minimum kernel startup adaptation. + +### Kconfig File Adaptation + +1. Create the **kernel_configs** directory in `//vendor/talkweb/niobe407` and create an empty file named **debug.config**. + +2. Open the `//kernel/liteos_m/Kconfig` file. Multiple Kconfig files in `//device/board` and `//device/soc` have been imported using the **orsource** command. You need to create and modify these files later. + +``` +orsource "../../device/board/*/Kconfig.liteos_m.shields" +orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards" +orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards" +orsource "../../device/soc/*/Kconfig.liteos_m.defconfig" +orsource "../../device/soc/*/Kconfig.liteos_m.series" +orsource "../../device/soc/*/Kconfig.liteos_m.soc" +``` + +3. Create the Kconfig files in `//device/board/talkweb` by referring to the following directory structure: + +``` +. +├── Kconfig.liteos_m.boards +├── Kconfig.liteos_m.defconfig.boards +├── Kconfig.liteos_m.shields +└── niobe407 + ├── Kconfig.liteos_m.board --- Board configuration items + ├── Kconfig.liteos_m.defconfig.board --- Default board configuration items + └── liteos_m + └── config.gni +``` + +4. Modify the `Kconfig` files. + + - Add the following information to the `//device/board/talkweb/Kconfig.liteos_m.boards` file: + + ``` + if SOC_STM32F407 + orsource "niobe407/Kconfig.liteos_m.board" --- Load the definition of the specified board directory based on SoC definitions. + endif + ``` + + - Add the following information to the `//device/board/talkweb/Kconfig.liteos_m.defconfig.boards` file: + + ``` + orsource "*/Kconfig.liteos_m.defconfig.board" + ``` + + - Add the following information to the `//device/board/talkweb/Kconfig.liteos_m.defconfig.boards` file: + + ``` + orsource "shields/Kconfig.liteos_m.shields" + ``` + + - Add the following information to the `//device/board/talkweb/niobe407/Kconfig.liteos_m.board` file: + + ``` + menuconfig BOARD_NIOBE407 + bool "select board niobe407" + depends on SOC_STM32F407 --- niobe407 uses the SoC of STM32F407. Only when the SoC is selected, the configuration items of niobe407 are available and can be selected. + ``` + + - Add the following information to the `//device/board/talkweb/niobe407/Kconfig.liteos_m.defconfig.board` file: + + ``` + if BOARD_NIOBE407 + --- Used to add the default configuration of BOARD_NIOBE407. + endif #BOARD_NIOBE407 + ``` + +5. Create Kconfig files in the `//device/soc/st` directory by referring to the following directory structure, and copy the **Drivers** directory in the project generated by `STM32CubeMX` to the `stm32f4xx/sdk` directory. + + ``` + . + ├── Kconfig.liteos_m.defconfig + ├── Kconfig.liteos_m.series + ├── Kconfig.liteos_m.soc + └── stm32f4xx + ├── Kconfig.liteos_m.defconfig.series + ├── Kconfig.liteos_m.defconfig.stm32f4xx + ├── Kconfig.liteos_m.series + ├── Kconfig.liteos_m.soc + └── sdk + └── Drivers + ├── CMSIS + └── STM32F4xx_HAL_Driver + ``` + +6. Modify the Kconfig files. + + - Add the following information to the `//device/soc/st/Kconfig.liteos_m.defconfig` file: + + ``` + rsource "*/Kconfig.liteos_m.defconfig.series" + ``` + + - Add the following information to the `//device/soc/st/Kconfig.liteos_m.series` file: + + ``` + rsource "*/Kconfig.liteos_m.series" + ``` + + - Add the following information to the `//device/soc/st/Kconfig.liteos_m.soc` file: + + ``` + config SOC_COMPANY_STMICROELECTRONICS + bool + if SOC_COMPANY_STMICROELECTRONICS + config SOC_COMPANY + default "st" + rsource "*/Kconfig.liteos_m.soc" + endif # SOC_COMPANY_STMICROELECTRONICS + ``` + + - Add the following information to the `//device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.series` file: + + ``` + if SOC_SERIES_STM32F4xx + rsource "Kconfig.liteos_m.defconfig.stm32f4xx" + config SOC_SERIES + string + default "stm32f4xx" + endif + ``` + + - Add the following information to the `//device/soc/st/stm32f4xx/Kconfig.liteos_m.defconfig.stm32f4xx` file: + + ``` + config SOC + string + default "stm32f4xx" + depends on SOC_STM32F4xx + ``` + + - Add the following information to the `//device/soc/st/stm32f4xx/Kconfig.liteos_m.series` file: + + ``` + config SOC_SERIES_STM32F4xx + bool "STMicroelectronics STM32F4xx series" + select ARCH_ARM + select SOC_COMPANY_STMICROELECTRONICS + select CPU_CORTEX_M4 + help + Enable support for STMicroelectronics STM32F4xx series + ``` + + - Add the following information to the `//device/soc/st/stm32f4xx/Kconfig.liteos_m.soc` file: + + ``` + choice + prompt "STMicroelectronics STM32F4xx series SoC" + depends on SOC_SERIES_STM32F4xx + config SOC_STM32F407 + bool "SoC STM32F407" + endchoice + ``` + +7. Run the `make menuconfig` command in the `kernel/liteos_m` directory to select `SoC Series`. + + ![board make menuconfig](figures/niobe407_menuconfig.png) + + The result is automatically saved in `$(PRODUCT_PATH)/kernel_configs/debug.config`. The saved result will be exported when you run the `make menuconfig` command next time. + +### BUILD.gn File Adaptation + +To quickly get familiar with **gn** compilation and adaptation, you are advised to read [LiteOS-M Kernel BUILD.gn Compilation Guide](https://gitee.com/caoruihong/kernel_liteos_m/wikis/LiteOS-M%E5%86%85%E6%A0%B8BUILD.gn%E7%BC%96%E5%86%99%E6%8C%87%E5%8D%97). + +**(Note that no tab is allowed in the BUILD.gn file. All tabs are replaced with spaces.)** + +1. In `kernel/liteos_m/BUILD.gn`, see that the compilation entries of `Board` and `SoC` are specified through `deps`. + + ``` + deps += [ "//device/board/$device_company" ] --- Corresponds to **//device/board/talkweb**. + deps += [ "//device/soc/$LOSCFG_SOC_COMPANY" ] --- Corresponds to **//device/soc/st**. + ``` + +2. Add the following information to the `//device/board/talkweb/BUILD.gn` file: + + ``` + if (ohos_kernel_type == "liteos_m") { + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ "niobe407" ] + } + } + ``` + +3. Create a **BUILD.gn** file in the **niobe407** directory. To facilitate management, use the directory name as the module name. + + ``` + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ + "liteos_m", + ] + } + ``` + +4. Copy the files in the **Core** directory of the sample project generated by STM32CubeMX, `startup_stm32f407xx.s`, and `STM32F407IGTx_FLASH.ld` link file to the `//device/board/talkweb/niobe407/liteos_m/` directory, create `BUILD.gn` in the directory, and add the following content: + + ``` + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + kernel_module(module_name) { + sources = [ + "startup_stm32f407xx.s", + "Src/main.c", + "Src/stm32f4xx_hal_msp.c", + "Src/stm32f4xx_it.c", + "Src/system_stm32f4xx.c", + ] + include_dirs = [ + "Inc", + ] + } + + config("public") { + ldflags = [ + "-Wl,-T" + rebase_path("STM32F407IGTx_FLASH.ld"), + "-Wl,-u_printf_float", + ] + libs = [ + "c", + "m", + "nosys", + ] + } + ``` + +5. Configure `(Top) → Compat → Choose libc implementation` in **make menuconfig** and select `newlibc`. + +6. The **\_write** function has the same name as the kernel file operation function. As a result, the compilation fails. Use another method to adapt the **printf** function. In this example, the **_write** function is deleted from the **main.c** file, and the **printf** function is used to perform the serial port printing test as follows: + + ``` + uint8_t test[]={"hello niobe407!!\r\n"}; + int len = strlen(test); + HAL_UART_Transmit(&huart1, (uint8_t *)test, len, 0xFFFF); + ``` + +7. The same is true for `//device/soc/st/BUILD.gn`. The file is included layer by layer based on the directory structure. The file to be compiled and compilation parameters are specified in `//device/soc/st/stm32f4xx/sdk/BUILD.gn` using the `kernel_module` template. The following is an example: + + ``` + import("//kernel/liteos_m/liteos.gni") + module_name = "stm32f4xx_sdk" + kernel_module(module_name) { + asmflags = board_asmflags + sources = [ + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_exti.c", + "Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c", + ] + } + #Specify the global header file search path. + config("public") { + include_dirs = [ + "Drivers/STM32F4xx_HAL_Driver/Inc", + "Drivers/CMSIS/Device/ST/STM32F4xx/Include", + ] + } + ``` + +### config.gni File Adaptation + +In the precompilation phase, the **config.gni** file is created in the `//device/board/talkweb/niobe407/liteos_m` directory. The **config.gni** file is the header file of the GN script and can be regarded as the global configuration file for project building. The file contains important information such as the CPU model, cross compilation toolchain, global compilation, and link parameters. + +``` +# Kernel type, e.g. "linux", "liteos_a", "liteos_m". +kernel_type = "liteos_m" + +# Kernel version. +kernel_version = "3.0.0" + +# Board CPU type, e.g. "cortex-a7", "riscv32". +board_cpu = "cortex-m4" + +# Board arch, e.g. "armv7-a", "rv32imac". +board_arch = "" + +# Toolchain name used for system compiling. +# E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang, riscv32-unknown-elf. +# Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toolchain. +board_toolchain = "arm-none-eabi-gcc" + +use_board_toolchain = true + +# The toolchain path installed, it's not mandatory if you have added toolchain path to your ~/.bashrc. +board_toolchain_path = "" + +# Compiler prefix. +board_toolchain_prefix = "arm-none-eabi-" + +# Compiler type, "gcc" or "clang". +board_toolchain_type = "gcc" + +#Debug compiler optimization level options +board_opt_flags = [ + "-mcpu=cortex-m4", + "-mthumb", + "-mfpu=fpv4-sp-d16", + "-mfloat-abi=hard", +] + +# Board related common compile flags. +board_cflags = [ + "-Og", + "-Wall", + "-fdata-sections", + "-ffunction-sections", + "-DSTM32F407xx", +] +board_cflags += board_opt_flags + +board_asmflags = [ + "-Og", + "-Wall", + "-fdata-sections", + "-ffunction-sections", +] +board_asmflags += board_opt_flags + +board_cxx_flags = board_cflags + +board_ld_flags = board_opt_flags + +# Board related headfiles search path. +board_include_dirs = [ "//utils/native/lite/include" ] + +# Board adapter dir for OHOS components. +board_adapter_dir = "" +``` + +The settings of parameters such as **board_opt_flags**, **board_cflags**, and **board_asmflags** are defined as follows. You can extract the parameters from the `Makefile` file in the project generated by STM32CubeMX by referring to the following: +``` +**board_opt_flags**: compiler-related options, such as the chip architecture, floating-point type, and compilation debugging optimization level. +**board_asmflags**: assembly compilation options, corresponding to the **ASFLAGS** variable in **Makefile**. +**board_cflags**: C code compilation options, corresponding to the **CFLAGS** variable in **Makefile**. +**board_cxx_flags**: C++ code compilation options, corresponding to the **CXXFLAGS** variable in **Makefile**. +**board_ld_flags**: link options, corresponding to the **LDFLAGS** variable in **Makefile**. +``` + +### Kernel Subsystem Adaptation + +Add the kernel subsystem and related configurations to the `//vendor/talkweb/niobe407/config.json` file as follows: + +``` +{ + "product_name": "niobe407", + "type": "mini", + "version": "3.0", + "device_company": "talkweb", + "board": "niobe407", + "kernel_type": "liteos_m", + "kernel_version": "3.0.0", + "subsystems": [ + { + "subsystem": "kernel", + "components": [ + { + "component": "liteos_m" + } + ] + } + ], + "product_adapter_dir": "", + "third_party_dir": "//third_party" +} +``` + +### config.gni File Adaptation + +The `//kernel/liteos_m/kernel/include/los_config.h` file contains a header file named **target_config.h**. If this header file does not exist, a compilation error occurs. + +This header file is used to define macros related to the SoC. You can create an empty header file and determine the macros to be defined based on the compilation error message. It has been verified that the kernel can be compiled successfully after the `LOSCFG_BASE_CORE_TICK_RESPONSE_MAX` macro is defined and the `stm32f4xx.h` header file is included for Cortex-M4 core adaptation. + +If you do not know how to configure, see the `//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h` configuration in the VM QEMU example. + +``` +#ifndef _TARGET_CONFIG_H +#define _TARGET_CONFIG_H + +#define LOSCFG_BASE_CORE_TICK_RESPONSE_MAX 0xFFFFFFUL +#include "stm32f4xx.h" // Contains a large number of macro definitions of the STM32f4. + +#endif +``` + +The macro definition `LOSCFG_BASE_CORE_TICK_RESPONSE_MAX` is the configuration in the `//device/qemu/arm_mps2_an386/liteos_m/board/target_config.h` file, and `//device/qemu/arm_mps2_an386` is the VM project of `cortex-m4`. These can be used as references. + +### Kernel Startup Adaptation + +The kernel subsystem is compiled successfully, and the **OHOS_Image.bin** file is generated in the **out** directory. Burn the generated **OHOS_Image.bin** file to the development board to check whether the board can start and run properly. If the correct information output by the serial port in the main function can be printed, start kernel startup adaptation. + +1. Allocate memory for **liteos_m** and adapt the memory allocation function. + + In the `//kernel/liteos_m/kernel/src/mm/los_memory.c` file, the `OsMemSystemInit` function initializes the memory through LOS_MemInit. Several key macros need to be specified. Add them to `target_config.h`. + + ``` + extern unsigned int __los_heap_addr_start__; + extern unsigned int __los_heap_addr_end__; + #define LOSCFG_SYS_EXTERNAL_HEAP 1 + #define LOSCFG_SYS_HEAP_ADDR ((void *)&__los_heap_addr_start__) + #define LOSCFG_SYS_HEAP_SIZE (((unsigned long)&__los_heap_addr_end__) - ((unsigned long)&__los_heap_addr_start__)) + ``` + + The `__los_heap_addr_start__` and `__los_heap_addr_end__` variables are defined in the `STM32F407IGTx_FLASH.ld` link file. Change the content in the braces of **_user_heap_stack** to the following: + + ``` + ._user_heap_stack : + { + . = ALIGN(0x40); + __los_heap_addr_start__ = .; + __los_heap_addr_end__ = ORIGIN(RAM) + LENGTH(RAM); + } >RAM + ``` + + In addition, adapt the memory allocation function. The memory allocation functions such as **_malloc_r** have been implemented in the kernel, so you need to replace the memory allocation function in the standard library with that in the kernel. Modify the **board_ld_flags** variable in `//device/board/talkweb/niobe407/liteos_m/config.gni` as follows: + + ``` + board_ld_flags = [ + "-Wl,--wrap=_calloc_r", + "-Wl,--wrap=_malloc_r", + "-Wl,--wrap=_realloc_r", + "-Wl,--wrap=_reallocf_r", + "-Wl,--wrap=_free_r", + "-Wl,--wrap=_memalign_r", + "-Wl,--wrap=_malloc_usable_size_r", + ] + board_ld_flags += board_opt_flags + ``` + +2. printf Function Adaptation + + To facilitate subsequent debugging, the **printf** function needs to be adapted first. Only simple adaptation is performed here. For details, see the source code of other development boards. + + Create the **dprintf.c** file in the same directory as **main.c**. The file content is as follows: + + ``` + #include + #include "los_interrupt.h" + #include + + extern UART_HandleTypeDef huart1; + + INT32 UartPutc(INT32 ch, VOID *file) + { + char RL = '\r'; + if (ch =='\n') { + HAL_UART_Transmit(&huart1, &RL, 1, 0xFFFF); + } + return HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); + } + + static void dputs(char const *s, int (*pFputc)(int n, FILE *cookie), void *cookie) + { + unsigned int intSave; + + intSave = LOS_IntLock(); + while (*s) { + pFputc(*s++, cookie); + } + LOS_IntRestore(intSave); + } + + int printf(char const *fmt, ...) + { + char buf[1024] = { 0 }; + va_list ap; + va_start(ap, fmt); + int len = vsnprintf_s(buf, sizeof(buf), 1024 - 1, fmt, ap); + va_end(ap); + if (len > 0) { + dputs(buf, UartPutc, 0); + } else { + dputs("printf error!\n", UartPutc, 0); + } + return len; + } + ``` + + Add the **dprintf.c** file to the **BUILD.gn** script for compilation. + + ``` + kernel_module(module_name) { + sources = [ + "startup_stm32f407xx.s", + ] + + sources += [ + "Src/main.c", + "Src/dprintf.c", + "Src/stm32f4xx_hal_msp.c", + "Src/stm32f4xx_it.c", + "Src/system_stm32f4xx.c", + ] + } + ``` + + After the serial port is initialized, use the **printf** function to generate information and test whether the adaptation is successful. + +3. Call **LOS_KernelInit** to initialize the kernel and enter task scheduling. + + After the serial port is initialized in the main function, call `LOS_KernelInit` for kernel initialization, create a task instance, and start task scheduling. + + ``` + #include "los_task.h" + + UINT32 ret; + ret = LOS_KernelInit(); // Initialize the kernel. + if (ret == LOS_OK) { + TaskSample(); // Sample task function, in which a thread task is created. + LOS_Start(); // Start task scheduling. Program execution is blocked here, and the kernel takes over the scheduling. + } + ``` + + The content of the `TaskSample()` function is as follows: + + ``` + VOID TaskSampleEntry2(VOID) + { + while (1) { + printf("TaskSampleEntry2 running...\n"); + (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */ + } + } + + VOID TaskSampleEntry1(VOID) + { + while (1) { + printf("TaskSampleEntry1 running...\n"); + (VOID)LOS_TaskDelay(2000); /* 2000 millisecond */ + } + } + VOID TaskSample(VOID) + { + UINT32 uwRet; + UINT32 taskID1; + UINT32 taskID2; + TSK_INIT_PARAM_S stTask = {0}; + + stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry1; + stTask.uwStackSize = 0x1000; + stTask.pcName = "TaskSampleEntry1"; + stTask.usTaskPrio = 6; /* Os task priority is 6 */ + uwRet = LOS_TaskCreate(&taskID1, &stTask); + if (uwRet != LOS_OK) { + printf("Task1 create failed\n"); + } + + stTask.pfnTaskEntry = (TSK_ENTRY_FUNC)TaskSampleEntry2; + stTask.uwStackSize = 0x1000; + stTask.pcName = "TaskSampleEntry2"; + stTask.usTaskPrio = 7; /* Os task priority is 7 */ + uwRet = LOS_TaskCreate(&taskID2, &stTask); + if (uwRet != LOS_OK) { + printf("Task2 create failed\n"); + } + } + ``` + +After the kernel startup is adapted, you can view the following information generated by serial port debugging: + + ![niobe407_boot](figures/niobe407_boot.png) + +In the future, detailed adaptation verification needs to be performed on the entire basic kernel. + +### Basic Kernel Function Adaptation + +The adaptation items of basic kernel functions include [interrupt management](../kernel/kernel-mini-basic-interrupt.md), [task management](../kernel/kernel-mini-basic-task.md), [memory management](../kernel/kernel-mini-basic-memory.md), [kernel communication mechanism] (../kernel/kernel-mini-basic-ipc-event.md), [time management](../kernel/kernel-mini-basic-time.md), and [software timer](../kernel/kernel-mini-basic-soft.md). You can verify basic kernel functions by referring to the programming samples in the links. If any problem is found during the verification, perform specific adaptation. + +According to the information generation interval in the previous section, the delay time of the `LOS_TaskDelay` function is inaccurate. You can define the following macros in `target_config.h` to adapt the kernel clock: + + ``` + #define OS_SYS_CLOCK 168000000 + #define LOSCFG_BASE_CORE_TICK_PER_SECOND (1000UL) + ``` +Most adaptation methods of other basic kernel functions are based on the macro definition in `target_config.h`. You need to explore the adaptation methods based on the source code in `//kernel/liteos_m`. + +## Porting and Adaptation of LittleFS + + The `Niobe407` development board is connected to an external 16 MB SPI flash. The Niobe407 performs LittleFS adaptation based on the SPI flash. + +The kernel has adapted LittleFS. You only need to enable the configuration in Kconfig files and adapt the following LittleFS APIs: + +``` + int32_t LittlefsRead(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) + { + W25x_BufferRead(buffer, cfg->context + cfg->block_size * block + off, size); + return LFS_ERR_OK; + } + + int32_t LittlefsProg(const struct lfs_config *cfg, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) + { + W25x_BufferWrite((uint8_t *)buffer,cfg->context + cfg->block_size * block + off,size); + return LFS_ERR_OK; + } + + int32_t LittlefsErase(const struct lfs_config *cfg, lfs_block_t block) + { + W25x_SectorErase(cfg->context + cfg->block_size * block); + return LFS_ERR_OK; + } + + int32_t LittlefsSync(const struct lfs_config *cfg) + { + return LFS_ERR_OK; + } +``` + +Functions such as `W25x_BufferRead` are APIs for SPI flash reading and writing. The implementation varies according to the SPI flash model. For details about the implementation of the SPI flash operation of Niobe407, please refer to `//device/board/talkweb/niobe407/liteos_m/drivers/spi_flash/src/w25qxx.c`. + +The SPI uses the HDF, and LittleFS depends on the SPI driver. To facilitate the configuration of the file system, you can add the LittleFS configuration to the .hcs file. For details, please refer to the `//device/board/talkweb/niobe407/liteos_m/hdf_config/hdf_littlefs.hcs` file. + +``` +misc { + littlefs_config { + match_attr = "littlefs_config"; + mount_points = ["/talkweb"]; + partitions = [0x800000]; + block_size = [4096]; + block_count = [256]; + } +} +``` + +## Board-Level Driver Porting + +Driver adaptation files are stored in `//drivers/adapter/platform`, including `gpio`, `i2c`, `pwm`, `spi`, `uart`, and `watchdog` drivers. These files are loaded using the `HDF` mechanism. This section uses `pwm` as an example. + +### PWM Driver Adaptation +In the Hardware Driver Foundation (HDF), the PWM module uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device. + +- API Description + + ``` + 1. pwm open initialization function: DevHandle PwmOpen(uint32_t num); + Parameters: + **num**: PWM device ID. + **return**: PWM device handle if the operation is successful; **NULL** otherwise. + 2. pwm close deinitialization function: void PwmClose(DevHandle handle); + Parameters: + **handle**: PWM device handle. + **return**: none + 3. PWM device parameter set function: int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config); + Parameters: + **handle**: PWM device handle. + ***config**: parameter pointer. + **return**: **0** if the setting is successful; negative number otherwise. + ``` + +- PWM HDF HCS Configuration File Parsing + + The `device_info.hcs` file is stored in `//device/board/talkweb/niobe407/liteos_m/hdf_config/device_info.hcs`. The following example shows how to use the TIM2, TIM3, and TIM7 timers to output PWM signals: + + ``` + device_pwm1 :: device { + pwm1 :: deviceNode { + policy = 2; + priority = 100; + moduleName = "ST_HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_1"; + deviceMatchAttr = "config_pwm1"; + } + } + device_pwm2 :: device { + pwm2 :: deviceNode { + policy = 2; + priority = 100; + moduleName = "ST_HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_2"; + deviceMatchAttr = "config_pwm2"; + } + } + device_pwm7 :: device { + pwm7 :: deviceNode { + policy = 2; + priority = 100; + moduleName = "ST_HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_7"; + deviceMatchAttr = "config_pwm7"; + } + } + ``` + + The `hdf.hcs` file is stored in `//device/board/talkweb/niobe407/liteos_m/hdf_config/hdf.hcs`. You can configure the following information about the TIM timer in this file: + + ``` + --- Note: The frequency of tim2-tim7 and tim12-tim14 is 84 MHz, and that of TIM1 and TIM8 to TIM11 is 168 MHz. tim6 and tim7 cannot output PWM signals. + --- tim1 to tim5 and tim8 have four channels, tim9 and tim12 have two channels, and tim10, tim11, tim13, and tim14 have only one channel. + + pwm_config { + pwm1_config { + match_attr = "config_pwm1"; + pwmTim = 1; --- timer ID. It means tim2. (0: tim1, 1: tim2, ... tim6 and tim7 are unavailable.) + pwmCh = 3; --- Number of channels. (0: ch1, 1: ch2, 2: ch3, 3: ch4) + prescaler = 4199; --- prescaler. For example, if the tim2 frequency is 84 MHz, 20 kHz (84 MHz/(4199 + 1)) is used as the reference. + } + pwm2_config { + match_attr = "config_pwm2"; + pwmTim = 2; + pwmCh = 0; + prescaler = 8399; + } + pwm3_config { + match_attr = "config_pwm7"; + pwmTim = 7; + pwmCh = 0; + prescaler = 8399; + } + } + ``` + +For details about the `hdf pwm` adaptation code, see **//drivers/adapter/platform/pwm/pwm_stm32f4xx.c**. + +For details about the `hdf pwm` usage example, see **//device/board/talkweb/niobe407/applications/206_hdf_pwm**. + +## Subsystem Adaptation + +The `OpenHarmony` subsystem adaptation consists of two parts: + +- Add the related subsystem and component to `config.json`, so that they can be included in compilation by the compilation system. +- Perform hardware adaptation or optional software function adaptation for the `HAL` layer API of the component. + +### lwIP Adaptation + +The `LiteOS-M kernel` can use the Kconfig configuration to enable the lwIP to participate in compilation and specify the `lwip` compilation adaptation directory in the `kernel` component. The sample code is as follows: + +``` +{ + "subsystem": "kernel", + "components": [ + { + "component": "liteos_m", + "features": [ + "ohos_kernel_liteos_m_lwip_path = \"//device/board/talkweb/niobe407/liteos_m/lwip_adapter\"" --- Specify the adaptation path. + ] + } + ] +} +``` + +In the specified compilation adaptation directory, intrude and modify the header file configuration of the lwIP third-party library in `#include_next "lwip/lwipopts.h"` mode. The detailed adaptation procedure for wired Ethernet lwIP adaptation will be supplemented later. + +### Startup Subsystem Adaptation + +For the startup subsystem, adapt the `bootstrap_lite` and `syspara_lite` components. Add the corresponding configuration items to the `//vendor/talkweb/niobe407/config.json` file, as shown below: + +``` +{ + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite", + "features": [] + }, + { + "component": "syspara_lite", + "features": [] + } + ] +} +``` + +When adapting the `bootstrap_lite` component, manually add the following information to the link file `//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld`: + +``` +__zinitcall_bsp_start = .; +KEEP (*(.zinitcall.bsp0.init)) +KEEP (*(.zinitcall.bsp1.init)) +KEEP (*(.zinitcall.bsp2.init)) +KEEP (*(.zinitcall.bsp3.init)) +KEEP (*(.zinitcall.bsp4.init)) +__zinitcall_bsp_end = .; +__zinitcall_device_start = .; +KEEP (*(.zinitcall.device0.init)) +KEEP (*(.zinitcall.device1.init)) +KEEP (*(.zinitcall.device2.init)) +KEEP (*(.zinitcall.device3.init)) +KEEP (*(.zinitcall.device4.init)) +__zinitcall_device_end = .; +__zinitcall_core_start = .; +KEEP (*(.zinitcall.core0.init)) +KEEP (*(.zinitcall.core1.init)) +KEEP (*(.zinitcall.core2.init)) +KEEP (*(.zinitcall.core3.init)) +KEEP (*(.zinitcall.core4.init)) +__zinitcall_core_end = .; +__zinitcall_sys_service_start = .; +KEEP (*(.zinitcall.sys.service0.init)) +KEEP (*(.zinitcall.sys.service1.init)) +KEEP (*(.zinitcall.sys.service2.init)) +KEEP (*(.zinitcall.sys.service3.init)) +KEEP (*(.zinitcall.sys.service4.init)) +__zinitcall_sys_service_end = .; +__zinitcall_sys_feature_start = .; +KEEP (*(.zinitcall.sys.feature0.init)) +KEEP (*(.zinitcall.sys.feature1.init)) +KEEP (*(.zinitcall.sys.feature2.init)) +KEEP (*(.zinitcall.sys.feature3.init)) +KEEP (*(.zinitcall.sys.feature4.init)) +__zinitcall_sys_feature_end = .; +__zinitcall_run_start = .; +KEEP (*(.zinitcall.run0.init)) +KEEP (*(.zinitcall.run1.init)) +KEEP (*(.zinitcall.run2.init)) +KEEP (*(.zinitcall.run3.init)) +KEEP (*(.zinitcall.run4.init)) +__zinitcall_run_end = .; +__zinitcall_app_service_start = .; +KEEP (*(.zinitcall.app.service0.init)) +KEEP (*(.zinitcall.app.service1.init)) +KEEP (*(.zinitcall.app.service2.init)) +KEEP (*(.zinitcall.app.service3.init)) +KEEP (*(.zinitcall.app.service4.init)) +__zinitcall_app_service_end = .; +__zinitcall_app_feature_start = .; +KEEP (*(.zinitcall.app.feature0.init)) +KEEP (*(.zinitcall.app.feature1.init)) +KEEP (*(.zinitcall.app.feature2.init)) +KEEP (*(.zinitcall.app.feature3.init)) +KEEP (*(.zinitcall.app.feature4.init)) +__zinitcall_app_feature_end = .; +__zinitcall_test_start = .; +KEEP (*(.zinitcall.test0.init)) +KEEP (*(.zinitcall.test1.init)) +KEEP (*(.zinitcall.test2.init)) +KEEP (*(.zinitcall.test3.init)) +KEEP (*(.zinitcall.test4.init)) +__zinitcall_test_end = .; +__zinitcall_exit_start = .; +KEEP (*(.zinitcall.exit0.init)) +KEEP (*(.zinitcall.exit1.init)) +KEEP (*(.zinitcall.exit2.init)) +KEEP (*(.zinitcall.exit3.init)) +KEEP (*(.zinitcall.exit4.init)) +__zinitcall_exit_end = .; +``` + +Adding the preceding content is because external APIs provided by `bootstrap_init` will be saved to the link segment. For details, see `//utils/native/lite/include/ohos_init.h`. The following table lists the automatic initialization macros of main services. + +| API | Description | +| ---------------------- | -------------------------------- | +| SYS_SERVICE_INIT(func) | Entry for initializing and starting a core system service.| +| SYS_FEATURE_INIT(func) | Entry for initializing and starting a core system feature.| +| APP_SERVICE_INIT(func) | Entry for initializing and starting an application-layer service. | +| APP_FEATURE_INIT(func) | Entry for initializing and starting an application-layer feature. | + + + +The **lib** file compiled using the loaded components needs to be manually add to the forcible link. + +If the `bootstrap_lite` component is configured in the `//vendor/talkweb/niobe407/config.json` file: + +``` + { + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite" + }, + ... + ] + }, +``` + +​ The `bootstrap_lite` component will compile the `//base/startup/bootstrap_lite/services/source/bootstrap_service.c` file. In this file, `SYS_SERVICE_INIT` is used to inject the `Init` function symbol to `__zinitcall_sys_service_start` and `__zinitcall_sys_service_end`. Since the `Init` function does not support explicit call, you need to forcibly link it to the final image. The sample code is as follows: + +``` +static void Init(void) +{ + static Bootstrap bootstrap; + bootstrap.GetName = GetName; + bootstrap.Initialize = Initialize; + bootstrap.MessageHandle = MessageHandle; + bootstrap.GetTaskConfig = GetTaskConfig; + bootstrap.flag = FALSE; + SAMGR_GetInstance()->RegisterService((Service *)&bootstrap); +} +SYS_SERVICE_INIT(Init); --- Forcible link to the generated lib file is required if **SYS_INIT** is used for startup. +``` + +​ The `//base/startup/bootstrap_lite/services/source/BUILD.gn` file describes how to generate `libbootstrap.a` in `//out/niobe407/niobe407/libs` as follows: + +``` +static_library("bootstrap") { + sources = [ + "bootstrap_service.c", + "system_init.c", + ] + ... +``` + +When the `syspara_lite` component is adapted, system parameters will be written into the file for persistent storage. In the mini system, file operation APIs include `POSIX` and `HalFiles`. + +For access to the file system in the kernel, use the `POSIX` API, which means you need to add `enable_ohos_startup_syspara_lite_use_posix_file_api = true` to the `features` field. + +If you are using the `HalFiles` API, no modification is required. +### DFX Subsystem Adaptation + +To adapt the `DFX` subsystem, you need to add the `hilog_lite` and `hievent_lite` components to the `config.json` file. + +``` +{ + "subsystem": "hiviewdfx", + "components": [ + { + "component": "hilog_lite", + "features": [] + }, + { + "component": "hievent_lite", + "features": [] + } + ] +} +``` + +After the configuration is complete, you need to register the log output implementation function and add it for compilation. + +``` +bool HilogProc_Impl(const HiLogContent *hilogContent, uint32_t len) +{ + char tempOutStr[LOG_FMT_MAX_LEN]; + tempOutStr[0] = 0,tempOutStr[1] = 0; + if (LogContentFmt(tempOutStr, sizeof(tempOutStr), hilogContent) > 0) { + printf(tempOutStr); + } + return true; +} + +HiviewRegisterHilogProc(HilogProc_Impl); +``` + +### System Service Management Subsystem Adaptation + +To adapt the system service management subsystem, you need to add the `samgr_lite` component to the `config.json` file. + +``` +{ + "subsystem": "systemabilitymgr", + "components": [ + { + "component": "samgr_lite", + "features": [] + } + ] +} +``` + +In the mini system, the default size of the shared task stack configured for the `samgr_lite` is `2048`. During adaptation, you can use `config_ohos_systemabilitymgr_samgr_lite_shared_task_size` in features to reset the size of the shared task stack. + +``` +"config_ohos_systemabilitymgr_samgr_lite_shared_task_size = 4096" +``` + +### Security Subsystem Adaptation + +To adapt the security subsystem, you need to add the `huks` component to the `config.json` file. + +``` +{ + "subsystem": "security", + "components": [ + { + "component": "huks", + "features": [ + "huks_use_lite_storage = true", + "huks_use_hardware_root_key = true", + "huks_config_file = \"hks_config_lite.h\"", + "huks_key_store_path = \"storage\"" + ] + } + ] +} +``` + +During `huks` adaptation, the `huks_key_store_path` configuration item specifies the path for storing the key, and `huks_config_file` indicates the name of the configuration header file. + +### Utils Subsystem Adaptation + +To adapt the utils subsystem, you need to add the `kv_store`, `file`, and `os_dump` components to the `config.json` file. + +``` +{ + "subsystem": "utils", + "components": [ + { + "component": "file", + "features": [] + }, + { + "component": "kv_store", + "features": [ + "enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = false" + ] + }, + { + "component": "os_dump", + "features": [] + } + ] +}, +``` + +Similar to the adaptation of the `syspara_lite` component, when the `kv_store` component is adapted, the key-value pair is written to the file. In the mini system, file operation APIs include `POSIX` and `HalFiles`. The `POSIX` API is used for access to the file system in the kernel, which means you need to add `enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true` to the `features` field. If you are using the `HalFiles` API, no modification is required. + +### HDF Subsystem Adaptation + +Similar to startup subsystem adaptation, you need to manually add the following information to the link file `//device/board/talkweb/niobe407/liteos_m/STM32F407IGTx_FLASH.ld`: + +``` +_hdf_drivers_start = .; +KEEP(*(.hdf.driver)) +_hdf_drivers_end = .; +``` + +After the kernel is initialized, call the **DeviceManagerStart** function. After the execution is complete, the HDF API can be called to control peripherals. + +``` +#include "devmgr_service_start.h" --- Note that this header file must be included. + +#ifdef LOSCFG_DRIVERS_HDF + DeviceManagerStart(); +#endif +``` + +The `devmgr_service_start.h` header file is stored in `//drivers/framework/core/common/include/manager`. To ensure that the header file can be found during compilation, add it to **include_dirs**. + +### Adaptation of the XTS Compatibility Test Subsystem + +#### Product Compatibility Specifications + +For details about the product compatibility specifications, see [Introduction to Product Compatibility](https://gitee.com/openharmony-sig/compatibility/tree/master). + +#### Adding an XTS Subsystem + +For details about the `XTS` test reference, see [XTS Reference](../device-test/xts.md). To adapt the `XTS` subsystem, you need to add the `xts_acts` and `xts_tools` components to the `config.json` file. The configuration is as follows: + + { + "subsystem": "xts", + "components": [ + { + "component": "xts_acts", + "features": [] + }, + { + "component": "xts_tools", + "features": [] + } + ] + } + +You can specify the following attributes in the features array of the **xts_acts** component: + +- `config_ohos_xts_acts_utils_lite_kv_store_data_path`: name of the root directory of the mounted file system. +- `enable_ohos_test_xts_acts_use_thirdparty_lwip`: If the source code in the `thirdparty/lwip` directory is used for compilation, set this parameter to `true`. Otherwise, set this parameter to `false`. + +#### Compiling the XTS + +After the **config.json** file is configured, `hb build` does not compile the XTS. The XTS is compiled only when the debug version is compiled. In addition, the suite static library to be tested needs to be forcibly linked. + +Add the following content to the **BUILD.gn** script that contains `kernel_module` in `//device/board/talkweb/liteos_m`: + +``` +config("public") { + if (build_xts) { + lib_dirs = [ "$root_out_dir/libs" ] + ldflags += [ + "-Wl,--whole-archive", --- After whole-archive is enabled, functions and variables of the static library can be output to the dynamic library. + "-lbootstrap", + "-lbroadcast", + "-lhctest", + + #Utils + # "-lmodule_ActsUtilsFileTest", + # "-lmodule_ActsKvStoreTest", + + #DFX + "-lmodule_ActsDfxFuncTest", + "-lmodule_ActsHieventLiteTest", + + #Startup + # "-lmodule_ActsBootstrapTest", + # "-lmodule_ActsParameterTest", + + #Distributed scheduling + # "-lmodule_ActsSamgrTest", + + "-Wl,--no-whole-archive", --- Disable the whole-archive feature. + ] + } +} +``` + +The memory of the Niobe407 board is limited. Therefore, the XTS test needs to be performed by suite. Run the following compilation command to generate the firmware that contains the XTS test: + +``` +hb build -f -b debug --gn-args build_xts=true +``` + +In addition, you need to modify the `//vendor/talkweb/niobe407/hals/utils/sys_param/hal_sys_param.c` file to correctly define these character strings. + +``` +static const char OHOS_DEVICE_TYPE[] = {"Evaluation Board"}; +static const char OHOS_DISPLAY_VERSION[] = {"OpenHarmony 3.1"}; +static const char OHOS_MANUFACTURE[] = {"Talkweb"}; +static const char OHOS_BRAND[] = {"Talkweb"}; +static const char OHOS_MARKET_NAME[] = {"Niobe"}; +static const char OHOS_PRODUCT_SERIES[] = {"Niobe"}; +static const char OHOS_PRODUCT_MODEL[] = {"Niobe407"}; +static const char OHOS_SOFTWARE_MODEL[] = {"1.0.0"}; +static const char OHOS_HARDWARE_MODEL[] = {"2.0.0"}; +static const char OHOS_HARDWARE_PROFILE[] = {"RAM:192K,ROM:1M,ETH:true"}; +static const char OHOS_BOOTLOADER_VERSION[] = {"twboot-v2022.03"}; +static const char OHOS_ABI_LIST[] = {"armm4_hard_fpv4-sp-d16-liteos"}; +static const char OHOS_SERIAL[] = {"1234567890"}; // provided by OEM. +``` + +#### Verifying the XTS + +After the compilation is complete, burn the firmware to the board. After the XTS is executed, information such as `xx Tests xx Failures xx Ignored` is displayed. The following uses the Utils test as an example: + +``` +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:590:testKvStoreClearCache002:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:625:testKvStoreCacheSize001:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:653:testKvStoreCacheSize002:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:681:testKvStoreCacheSize003:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:709:testKvStoreMaxSize001:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:737:testKvStoreMaxSize002:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:765:testKvStoreMaxSize003:PASS +../../../test/xts/acts/utils_lite/kv_store_hal/src/kvstore_func_test.c:793:testKvStoreMaxSize004:PASS ++-------------------------------------------+ + +----------------------- +32 Tests 0 Failures 0 Ignored +OK +All the test suites finished! +``` diff --git a/en/device-dev/porting/porting-stm32mp15xx-on-smallsystem.md b/en/device-dev/porting/porting-stm32mp15xx-on-smallsystem.md new file mode 100644 index 0000000000000000000000000000000000000000..76820392c8015b557111e0986a82b32579c7e3e3 --- /dev/null +++ b/en/device-dev/porting/porting-stm32mp15xx-on-smallsystem.md @@ -0,0 +1,600 @@ +# Small System STM32MP1 SoC Porting Case + +This document describes how to port small development boards with screens based on the [BearPi-HM Micro development board](https://gitee.com/openharmony/device_board_bearpi) of the `STM32MP157` SoC from STMicroelectronics, so as to adapt components such as `ace_engine_lite`, `graphic_ui`, `aafwk_lite`, `appexecfwk_lite`, and `HDF` to the `OpenHarmony LiteOS-A` kernel. The porting architecture uses the solution where `Board` and `SoC` are separated. + +## Compilation and Building + +### Directory Planning + +This 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: + +``` +device +├── board --- Board vendor directory +│ └── bearpi --- Board vendor: BearPi +│ └── bearpi_hm_micro --- Board name: bearpi_hm_micro +└── soc --- SoC vendor directory + └── common --- Directory storing common HDF drivers. + └── st --- SoC vendor: STMicroelectronics + └── stm32mp1xx --- SoC series: stm32mp1xx +``` + +The planned product demo directory is as follows: + +``` +vendor +└── bearpi --- Vendor of the product demo, which is BearPi here + └── bearpi_hm_micro --- Product name: bearpi_hm_micro development board +``` + +### Precompilation Adaptation + +Before porting, you need to perform precompilation adaptation. + +The precompilation adaptation process mainly uses the `hb set` command to set environment variables such as the project root directory, board directory, product directory, and board vendor name, to make preparations for compilation. + +The procedure is as follows: + +1. Add the `config.json` file to the `vendor/bearpi/bearpi_hm_micro` directory to describe board and kernel information used by the product demo. The sample code is as follows: + + ``` + { + "product_name": "bearpi_hm_micro", --- Product name displayed when the **hb set** command is used. + "version": "3.0", --- Version of the system, which can be 1.0, 2.0, or 3.0. + "type": "small", --- System type, which can be mini, small, or standard. + "ohos_version": "OpenHarmony 3.0", --- OpenHarmony system version. + "device_company": "bearpi", --- Board vendor name, which is used to find the /device/board/bearpi directory during compilation. + "board": "bearpi_hm_micro", --- Board name, which is used to find the /device/board/bearpi/bearpi_hm_micro directory during compilation. + "kernel_type": "liteos_a", --- Kernel type. OpenHarmony supports multiple kernels, and one board may adapt to multiple kernels. Therefore, you need to specify a kernel for compilation. + "kernel_version": "", --- Kernel version. One board may adapt to multiple Linux kernel versions. Therefore, you need to specify a kernel version for compilation. + "subsystems": [ ] --- Subsystem to be built. + } + ``` + +2. Add the `config.gni` file to the `device/board/bearpi/bearpi_hm_micro/liteos_a` directory to describe board and kernel information used by the product demo. The sample code is as follows: + + ``` + # Kernel type, e.g. "linux", "liteos_a", "liteos_m". + kernel_type = "liteos_a" + + # Kernel version. + kernel_version = "" + + # Board CPU type, e.g. "cortex-a7", "riscv32". + board_cpu = "cortex-a7" + + # Board arch, e.g. "armv7-a", "rv32imac". + board_arch = "" + + # Toolchain name used for system compiling. + # E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang, riscv32-unknown-elf. + # Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toolchain. + board_toolchain = "" + + # The toolchain path installed, it's not mandatory if you have added toolchain path to your ~/.bashrc. + board_toolchain_path = "" + + # Compiler prefix. + board_toolchain_prefix = "" + + # Compiler type, "gcc" or "clang". + board_toolchain_type = "clang" + + # Board related common compile flags. + board_cflags = [ + "-mfloat-abi=softfp", + "-mfpu=neon-vfpv4", + ] + board_cxx_flags = [ + "-mfloat-abi=softfp", + "-mfpu=neon-vfpv4", + ] + board_ld_flags = [] + + # Board related headfiles search path. + board_include_dirs = [] + + # Board adapter dir for OHOS components. + board_adapter_dir = "//device/board/bearpi/bearpi_hm_micro/hal" + + # Sysroot path. + board_configed_sysroot = "" + + # Board storage type, it used for file system generation. + storage_type = "emmc" + ``` + +3. Verify that the `hb set` configuration is correct. The `hb set` configuration is correct if the output information is as shown in the following figure. + + Run `hb set`, enter the project root directory, and press **Enter**. The `hb` command will traverse all `config.json` files in the `//vendor//` directory and list all available product compilation options. In `config.json`, `product_name` is used to display the product name, and `device_company` and `board` are used to associate the `//device/board//` directory and match the `/config.gni` file. If multiple files are matched, the board adapts to multiple kernels. Then, `kernel_type` and `kernel_version` in `config.json` can be used to uniquely match `kernel_type` and `kernel_version` in `config.gni`, thus determining the board with a kernel to be compiled. + + ![hb set](figures/bearpi_hm_micro_hb_set.png) + + After you select a product and press **Enter**, a **ohos_config.json** file will be generated in the root directory. The file will list the product information to be compiled. You can also run the `hb env` command to view the selected precompilation environment variables. + + ![hb env](figures/bearpi_hm_micro_hb_env.png) + + + +## Kernel Porting + +Kernel porting requires `LiteOS-A Kconfig` adaptation, `gn` compilation and building, and minimum kernel startup adaptation. + +For details about the porting procedure, see [LiteOS-A kernel porting](porting-smallchip-kernel-a.md). +### Kconfig Adaptation +1. Add the chip, product name, and vendor name configurations to **//device/board/bearpi/bearpi_hm_micro/liteos_a/drivers/Kconfig**. + ``` + source "../../device/soc/st/common/platform/Kconfig" + + config PLATFORM + string + default "stm32mp157" if PLATFORM_STM32MP157 + + config PRODUCT_NAME + string "product name" + default "bearpi_hm_micro" if PRODUCT_BEARPI_HM_MICRO + + config DEVICE_COMPANY + string "vendor name" + default "st" if PLATFORM_STM32MP157 + + config TEE_ENABLE + bool "Enable TEE" + default n + depends on PLATFORM_STM32MP157 + help + Enable teeos in platform + ``` +2. Add driver configurations to **//device/soc/st/common/platform/Kconfig**. + ``` + config DRIVERS_MMC + depends on DRIVERS + bool "Enable MMC" + default y + depends on DRIVERS && FS_VFS + help + Answer Y to enable LiteOS support MMC driver. + + config DRIVERS_EMMC + depends on DRIVERS_MMC && PLATFORM_STM32MP157 + bool "Enable MMC0 support eMMC type" + + config DRIVERS_HI3881 + bool "Enable Hi3881 Host driver" + default n + depends on DRIVERS_HDF_WIFI + help + Answer Y to enable Hi3881 Host driver. + config HW_RANDOM_ENABLE + depends on DRIVERS_RANDOM + bool "Select hw random" + default y + help + Answer Y to select hw random. + ``` +3. Enable related configurations in **//vendor/bearpi/bearpi_hm_micro/kernel_configs/debug_tee.config**. + ``` + ... + LOSCFG_PLATFORM="stm32mp157" + LOSCFG_PRODUCT_NAME="bearpi_hm_micro" + LOSCFG_DEVICE_COMPANY="st" + # LOSCFG_PLATFORM_HI3516DV300 is not set + # LOSCFG_PLATFORM_HI3518EV300 is not set + # LOSCFG_PLATFORM_QEMU_ARM_VIRT_CA7 is not set + LOSCFG_PLATFORM_STM32MP157=y + LOSCFG_PRODUCT_BEARPI_HM_MICRO=y + LOSCFG_BOARD_CONFIG_PATH="device/board/bearpi/bearpi_hm_micro/liteos_a/board" + LOSCFG_TEE_ENABLE=y + ... + ``` +### GN Build Adaptation +1. Create **BUILD.gn** in **//device/board/bearpi/bearpi_hm_micro/liteos_a** and add the following code. This module depends on **board**, **drivers**, and **hdf_config**. + ``` + cmd = "if [ -f $product_path/hdf_config/BUILD.gn ]; then echo true; else echo false; fi" + HAVE_PRODUCT_CONFIG = + exec_script("//build/lite/run_shell_cmd.py", [ cmd ], "value") + + group("liteos_a") { + deps = [ + "board", + "drivers", + ] + if (HAVE_PRODUCT_CONFIG) { + deps += [ "$product_path/hdf_config" ] + } else { + deps += [ "hdf_config" ] + } + } + + config("public") { + configs = [ + "board:public", + "drivers:public", + ] + } + ``` +2. Create **BUILD.gn** in **//device/board/bearpi/bearpi_hm_micro/liteos_a/board** and add the following code: Compile **os_adapt.c** kernel startup code into the system. + ``` + import("//kernel/liteos_a/liteos.gni") + + module_name = "bsp_config" + + kernel_module(module_name) { + sources = [] + if (defined(LOSCFG_PLATFORM_ADAPT)) { + sources += [ "os_adapt/os_adapt.c" ] + } + } + + config("public") { + include_dirs = [ "." ] + include_dirs += [ "include" ] + include_dirs += [ "$LITEOSTOPDIR/drivers/block/disk/include" ] + include_dirs += + [ "$LITEOSTOPDIR/../../drivers/adapter/khdf/liteos/osal/include" ] + } + ``` +3. Create **BUILD.gn** in **//device/board/bearpi/bearpi_hm_micro/liteos_a/drivers**, add the following code, and compile the HDF driver under **device/soc/st/common/platform** into the system: + ``` + import("//drivers/adapter/khdf/liteos/hdf.gni") + + group("drivers") { + public_deps = [ "//device/soc/st/common/platform:drivers" ] + } + + config("public") { + configs = [ "//device/soc/st/common/platform:public" ] + } + + ``` +4. Create **BUILD.gn** in **//vendor/bearpi/bearpi_hm_micro/hdf_config**, add the following code, and compile the HCS configuration file into the system: + ``` + module_switch = defined(LOSCFG_DRIVERS_HDF) && !defined(LOSCFG_DRIVERS_HDF_TEST) + module_name = "libhdf_config" + hdf_driver(module_name) { + hcs_sources = [ "hdf.hcs" ] + } + + group("hdf_config") { + public_deps = [ ":$module_name" ] + deps = [ + "hdf_test", + "hdf_test/hcs_macro_test", + ] + } + ``` +### Kernel Startup Adaptation +1. Add the following kernel startup code to **//device/board/bearpi/bearpi_hm_micro/liteos_a/board/os_adapt.c**. For details, see [LiteOS-A kernel porting](porting-smallchip-kernel-a.md). + ``` + ... + void SystemInit(void) + { + #ifdef LOSCFG_DRIVERS_RANDOM + dprintf("dev random init ...\n"); + Mp1xxRngInit(); + #endif + #ifdef LOSCFG_DRIVERS_MEM + dprintf("mem dev init ...\n"); + extern int mem_dev_register(void); + mem_dev_register(); + #endif + + dprintf("Date:%s.\n", __DATE__); + dprintf("Time:%s.\n", __TIME__); + + #ifdef LOSCFG_DRIVERS_HDF + dprintf("DeviceManagerStart start ...\n"); + if (DeviceManagerStart()) { + PRINT_ERR("No drivers need load by hdf manager!"); + } + dprintf("DeviceManagerStart end ...\n"); + #endif + net_init(); + + #ifdef LOSCFG_PLATFORM_ROOTFS + dprintf("OsMountRootfs start ...\n"); + if (LOS_GetCmdLine()) { + dprintf("get cmdline error!\n"); + } + if (LOS_ParseBootargs()) { + dprintf("parse bootargs error!\n"); + } + if (OsMountRootfs()) { + dprintf("mount rootfs error!\n"); + } + dprintf("OsMountRootfs end ...\n"); + #endif + + dprintf("Before PLATFORM_UART ...\n"); + + #ifdef LOSCFG_DRIVERS_HDF_PLATFORM_UART + if (virtual_serial_init(TTY_DEVICE) != 0) { + PRINT_ERR("virtual_serial_init failed"); + } + if (system_console_init(SERIAL) != 0) { + PRINT_ERR("system_console_init failed\n"); + } + #endif + + dprintf("After PLATFORM_UART ...\n"); + + if (OsUserInitProcess()) { + PRINT_ERR("Create user init process failed!\n"); + return; + } + dprintf("cat log shell end\n"); + return; + } + ... + ``` + + +## Board-Level OS Porting + +### Porting the HDF Driver for the SoC Platform + +Driver adaptation files are stored in `device/soc/st/common/platform` and are loaded using the `HDF` mechanism. This section uses GPIO driver adaptation as an example. + +1. Describe the building adaptation of the stm32mp1xx `gpio` driver in the `//device/soc/st/common/platform/gpio/BUILD.gn` file. The sample code is as follows: + + ``` + module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO) --- If the GPIO configuration switch of the HDF is enabled, the following is built: + module_name = get_path_info(rebase_path("."), "name") + + hdf_driver("hdf_gpio") { + sources = [ "stm32mp1_gpio.c" ] --- GPIO driver source file + include_dirs = [ --- Dependent .h path + "." , + "../stm32mp1xx_hal/STM32MP1xx_HAL_Driver/Inc", + ] + } + ``` + +2. Describe the source code adaptation of the stm32mp1xx `gpio` driver in the `//device/soc/st/common/platform/gpio/stm32mp1_gpio.c` file. + First, load the basic driver adaptation framework based on the `HDF` driver framework of `OpenHarmony`, as shown below: + + ``` + struct HdfDriverEntry g_GpioDriverEntry = { + .moduleVersion = 1, + .moduleName = "HDF_PLATFORM_GPIO", + .Bind = GpioDriverBind, + .Init = GpioDriverInit, + .Release = GpioDriverRelease, + }; + HDF_INIT(g_GpioDriverEntry); --- Load the GPIO driver using HDF_INIT. + ``` +3. Add the GPIO hardware description file **gpio_config.hcs** to **//device/soc/st/stm32mp1xx/sdk_liteos/hdf_config/gpio**, and add private configuration information of the driver to the file. + ``` + root { + platform { + gpio_config { + controller_0x50002000 { + match_attr = "st_stm32mp1_gpio"; + groupNum = 11; + bitNum = 16; + gpioRegBase = 0x50002000; + gpioRegStep = 0x1000; + irqRegBase = 0x5000D000; + irqRegStep = 0x400; + } + } + } + } + ``` +4. Configure the product load driver. All device information of the product is defined in the source code file **//vendor/bearpi/bearpi_hm_micro/hdf_config/device_info/device_info.hcs**. + + Add the platform driver to the host of the platform. + + > **NOTE** + > + > **moduleName** must be the same as that defined in the driver file, and **deviceMatchAttr** must be the same as **match_attr** defined in the driver's private configuration information file **gpio_config.hcs**. + + + ``` + root { + ... + platform :: host { + device_gpio :: device { + device0 :: deviceNode { + policy = 2; + priority = 10; + permission = 0644; + moduleName = "HDF_PLATFORM_GPIO_MANAGER"; + serviceName = "HDF_PLATFORM_GPIO_MANAGER"; + } + device1 :: deviceNode { + policy = 0; + priority = 10; + permission = 0644; + moduleName = "HDF_PLATFORM_GPIO"; + serviceName = "HDF_PLATFORM_GPIO"; + deviceMatchAttr = "st_stm32mp1_gpio"; + } + } + } + } + ``` +5. Complete driver code. Configuration information in **gpio_config.hcs** will be loaded in **GpioDriverInit**. + ``` + static int32_t GpioDriverInit(struct HdfDeviceObject *device) + { + + int32_t ret; + struct Mp1xxGpioCntlr *stm32gpio = &g_Mp1xxGpioCntlr; + + dprintf("%s: Enter", __func__); + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property NULL!", __func__); + return HDF_ERR_INVALID_OBJECT; + } + // Obtain property data. + ret = Mp1xxGpioReadDrs(stm32gpio, device->property); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: get gpio device resource fail:%d", __func__, ret); + return ret; + } + ... + } + ``` + +### OpenHarmony Subsystem Adaptation + +To adapt `OpenHarmony` subsystems, you only need to add related subsystems and components to `config.json`, so that the components can be included in the compilation by the compilation system. + +#### Startup Subsystem Adaptation + +For the startup subsystem, adapt the `bootstrap_lite`, `syspara_lite`, `appspawn_lite`, and `init_lite` components. Add the corresponding configuration items to the `vendor/bearpi/bearpi_hm_micro/config.json` file, as shown below: + +``` + { + "subsystem": "startup", + "components": [ + { "component": "syspara_lite", "features":[] }, + { "component": "bootstrap_lite", "features":[] }, + { "component": "appspawn_lite", "features":[] }, + { "component": "init_lite", "features":[] } + ] + }, +``` +The system will be started according to startup configurations in the **//vendor/bearpi/bearpi_hm_micro/init_configs** file. + + +#### DFX Subsystem Adaptation + +To adapt the `DFX` subsystem, you need to add the `hilog_featured_lite` and `hidumper_lite` components to the `config.json` file. + +``` + { + "subsystem": "hiviewdfx", + "components": [ + { "component": "hilog_featured_lite", "features":[] }, + { "component": "hidumper_lite", "features":[] } + ] + }, +``` + +#### System Service Management Subsystem Adaptation + +To adapt the system service management subsystem, you need to add the `samgr_lite`, `safwk_lite`, and `dmsfwk_lite` components to the `config.json` file. + +``` + { + "subsystem": "distributed_schedule", + "components": [ + { "component": "samgr_lite", "features":[] }, + { "component": "safwk_lite", "features":[] }, + { "component": "dmsfwk_lite", "features":[] } + ] + }, +``` + +#### Security Subsystem Adaptation + +To adapt the security subsystem, you need to add the `permission_lite`, `appverify`, `device_auth`, and `huks` components to the `config.json` file. + +``` + { + "subsystem": "security", + "components": [ + { "component": "permission_lite", "features":[] }, + { "component": "appverify", "features":[] }, + { "component": "device_auth", "features":[] }, + { "component": "huks", "features": + [ + "huks_config_file = \"hks_config_small.h\"" + ] + } + ] + }, +``` + + +#### utils Subsystem Adaptation + +To adapt the utils subsystem, you need to add the `kv_store` and `os_dump` components to the `config.json` file. + +``` + { + "subsystem": "utils", + "components": [ + { "component": "kv_store", "features":[] }, + { "component": "os_dump", "features":[] } + ] + }, +``` + +#### Graphics Subsystem Adaptation + +To adapt the graphics subsystem, you need to add the `graphic_utils` component to the `config.json` file. + +``` + { + "subsystem": "graphic", + "components": [ + { "component": "graphic_utils", + "features": [ "enable_ohos_graphic_utils_product_config = true" + ] + }, + { "component": "graphic_hals", "features":[] }, + { "component": "ui", "features":[ "enable_graphic_font = true","enable_video_component=false"] }, + { "component": "surface", "features":[] }, + { "component": "wms", "features":[] } + ] + }, +``` + +For details about `graphic` configuration, see `//vendor/bearpi/bearpi_hm_micro/graphic_config/product_graphic_lite_config.h`. + + +#### ACE Subsystem Adaptation + +To adapt the ACE subsystem, you need to add the `ace_engine_lite` component to the `config.json` file. + +``` + { + "subsystem": "ace", + "components": [ + { + "component": "ace_engine_lite", + "features": [ + "enable_ohos_ace_engine_lite_product_config = true" + ] + } + ] + }, +``` +For details about `ace_engine_lite` configuration, see `//vendor/bearpi/bearpi_hm_micro/ace_lite_config/product_acelite_config.h`. + +#### aafwk Subsystem Adaptation + +To adapt the aafwk subsystem, you need to add the `aafwk_lite` component to the `config.json` file. + +``` + { + "subsystem": "aafwk", + "components": [ + { + "component": "aafwk_lite", + "features": [ + "enable_ohos_appexecfwk_feature_ability = true" --- The FA feature is supported, that is, the graphics capability is included. + ] + } + ] + }, +``` + + +#### appexecfwk Subsystem Adaptation + +To adapt the appexecfwk subsystem, you need to add the `appexecfwk_lite` component to the `config.json` file. + +``` + { + "subsystem": "appexecfwk", + "components": [ + { + "component": "appexecfwk_lite" + } + ] + }, +``` diff --git a/en/device-dev/porting/porting-w800-combo-demo.md b/en/device-dev/porting/porting-w800-combo-demo.md new file mode 100644 index 0000000000000000000000000000000000000000..1b2a89a9480b99f82a9704564b2b8d58468447de --- /dev/null +++ b/en/device-dev/porting/porting-w800-combo-demo.md @@ -0,0 +1,851 @@ + +# Combo Solution – W800 Chip Porting Case + +The combo solution is developed based on the OpenHarmony LiteOS-M kernel. This document exemplifies how to port code of the [Neptune100 development board](https://gitee.com/openharmony-sig/device_board_hihope) powered by the W800 chip from Winner Micro. The porting architecture uses the solution where Board and SoC are separated. Compilation options can be graphically configured through KConfig. The porting of the `ck804ef` architecture is added to adapt subsystems and components such as `HDF` and `XTS`. + +## Adaptation Preparation + +Prepare the ubuntu20.04 system environment and install the cross compilation toolchain [csky-abiv2-elf-gcc](https://occ.t-head.cn/community/download?id=3885366095506644992). + +## Compilation and Building + +### Directory Planning + +This 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). + +The SoC adaptation directory is planned as follows: + ``` + device + ├── board --- Board vendor directory + │ └── hihope --- Board vendor: HiHope + │ └── neptune100 --- Board name: Neptune100 + └── soc --- SoC vendor directory + └── winnermicro --- SoC vendor: Winner Micro + └── wm800 --- SoC series: W800 + ``` + +The planned product demo directory is as follows: + + ``` + vendor + └── hihope --- Vendor of the product demo. + ├── neptune_iotlink_demo --- Product demo name: sample code of Neptune100 + └── ... + ``` + +### Product Definition + +The `vendor/hihope/neptune_iotlink_demo/config.json` file describes the kernel, board, and subsystem information used by the product. The kernel, board model, and board vendor are required by the precompilation command `hb set` and must be planned. Example: + + ``` + { + "product_name": "neptune_iotlink_demo", --- Product name + "ohos_version": "OpenHarmony 3.1", --- OS version in use + "type":"mini", --- OS type: mini + "version": "3.0", --- OS version: 3.0 + "device_company": "hihope", --- Board vendor: hihope + "board": "neptune100", --- Board name: neptune100 + "kernel_type": "liteos_m", --- Kernel type: liteos_m + "kernel_version": "3.0.0", --- Kernel version: 3.0.0 + "subsystems": [] --- Subsystem + } + ``` +The filled information corresponds to the planned directory. In the information, `device_company` and `board` are used to associate the `device/board//` directory. + +### Board Configuration + +In the associated **\** directory, place the `config.gni` file to the `device/board/hihope/neptune100/liteos_m` directory. This file is used to describe the board information, including the CPU model, cross compilation toolchain, global compilation, and link parameters. + +``` +# Kernel type, e.g. "linux", "liteos_a", "liteos_m". +kernel_type = "liteos_m" + +# Kernel version. +kernel_version = "3.0.0" + +# Board CPU type, e.g. "cortex-a7", "riscv32". +board_cpu = "ck804ef" + +# Board arch, e.g. "armv7-a", "rv32imac". +board_arch = "ck803" + +# Toolchain name used for system compiling. +# E.g. gcc-arm-none-eabi, arm-linux-harmonyeabi-gcc, ohos-clang, riscv32-unknown-elf. +# Note: The default toolchain is "ohos-clang". It's not mandatory if you use the default toolchain. +board_toolchain = "csky-elfabiv2-gcc" + +#use_board_toolchain = true + +# The toolchain path installed, it's not mandatory if you have added toolchain path to your ~/.bashrc. +board_toolchain_path = "" + +# Compiler prefix. +board_toolchain_prefix = "csky-elfabiv2-" + +# Compiler type, "gcc" or "clang". +board_toolchain_type = "gcc" + +# config.json parse +if (product_path != "") { + product_conf = read_file("${product_path}/config.json", "json") + product_name = product_conf.product_name + bin_list = product_conf.bin_list +} + +# Board related common compile flags. +board_cflags = [ + "-mcpu=ck804ef", + "-mhard-float", + "-DGCC_COMPILE=1", + "-DTLS_CONFIG_CPU_XT804=1", + "-DNIMBLE_FTR=1", + "-D__CSKY_V2__=1", + "-DCPU_CK804", + "-O2", + "-g3", + "-Wall", + "-ffunction-sections", + "-MMD", + "-MP", +] + +board_cxx_flags = board_cflags + +board_asmflags = [ + "-mcpu=ck804ef", + "-DCPU_CK804", +] + +board_ld_flags = [] + +# Board related headfiles search path. +board_include_dirs = [] + +# Board adapter dir for OHOS components. +board_adapter_dir = "" + +# Sysroot path. +board_configed_sysroot = "" + +# Board storage type, it used for file system generation. +storage_type = "" +``` + +### Precompilation + +Run the precompilation command `hb set` in the project root directory to show relevant product information, as shown below: + +``` +hb set +OHOS Which product do you need? (Use arrow keys) + +hihope + > neptune_iotlink_demo + +OHOS Which product do you need? neptune_iotlink_demo +``` + +After `hb set` is executed, an `ohos_config.json` file will be automatically generated in the root directory. The file lists the product information to be compiled. + +Run the `hb env` command to view the selected precompilation environment variables. + +``` +[OHOS INFO] root path: /home/xxxx/openharmony_w800 +[OHOS INFO] board: neptune100 +[OHOS INFO] kernel: liteos_m +[OHOS INFO] product: neptune_iotlink_demo +[OHOS INFO] product path: /home/xxxx/openharmony_w800/vendor/hihope/neptune_iotlink_demo +[OHOS INFO] device path: /home/xxxx/openharmony_w800/device/board/hihope/neptune100/liteos_m +[OHOS INFO] device company: hihope +``` + +So far, the precompilation adaptation is complete. However, the project cannot be compiled by running **hb build**. You also need to prepare for the subsequent LiteOS-M kernel porting. + +## Kernel Porting + +### Kconfig Adaptation + +During the compilation of `kernel/liteos_m`, you need to use the `Kconfig` file for indexing in the corresponding board and SoC directory. + +1. Create a **kernel_configs** directory in the `vendor/hihope/neptune_iotlink_demo` directory, and create an empty `debug.config` file. + +2. Open the `kernel/liteos_m/Kconfig` file. Multiple `Kconfig` files in `device/board` and `device/soc` have been imported using the **orsource** command in this file. You need to create and modify these files later. + + ``` + orsource "../../device/board/*/Kconfig.liteos_m.shields" + orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.defconfig.boards" + orsource "../../device/board/$(BOARD_COMPANY)/Kconfig.liteos_m.boards" + orsource "../../device/soc/*/Kconfig.liteos_m.defconfig" + orsource "../../device/soc/*/Kconfig.liteos_m.series" + orsource "../../device/soc/*/Kconfig.liteos_m.soc" + ``` + +3. Create corresponding `Kconfig` files in `device/board/hihope`. + + ``` + ├── neptune100 --- neptune100 board configuration directory. + │ ├── Kconfig.liteos_m.board --- Board configuration options. + │ ├── Kconfig.liteos_m.defconfig.board --- Default board configuration options. + │ └── liteos_m + │ └── config.gni --- Board configuration file. + ├── Kconfig.liteos_m.boards --- Board configuration information of the board vendor. + └── Kconfig.liteos_m.defconfig.boards --- Default board configuration information of the board vendor. + ``` + +4. Modify the `Kconfig` file in the `Board` directory. + + Add the following content to `neptune100/Kconfig.liteos_m.board`: + + ``` + config BOARD_NEPTUNE100 + bool "select board neptune100" + depends on SOC_WM800 + ``` + + Configure that **BOARD_NEPTUNE100** can be selected only when **SOC_WM800** is selected. + + Add the following content to `neptune100/Kconfig.liteos_m.defconfig.board`: + + ``` + if BOARD_NEPTUNE100 + + endif #BOARD_NEPTUNE100 + ``` + + This content is used to add the default configuration of **BOARD_NEPTUNE100**. + +5. Create corresponding `Kconfig` files in `device/soc/winnermicro`. + + ``` + ├── wm800 --- W800 series. + │ ├── Kconfig.liteos_m.defconfig.wm800 --- Default W800 SoC configuration. + │ ├── Kconfig.liteos_m.defconfig.series --- Default configuration of the W800 series. + │ ├── Kconfig.liteos_m.series --- Configuration of the W800 series. + │ └── Kconfig.liteos_m.soc --- W800 SoC configuration. + ├── Kconfig.liteos_m.defconfig --- Default SoC configuration. + ├── Kconfig.liteos_m.series --- Series configuration. + └── Kconfig.liteos_m.soc --- SoC configuration. + ``` + +6. Modify the `Kconfig` file in the `Soc` directory. + + Add the following content to `wm800/Kconfig.liteos_m.defconfig.wm800`: + + ``` + config SOC + string + default "wm800" + depends on SOC_WM800 + ``` + + Add the following content to `wm800/Kconfig.liteos_m.defconfig.series`: + + ``` + if SOC_SERIES_WM800 + + rsource "Kconfig.liteos_m.defconfig.wm800" + + config SOC_SERIES + string + default "wm800" + + endif + ``` + + Add the following content to `wm800/Kconfig.liteos_m.series`: + + ``` + config SOC_SERIES_WM800 + bool "winnermicro 800 Series" + select ARM + select SOC_COMPANY_WINNERMICRO --- Select SOC_COMPANY_WINNERMICRO. + select CPU_XT804 + help + Enable support for winnermicro 800 series + ``` + + **SOC_WM800** in the `wm800/Kconfig.liteos_m.soc` file can be selected only after **SOC_SERIES_WM800** is selected. + + ``` + choice + prompt "Winnermicro 800 series SoC" + depends on SOC_SERIES_WM800 + + config SOC_WM800 --- Select SOC_WM800. + bool "SoC WM800" + + endchoice + ``` + + In conclusion, to compile **BOARD_NEPTUNE100**, you need to select **SOC_COMPANY_WINNERMICRO**, **SOC_SERIES_WM800**, and **SOC_WM800**. +7. Run `make menuconfig` in `kernel/liteos_m` for configuration selection. The SoC series can be selected. + + ![w800_select.json](figures/w800_select.png) + + The configured file is saved to `vendor/hihope/neptune_iotlink_demo/kernel_configs/debug.config` by default. You can also directly configure `debug.config`. + + ``` + LOSCFG_PLATFORM_QEMU_CSKY_SMARTL=y + LOSCFG_SOC_SERIES_WM800=y + ``` + +### Modular Compilation + +The compilation of `Board` and `SoC` adopts the modular compilation method, starting from `kernel/liteos_m/BUILD.gn` and increasing by level. The adaptation process of this solution is as follows: + +1. Create the `BUILD.gn` file in `device/board/hihope` and add the following content to the file: + + ``` + if (ohos_kernel_type == "liteos_m") { + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ + "neptune100", --- Board module. + "shields", + ] + } + } + ``` + + In the preceding `BUILD.gn` file, **neptune100** and **shields** are the module names organized by directory level. + +2. Create the `BUILD.gn` file in `device/soc/winnermicro` and add the following content to the file: + + ``` + if (ohos_kernel_type == "liteos_m") { + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + module_group(module_name) { + modules = [ + "hals", + "wm800", + ] + } + } + ``` + +3. In the `device/soc/winnermicro` module at each level, add the `BUILD.gn` file and compile the module. The following uses `device/soc/winnermicro/wm800/board/platform/sys/BUILD.gn` as an example: + + ``` + import("//kernel/liteos_m/liteos.gni") + module_name = get_path_info(rebase_path("."), "name") + kernel_module(module_name) { --- Compiled module. + sources = [ --- Compiled source file. + "wm_main.c", + ] + include_dirs = [ --- Header file used in the module. + ".", + ] + } + + ``` + +4. To organize links and some compilation options, set the following parameters in `config("board_config")` in `device/soc/winnermicro/wm800/board/BUILD.gn`: + + ``` + config("board_config") { + ldflags = [] --- Link parameters, including the Id file. + libs = [] --- Link library. + include_dirs = [] --- Common header file. + ``` + + +5. To organize some product applications, this solution adds a corresponding list to the `config.json` file of the vendor. The following uses `vendor/hihope/neptune_iotlink_demo/config.json` as an example to describe how to add a corresponding list to the `config.json` file: + + ``` + "bin_list": [ --- demo list + { + "elf_name": "hihope", + "enable": "false", --- List switch. + "force_link_libs": [ + "bootstrap", + "broadcast", + ... + ] + } + ``` + + The demo is managed as a module. To enable or disable a demo, add or delete corresponding library files in **bin_list**. **bin_list** can be directly read in GN. You need to add the following content to `device/board/hihope/neptune100/liteos_m/config.gni`: + + ``` + # config.json parse + if (product_path != "") { + product_conf = read_file("${product_path}/config.json", "json") + product_name = product_conf.product_name + bin_list = product_conf.bin_list + } + ``` + + After reading the list, you can add related component libraries to the corresponding link options. Add the following content to `//device/soc/winnermicro/wm800/BUILD.gn`: + + ``` + foreach(bin_file, bin_list) { + build_enable = bin_file.enable + ... + if(build_enable == "true") + { + ... + foreach(force_link_lib, bin_file.force_link_libs) { + ldflags += [ "-l${force_link_lib}" ] + } + ... + } + } + ``` + +### Kernel Subsystem Adaptation + +Add the kernel subsystem and relevant configuration to `vendor/hihope/neptune_iotlink_demo/config.json`, as shown below: + + ``` + "subsystems": [ + { + "subsystem": "kernel", + "components": [ + { + "component": "liteos_m", "features":[] + } + ] + }, + ``` + +### Kernel Startup Adaptation + +The Neptune100 development board uses the SoC architecture **ck804ef**, which is not supported by OpenHarmony. You need to port the architecture **ck804ef**. Adapt general files and function lists defined in `kernel\liteos_m\arch\include`, and place them to the `kernel\liteos_m\arch\csky\v2\ck804\gcc` directory. + +The following is an example of kernel initialization: + + ``` + osStatus_t ret = osKernelInitialize(); --- Kernel initialization. + if(ret == osOK) + { + threadId = osThreadNew((osThreadFunc_t)sys_init,NULL,&g_main_task); --- Create the init thread. + if(threadId!=NULL) + { + osKernelStart(); --- Thread scheduling. + } + } + ``` + +Initialize necessary actions before **board_main** starts **OHOS_SystemInit**, as shown below: + + ``` + ... + UserMain(); --- Initialize the driver before starting OHOS_SystemInit of OpenHarmony. + ... + OHOS_SystemInit(); --- Start OpenHarmony services and initialize components. + ... + ``` + +The **UserMain** function is in the `device/soc/winnermicro/wm800/board/app/main.c` file, as shown below: + + ``` + ... + if (DeviceManagerStart()) { --- HDF initialization. + printf("[%s] No drivers need load by hdf manager!",__func__); + } + ... + ``` + +### HDF Framework Adaptation + +HDF provides a set of unified APIs for applications to access hardware, simplifying application development. To add the HDF component, you need to add it to `//vendor/hihope/neptune_iotlink_demo/kernel_configs`: + + ``` + LOSCFG_DRIVERS_HDF=y + LOSCFG_DRIVERS_HDF_PLATFORM=y + ``` + +Driver adaptation files are stored in `drivers/adapter/platform`, including the gpio, i2c, pwm, spi, uart, and watchdog drivers. These files are loaded using the HDF mechanism. This section uses GPIO and UART as an example. + +#### GPIO Adaptation + +1. The chip driver adaptation file is stored in the `drivers/adapter/platform` directory. Add the `gpio_wm.c` file to the **gpio** directory, and define the compilation adaptation of the W800 driver in `BUILD.gn`, as shown below: + + ``` + ... + if (defined(LOSCFG_SOC_COMPANY_WINNERMICRO)) { + sources += [ "gpio_wm.c" ] + } + ... + ``` + +2. Define the driver description file in `gpio_wm.c` as follows: + + ``` + /* HdfDriverEntry definitions */ + struct HdfDriverEntry g_GpioDriverEntry = { + .moduleVersion = 1, + .moduleName = "WM_GPIO_MODULE_HDF", + .Bind = GpioDriverBind, + .Init = GpioDriverInit, + .Release = GpioDriverRelease, + }; + HDF_INIT(g_GpioDriverEntry); + ``` + +3. Add the GPIO hardware description information to `device/board/hihope/shields/neptune100/neptune100.hcs`. + + ``` + root { + platform { + gpio_config { + match_attr = "gpio_config"; + groupNum = 1; + pinNum = 48; + } + } + } + ``` + +4. Obtain the **hcs** parameter from **GpioDriverInit** for initialization, as shown below: + + ``` + ... + gpioCntlr = GpioCntlrFromHdfDev(device); --- Obtain specific GPIO configurations through the **gpioCntlr** node variable. + if (gpioCntlr == NULL) { + HDF_LOGE("GpioCntlrFromHdfDev fail\r\n"); + return HDF_DEV_ERR_NO_DEVICE_SERVICE; + } + ... + ``` + +#### UART Adaptation + +1. The chip driver adaptation file is stored in the `drivers/adapter/platform` directory. Add the `uart_wm.c` file to the **uart** directory, and define the compilation adaptation of the W800 driver in `BUILD.gn`, as shown below: + + ``` + ... + if (defined(LOSCFG_SOC_COMPANY_WINNERMICRO)) { + sources += [ "uart_wm.c" ] + } + ... + ``` + +2. Define the driver description file in `uart_wm.c` as follows: + + ``` + /* HdfDriverEntry definitions */ + struct HdfDriverEntry g_UartDriverEntry = { + .moduleVersion = 1, + .moduleName = "W800_UART_MODULE_HDF", + .Bind = UartDriverBind, + .Init = UartDriverInit, + .Release = UartDriverRelease, + }; + + /* Initialize HdfDriverEntry */ + HDF_INIT(g_UartDriverEntry); + ``` + +3. Add the UART hardware description information to `device/board/hihope/shields/neptune100/neptune100.hcs`. + + ``` + root { + platform { + uart_config { + /* + uart0 { + match_attr = "uart0_config"; + num = 0; + baudrate = 115200; + parity = 0; + stopBit = 1; + data = 8; + }*/ + uart1 { + match_attr = "uart1_config"; + num = 1; + baudrate = 115200; + parity = 0; + stopBit = 1; + data = 8; + } + } + } + } + ``` + +4. Obtain the **hcs** parameter from **UartDriverInit** for initialization, as shown below: + + ``` + ... + host = UartHostFromDevice(device); + if (host == NULL) { + HDF_LOGE("%s: host is NULL", __func__); + return HDF_ERR_INVALID_OBJECT; + } + ... + ``` + +## OpenHarmony Subsystem Adaptation + +Subsystem compilation options are configured in the `config.json` file of the corresponding product, for example, `vendor/hihope/neptune_iotlink_demo/config.json`. + +### wifi_lite Component + +Add the `wifi_lite` component of the `communication` subsystem to the `config.json` file, as shown below: + + ``` + { + "subsystem": "communication", + "components": [ + { + "component": "wifi_lite", + "optional": "true" + } + ] + }, + ``` + +The `wifi_lite` component is in the `build/lite/components/communication.json` file, which is described as follows: + + ``` + { + "component": "wifi_lite", + "targets": [ + "//foundation/communication/wifi_lite:wifi" --- Compilation target of the wifi_lite component. + ] + }, + ``` + +In this case, the `wifi` adaptation source code can be checked in `device/soc/winnermicro/wm800/board/src/wifi/wm_wifi.c`, which is shown below: + + ``` + int tls_wifi_netif_add_status_event(tls_wifi_netif_status_event_fn event_fn) --- Used to add the wifi event function. + { + u32 cpu_sr; + struct tls_wifi_netif_status_event *evt; + //if exist, remove from event list first. + tls_wifi_netif_remove_status_event(event_fn); + evt = tls_mem_alloc(sizeof(struct tls_wifi_netif_status_event)); + if(evt==NULL) + return -1; + memset(evt, 0, sizeof(struct tls_wifi_netif_status_event)); + evt->status_callback = event_fn; + cpu_sr = tls_os_set_critical(); + dl_list_add_tail(&wifi_netif_status_event.list, &evt->list); + tls_os_release_critical(cpu_sr); + + return 0; + } + ``` + +### systemabilitymgr Subsystem Adaptation +To adapt the **systemabilitymgr** subsystem, you need to add the `samgr_lite` component to the `config.json` file, as shown below: + + ``` + { + "subsystem": "systemabilitymgr", + "components": [ + { + "component": "samgr_lite" + } + ] + }, + ``` + +### utils Subsystem Adaptation + +To adapt the utils subsystem, you need to add the `kv_store` and `file` components to the `config.json` file, as shown below: + + ``` + { + "subsystem": "utils", + "components": [ + { + "component": "kv_store", + "features": [ + "enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true" + ] + }, + { "component": "file", "features":[] } + ] + }, + ``` + +When the `kv_store` component is adapted, key-value pairs will be written to the file. In the lite system, file operation APIs include `POSIX` and `HalFiles`. +The `POSIX` API is used for accessing the file system in the kernel. Therefore, you need to add `enable_ohos_utils_native_lite_kv_store_use_posix_kv_api = true` to `features`. + +### Startup Subsystem Adaptation + +To adapt the startup subsystem, you need to add the `bootstrap_lite` and `syspara_lite` components to the `config.json` file, as shown below: + + ``` + { + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite" + }, + { + "component": "syspara_lite", + "features": [ + "enable_ohos_startup_syspara_lite_use_posix_file_api = true", + "config_ohos_startup_syspara_lite_data_path = \"/data/\"" + ] + } + ] + }, + ``` + +When adapting the **bootstrap_lite** component, you need to manually add the following content to the link script file `device/soc/winnermicro/wm800/board/ld/w800/gcc_csky.ld`: + + ``` + .zinitcall_array : + { + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_core_start = .); + KEEP (*(SORT(.zinitcall.core*))) + KEEP (*(.zinitcall.core*)) + PROVIDE_HIDDEN (__zinitcall_core_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_device_start = .); + KEEP (*(SORT(.zinitcall.device*))) + KEEP (*(.zinitcall.device*)) + PROVIDE_HIDDEN (__zinitcall_device_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_bsp_start = .); + KEEP (*(SORT(.zinitcall.bsp*))) + KEEP (*(.zinitcall.bsp*)) + PROVIDE_HIDDEN (__zinitcall_bsp_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_sys_service_start = .); + KEEP (*(SORT(.zinitcall.sys.service*))) + KEEP (*(.zinitcall.sys.service*)) + PROVIDE_HIDDEN (__zinitcall_sys_service_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_app_service_start = .); + KEEP (*(SORT(.zinitcall.app.service*))) + KEEP (*(.zinitcall.app.service*)) + PROVIDE_HIDDEN (__zinitcall_app_service_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_sys_feature_start = .); + KEEP (*(SORT(.zinitcall.sys.feature*))) + KEEP (*(.zinitcall.sys.feature*)) + PROVIDE_HIDDEN (__zinitcall_sys_feature_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_app_feature_start = .); + KEEP (*(SORT(.zinitcall.app.feature*))) + KEEP (*(.zinitcall.app.feature*)) + PROVIDE_HIDDEN (__zinitcall_app_feature_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_run_start = .); + KEEP (*(SORT(.zinitcall.run*))) + KEEP (*(.zinitcall.run*)) + PROVIDE_HIDDEN (__zinitcall_run_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_test_start = .); + KEEP (*(SORT(.zinitcall.test*))) + KEEP (*(.zinitcall.test*)) + PROVIDE_HIDDEN (__zinitcall_test_end = .); + . = ALIGN(0x4) ; + PROVIDE_HIDDEN (__zinitcall_exit_start = .); + KEEP (*(SORT(.zinitcall.exit*))) + KEEP (*(.zinitcall.exit*)) + PROVIDE_HIDDEN (__zinitcall_exit_end = .); + } > REGION_RODATA + ``` + +Adding the preceding content is because external APIs provided by `bootstrap_init` uses the segment injection mode and will be saved to the link segment. For details, see `utils/native/lite/include/ohos_init.h`. The following table lists the automatic initialization macros of main services. + +| API | Description | +| ---------------------- | -------------------------------- | +| SYS_SERVICE_INIT(func) | Entry for initializing and starting a core system service.| +| SYS_FEATURE_INIT(func) | Entry for initializing and starting a core system feature.| +| APP_SERVICE_INIT(func) | Entry for initializing and starting an application-layer service. | +| APP_FEATURE_INIT(func) | Entry for initializing and starting an application-layer feature. | + + + +The **lib** file compiled using the loaded components needs to be manually add to the forcible link. + +If the `bootstrap_lite` component is configured in `vendor/hihope/neptune_iotlink_demo/config.json`: + + ``` + { + "subsystem": "startup", + "components": [ + { + "component": "bootstrap_lite" + }, + ... + ] + }, + ``` + +The `bootstrap_lite` component will compile the `base/startup/bootstrap_lite/services/source/bootstrap_service.c` file. In this file, `SYS_SERVICE_INIT` is used to inject the `Init` function symbol to `__zinitcall_sys_service_start` and `__zinitcall_sys_service_end`. Since the `Init` function does not support explicit call, you need to forcibly link it to the final image, as shown below: + + ``` + static void Init(void) + { + static Bootstrap bootstrap; + bootstrap.GetName = GetName; + bootstrap.Initialize = Initialize; + bootstrap.MessageHandle = MessageHandle; + bootstrap.GetTaskConfig = GetTaskConfig; + bootstrap.flag = FALSE; + SAMGR_GetInstance()->RegisterService((Service *)&bootstrap); + } + SYS_SERVICE_INIT(Init); --- Forcible link to the generated lib file is required if SYS_INIT is used for startup. + ``` + +The `base/startup/bootstrap_lite/services/source/BUILD.gn` file describes `libbootstrap.a` generated in `out/neptune100/neptune_iotlink_demo/libs`, as shown below: + + ``` + static_library("bootstrap") { + sources = [ + "bootstrap_service.c", + "system_init.c", + ] + ... + ``` + + +When the `syspara_lite` component is adapted, system parameters will be written into the file for persistent storage. In the lite system, file operation APIs include **POSIX** and **HalFiles**. + +The **POSIX** API is used for accessing the file system in the kernel. Therefore, you need to add `enable_ohos_startup_syspara_lite_use_posix_file_api = true` to the **features** field. + +### XTS Subsystem Adaptation + +To adapt the XTS subsystem, add the following component options to `config.json`: + + ``` + { + "subsystem": "xts", + "components": [ + { + "component": "xts_acts", + "features": + [ + "config_ohos_xts_acts_utils_lite_kv_store_data_path = \"/data\"", + "enable_ohos_test_xts_acts_use_thirdparty_lwip = true" + ] + }, + { "component": "xts_tools", "features":[] } + ] + } + ``` + +The XTS function is also organized using **list**. You can add or delete relevant modules in the `config.json` file. + + ``` + "bin_list": [ + { + "enable": "true", + "force_link_libs": [ + "module_ActsParameterTest", + "module_ActsBootstrapTest", + "module_ActsDfxFuncTest", + "module_ActsHieventLiteTest", + "module_ActsSamgrTest", + "module_ActsUtilsFileTest", + "module_ActsKvStoreTest", + "module_ActsWifiServiceTest" + ] + } + ], + ``` + +The adaptation process of other components is similar to that of other vendors. diff --git a/en/device-dev/porting/porting-yangfan-on_standard-demo.md b/en/device-dev/porting/porting-yangfan-on_standard-demo.md new file mode 100644 index 0000000000000000000000000000000000000000..5e86dd6ee02f14c0ca99dee187d8da871fe2d2ad --- /dev/null +++ b/en/device-dev/porting/porting-yangfan-on_standard-demo.md @@ -0,0 +1,1679 @@ +# Standard System Solution – Yangfan Porting Case + +This document describes how to port standard system functions based on the yangfan development board of the RK3399 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, BT, vibrator, sensor, and graphics display modules, as well as related function adaptation. +The development board system porting uses the solution where the board warehouse and SoC code are separated. The board warehouse stores adaptation code of on-board modules, such as audio, camera, TP, and Wi-Fi. The SoC warehouse stores adaptation codes of SoC driver modules, such as I2C, ISP, and RGA. + +## Product Configuration and Directory Planning + +### Product Configuration + +Create a **config.json** file in the `//vendor/yangfan` directory of the product and specify the CPU architecture. Configuration of `//vendor/yangfan/rk3399.json` is as follows: + +``` +{ + "product_name": "yangfan",--- Product name: yangfan + "device_company": "rockchip",--- Board vendor: Rockchip + "device_build_path": "device/board/isoftstone/yangfan",--- Device building path: device/board/isoftstone/yangfan + "target_cpu": "arm",--- Target CPU: arm + "type": "standard",--- OS type: standard + "version": "3.0",--- Version: 3.0 + "board": "yangfan",--- Board name: yangfan + "enable_ramdisk": true,--- Enable memory virtual disk: true + "build_selinux": true,--- Build selinux: true + "inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ], + "subsystems": [ + { + "subsystem": "security", + "components": [ + { + "component": "selinux", + "features": [] + } + ] + }, + { + "subsystem": "communication", + "components": [ + { + "component": "netmanager_ext", + "features": [] + } + ] + }, + ... +} +``` + + +The main configurations are as follows: + +1. "product_name": "yangfan",--- Product name: yangfan +2. "device_company": "rockchip",--- Board vendor: Rockchip +3. "device_build_path": "device/board/isoftstone/yangfan",--- Device building path: device/board/isoftstone/yangfan +4. "target_cpu": "arm",--- Target CPU: arm +5. "type": "standard",--- OS type: standard +6. "version": "3.0",--- Version: 3.0 +7. "board": "yangfan",--- Board name: yangfan +8. "enable_ramdisk": true,--- Enable memory virtual disk: true + +You can find defined subsystems in `//build/subsystem_config.json`. You can also customize subsystems. + +You are advised to copy the configuration file of Hi3516DV300 and delete the **hisilicon_products** subsystem, which is used to compile the kernel for Hi3516DV300 and is not suitable for RK3568. + +### Directory Planning + +This 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: + +``` +device +├── board --- Board vendor directory +│ └── isoftstone --- Board vendor +│ └── yangfan --- Board name: yangfan +└── soc --- SoC vendor directory + └── rockchip --- SoC vendor: Rockchip + └── rk3399 --- SoC series: RK3399, mainly solutions provided by the SoC manufacturer and closed-source libraries + + +``` +The planned product demo directory is as follows: +``` +vendor +└── isoftstone + └── yangfan --- Product name: product, HCS, and demo related +``` + +## **Kernel Startup** + +### Secondary Boot + +Unlike 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**. + +RK3399 adaptation is to pack **ramdisk** compiled in the mainline version into **boot_linux.img**. The procedure is as follows: + +1. Enable secondary boot. + +Enable **enable_ramdisk** in **//vendor/yangfan/rk3399.json**. + + ``` + { + "product_name": "yangfan", + "device_company": "rockchip", + "device_build_path": "device/board/isoftstone/yangfan", + "target_cpu": "arm", + "type": "standard", + "version": "3.0", + "board": "yangfan", + "enable_ramdisk": true, + "build_selinux": true, + ... + } + ``` + +2. Pack the **ramdsik.img** file compiled in the mainline version to **boot_linux.img**. + +Configuration: + +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**: + +``` +function make_extlinux_conf() +{ + dtb_path=$1 + uart=$2 + image=$3 + + echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF} + echo " kernel /extlinux/${image}" >> ${EXTLINUX_CONF} + echo " fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF} + if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then + echo " initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF} + fi + cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4" + echo " ${cmdline}" >> ${EXTLINUX_CONF} +} +``` + +### Packing + +Add 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: + +``` +genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img +``` + +For details about modification for calling **make-boot.sh**, see [RK3568 adaptation to secondary boot]( https://gitee.com/openharmony/build/pulls/569/files). + +### INIT Configuration + +For details about the init configuration, see [Startup Subsystem](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%90%AF%E5%8A%A8%E6%81%A2%E5%A4%8D%E5%AD%90%E7%B3%BB%E7%BB%9F.md). + +## **Audio** + +### Introduction + +This section describes how to develop the audio driver framework based on the Hardware Driver Foundation (HDF) in OpenHarmony 3.0, including detailed introduction to the composition of the audio driver framework, functional component implementation, and service nodes. + +![Audio driver framework](figures/isoftstone/yangfan-Audio-ADM.png) + +1. Audio Driver Model (ADM) + + The ADM helps system developers to develop scenario-specific applications for the multimedia audio subsystem. With the ADM, codec and DSP device vendors can adapt their driver code based on the APIs provided by the ADM and implement quick development and easy adaptation to HarmonyOS. + +2. Audio Control Dispatch + + The Audio Control Dispatch dispatches the control instructions from the Audio Interface Lib to the driver layer. + +3. Audio Stream Dispatch + + The Audio Control Dispatch receives data streams from the Audio Interface Lib and distributes the data streams to the driver layer. + +4. Card Manager + + The Card Manager manages multiple audio adapters. Each audio adapter consists of the digital audio interface (DAI), platform, codec, accessory, DSP, and Smart Audio Power Manager (SAPM) modules. + +5. Platform Driver + + The Platform Driver is the driver adaptation layer. + +6. Smart Audio Power Manager (SAPM) + + The SAPM is the power manager module that optimizes the power consumption policies of the entire ADM power. + +### Audio Driver Introduction + +#### Code Directory + +``` +drivers + ├── framework + │ └── model + │ │ └── audio # Framework code + │ │ ├─── common # Common implementation + │ │ ├─── core # Core + │ │ ├─── dispatch # Control stream and data stream implementation + │ │ └── sapm # Power manager + │ └── include + │ └── audio # External API + ├── adapter + │ └──khdf + │ └── linux + │ └── model + │ └── audio # Compilation file + └── peripheral + └── audio + └── chipsets + └── rk3399 # Driver implementation + ├── accessory #SmartPA driver + ├── dai #I2S driver + └── soc #Dma driver +``` + +#### Audio Process Description + +##### Startup Process + +![](figures/isoftstone/yangfan-Audio-start.png) + +1. When the system starts, the platform, codec, accessory, DSP, and DAI drivers of the audio module are loaded first. Each driver obtains the configuration information from its configuration file and saves the obtained information to the data structures. +2. Each driver module calls the ADM registration interface to add itself to the linked list of the driver module. +3. The ADM module reads configurations of hdf_audio_driver_0 (audio card_0) and hdf_audio_driver_1 (audio card_1), and loads specific devices of each module. +4. The ADM module initializes each module device by calling the initialization API of the respective module. +5. The initialized audio devices are added to the **cardManager** linked list. + +##### Playback Process + +![](figures/isoftstone/yangfan-Audio-play.png) + +1. The Interface Lib dispatches the **Render Open** instruction through the service launched by the driver for handling the playback streaming (referred to as driver service hereinafter). Upon receiving the instruction, the Stream Dispatch service calls the API of each module to deliver the instruction. +2. The Audio Interface Lib sends a path selection instruction to the Control Dispatch service. The Control Dispatch service calls the DAI API to set the path. +3. The Interface Lib dispatches hardware parameters through the driver service. Upon receiving the parameters, the Stream Dispatch service calls the API of each module to set hardware parameters. +4. The Interface Lib dispatches the start playing instruction through the driver service. Upon receiving the instruction, the Stream Dispatch service calls the API of each module to perform related settings for each module. +5. The Interface Lib dispatches audio data through the driver service. Upon receiving the data, the Stream Dispatch service calls the **Platform AudioPcmWrite** API to send the audio data to direct memory access (DMA). +6. The Interface Lib dispatches the stop playing instruction through the driver service. Upon receiving the instruction, the Stream Dispatch service calls the stop API of each module to perform related settings for each module. +7. The Interface Lib dispatches the **Render Close** instruction through the driver service. Upon receiving the instruction, the Stream Dispatch service calls the **Platform AudioRenderClose** API to release resources. + +##### Control Process + +![](figures/isoftstone/yangfan-Audio-commond.png) + +1. To adjust the volume, the Audio Interface Lib sends an instruction for obtaining the volume range to the Control Dispatch service. The Control Dispatch service parses the instruction and calls **get()** of the codec module to obtain the volume range. +2. The Audio Interface Lib sends an instruction for setting the volume to the Control Dispatch service. The Control Dispatch service parses the instruction and calls **Set()** of the codec module to set the volume. + +#### Implementation Description + +1. Driver registration + + Take the registration function of codec as an example. When the codec driver is initialized, the following codec registration function is called to register codec with the **codecController** linked list. + + ```c + int32_t AudioRegisterCodec(struct HdfDeviceObject *device, struct CodecData *codecData, struct DaiData *daiData) + { + ... + + codec = (struct CodecDevice *)OsalMemCalloc(sizeof(*codec)); + ... + + OsalMutexInit(&codec->mutex); + codec->devCodecName = codecData->drvCodecName; + codec->devData = codecData; + codec->device = device; + + ret = AudioSocRegisterDai(device, daiData); + ... + DListInsertHead(&codec->list, &codecController); + ... + } + ``` + +2. Data stream data distribution + + During audio recording or playback, the Lib layer distributes or reads data using Dispatch. After receiving the request from the Lib layer, the API distributes or returns the data. + + ```c + static int32_t StreamDispatch(struct HdfDeviceIoClient *client, int cmdId, + struct HdfSBuf *data, struct HdfSBuf *reply) + { + unsigned int count = sizeof(g_streamDispCmdHandle) / sizeof(g_streamDispCmdHandle[0]); + for (unsigned int i = 0; i < count; ++i) { + if ((cmdId == (int)(g_streamDispCmdHandle[i].cmd)) && (g_streamDispCmdHandle[i].func != NULL)) { + return g_streamDispCmdHandle[i].func(client, data, reply); + } + } + ADM_LOG_ERR("invalid [cmdId=%d]", cmdId); + return HDF_FAILURE; + } + ``` + +3. Control function registration API + + Control functions such as volume control, gain control, and channel control are added to the audio adapter control list using this API. + + ```c + int32_t AudioAddControls(struct AudioCard *audioCard, const struct AudioKcontrol *controls, int32_t controlMaxNum) + { + ... + + for (i = 0; i < controlMaxNum; i++) { + control = AudioAddControl(audioCard, &controls[i]); + if (control == NULL) { + ADM_LOG_ERR("Add control fail!"); + return HDF_FAILURE; + } + DListInsertHead(&control->list, &audioCard->controls); + } + ADM_LOG_DEBUG("Success."); + return HDF_SUCCESS; + } + ``` + +4. Power management API + + Add component implementation: + + ```c + int32_t AudioSapmNewComponents(struct AudioCard *audioCard, + const struct AudioSapmComponent *component, int32_t cptMaxNum) + { + ... + + for (i = 0; i < cptMaxNum; i++) { + ret = AudioSapmNewComponent(audioCard, component); + if (ret != HDF_SUCCESS) { + ADM_LOG_ERR("AudioSapmNewComponent fail!"); + return HDF_FAILURE; + } + component++; + } + + return HDF_SUCCESS; + } + + ``` + + Add channel implementation: + + ```c + + int32_t AudioSapmAddRoutes(struct AudioCard *audioCard, const struct AudioSapmRoute *route, int32_t routeMaxNum) + { + ... + + for (i = 0; i < routeMaxNum; i++) { + ret = AudioSapmAddRoute(audioCard, route); + if (ret != HDF_SUCCESS) { + ADM_LOG_ERR("AudioSapmAddRoute failed!"); + return HDF_FAILURE; + } + route++; + } + return HDF_SUCCESS; + } + + ``` + + Add control function implementation: + + ```c + + int32_t AudioSapmNewControls(struct AudioCard *audioCard) + { + ... + + DLIST_FOR_EACH_ENTRY(sapmComponent, &audioCard->components, struct AudioSapmComponent, list) { + if (sapmComponent->newCpt) { + continue; + } + if (sapmComponent->kcontrolsNum > 0) { + sapmComponent->kcontrols = OsalMemCalloc(sizeof(struct AudioKcontrol*) * sapmComponent->kcontrolsNum); + if (sapmComponent->kcontrols == NULL) { + ADM_LOG_ERR("malloc kcontrols fail!"); + return HDF_FAILURE; + } + } + + switch (sapmComponent->sapmType) { + case AUDIO_SAPM_ANALOG_SWITCH: + case AUDIO_SAPM_MIXER: + case AUDIO_SAPM_MIXER_NAMED_CTRL: + case AUDIO_SAPM_SPK: + case AUDIO_SAPM_PGA: + ret = AudioSapmNewMixerControls(sapmComponent, audioCard); + break; + case AUDIO_SAPM_MUX: + case AUDIO_SAPM_VIRT_MUX: + case AUDIO_SAPM_VALUE_MUX: + ret = AudioSapmNewMuxControls(sapmComponent, audioCard); + break; + default: + ret = HDF_SUCCESS; + break; + } + ... + + ReadInitComponentPowerStatus(sapmComponent); + sapmComponent->newCpt = 1; + DListInsertTail(&sapmComponent->dirty, &audioCard->sapmDirty); + } + + ret = AudioSapmPowerComponents(audioCard); + ... + + return HDF_SUCCESS; + } + + ``` + +5. Control stream data distribution + + During audio recording or playback, the Lib layer delivers the control instruction using Dispatch. After receiving the instruction, the API distributes the instruction to each driver module. + + ```c + static int32_t ControlDispatch(struct HdfDeviceIoClient *client, int cmdId, + struct HdfSBuf *data, struct HdfSBuf *reply) + { + ... + + if (cmdId >= AUDIODRV_CTRL_IOCTRL_ELEM_BUTT || cmdId < 0) { + ADM_LOG_ERR("Invalid [cmdId=%d].", cmdId); + return HDF_FAILURE; + } + + for (i = 0; i < HDF_ARRAY_SIZE(g_controlDispCmdHandle); ++i) { + if ((cmdId == (int)(g_controlDispCmdHandle[i].cmd)) && (g_controlDispCmdHandle[i].func != NULL)) { + return g_controlDispCmdHandle[i].func(client, data, reply); + } + } + return HDF_FAILURE; + } + ``` + +### Audio Service Introduction + +#### Service Node + +Three services are provided for the HDI layer based on the audio driver of the ADM framework: hdf_audio_render, hdf_audio_capture, and hdf_audio_control. +The audio driver service nodes of the development board are as follows: + +``` +console:/dev # ls -al hdf_audio_* +crw------- 1 system system 249, 5 1970-01-01 00:21 hdf_audio_capture // Audio recording data stream service. +crw------- 1 system system 249, 3 1970-01-01 00:21 hdf_audio_codec_dev0 // Audio device name. +crw------- 1 system system 249, 4 1970-01-01 00:21 hdf_audio_control // Audio control stream service. +crw------- 1 system system 249, 6 1970-01-01 00:21 hdf_audio_render // Playback data stream service. +``` + +1. Audio control stream service + + This service receives control instructions from the Lib layer, including the volume control, gain control, and channel control instructions. These control instructions are distributed to the driver through the control stream service. + +2. Audio data playback service + + This service receives audio data and playback parameters from the Lib layer, as well as instructions for starting, pausing, resuming, and stopping the playback. These instructions are distributed to the driver through the audio data and playback service. + +3. Audio recording service + + This service transmits audio data to the Lib layer and receives audio recording parameters from the Lib layer, as well as receive instructions for starting, pausing, resuming, and stopping audio recording. These instructions are distributed to the driver through the audio recording service. + +#### Driver Service + +Each audio device includes the following services. + +| **hdf_audio_codec_dev0** | **Audio Device** | +| ------------------------ | ---------------------- | +| dma_service_0 | DMA driver service | +| dai_service | CPU DAI driver service | +| codec_service_0 | Codec driver service | +| dsp_service_0 | (Optional) DSP driver service| + +| **hdf_audio_codec_dev1** | **Audio Device** | +| ------------------------ | --------------------------------- | +| dma_service_0 | DMA driver service | +| dai_service | CPU DAI driver service | +| codec_service_1 | Accessory driver service (SmartPA)| +| dsp_service_0 | (Optional) DSP driver service | + +#### Code Path + +``` +vendor/rockchip/rk3399/hdf_config/khdf +├── audio # Audio private configuration file +├── device_info +| └── device_info.hcs # Device configuration file +└── hdf.hcs # Reference the HCS configuration file +``` + +#### Configuration Node Description + +Take the codec driver as an example. Add the codec node information to the **audio host** node in the **device_info.hcs** file. + +``` + audio :: host { + hostName = "audio_host"; + priority = 60; +... + device_codec :: device { + device0 :: deviceNode { + policy = 1; + priority = 50; + preload = 0; + permission = 0666; + moduleName = "CODEC_ES8316"; + serviceName = "codec_service_0"; + deviceMatchAttr = "hdf_codec_driver"; + } + } +... + } +``` + +#### Driver Implementation + +Modify the driver file to implement the driver logic that is the same as that of **moduleName** of **device_info.hcs**. + +``` +/* HdfDriverEntry implementations */ +static int32_t Es8316DriverBind(struct HdfDeviceObject *device) +{ +... + return HDF_SUCCESS; +} + +static int32_t Es8316DriverInit(struct HdfDeviceObject *device) +{ +... + return HDF_SUCCESS; +} + +/* HdfDriverEntry definitions */ +struct HdfDriverEntry g_es8316DriverEntry = { + .moduleVersion = 1, + .moduleName = "CODEC_ES8316", + .Bind = Es8316DriverBind, + .Init = Es8316DriverInit, + .Release = NULL, +}; +HDF_INIT(g_es8316DriverEntry); +``` + +### Summary + + A unified architecture is provided for audio development of OpenHarmony and unified APIs are provided for audio drivers of various platforms based on the ADM of the HDF framework. Audio drivers developed for one platform can be applied to multiple platforms, improving the development efficiency. This document briefly introduces the ADM to help developers develop their applications. + +## **Camera** + +### Introduction + +This section describes how to develop the audio driver framework based on the Hardware Driver Foundation (HDF) in OpenHarmony 3.0, including detailed introduction to the composition of the camera driver framework, functional component implementation, and service nodes. + +### Camera driver framework + + OpenHarmony HDF camera driver module architecture + +![img](https://gitee.com/openharmony/drivers_peripheral/raw/master/camera/figures/logic-view-of-modules-related-to-this-repository_zh.png) + +The following uses the camera host as an example: + +1. HDI implementation: implements standard device APIs for OpenHarmony cameras. +2. PipelineCorer: connects to the HDI implementation layer for control instruction and stream transfer, establishes data channels, and manages camera devices. +3. Platform Adaptation: shields the differences between bottom-layer chips and OSs for multi-platform adaptation. + +According to analysis of the USB camera of RK3399E/T, Linux 4.19 is used as the kernel. The USB camera depends on the UVC of V4L2 under Linux. According to the architecture diagram, the HDF camera is compatible with the UVC of V4L2 under Linux. Therefore, you need to ensure that the USB and camera drivers required by the UVC are normal before debugging. + + +### Camera Driver Introduction + +#### Configuration + +arch/arm64/configs/rockchip_linux_defconfig + +```c +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +``` + +#### Node Information + +Before inserting the USB camera: + +``` +# ls -l dev/video* +crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0 +crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1 +crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2 +crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3 +crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4 +crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5 +crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6 +crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7 +crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8 +crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9 +# +``` + +After inserting the USB camera, add the **dev/video10** and **dev/video11** nodes. + +``` +# ls -l dev/video* +crw-rw---- 1 root root 81, 0 2013-01-18 10:59 dev/video0 +crw-rw---- 1 root root 81, 1 2013-01-18 10:59 dev/video1 +crw------- 1 root root 81, 10 2013-01-18 11:01 dev/video10 +crw------- 1 root root 81, 11 2013-01-18 11:01 dev/video11 +crw-rw---- 1 root root 81, 2 2013-01-18 10:59 dev/video2 +crw-rw---- 1 root root 81, 3 2013-01-18 10:59 dev/video3 +crw-rw---- 1 root root 81, 4 2013-01-18 10:59 dev/video4 +crw-rw---- 1 root root 81, 5 2013-01-18 10:59 dev/video5 +crw-rw---- 1 root root 81, 6 2013-01-18 10:59 dev/video6 +crw-rw---- 1 root root 81, 7 2013-01-18 10:59 dev/video7 +crw-rw---- 1 root root 81, 8 2013-01-18 10:59 dev/video8 +crw-rw---- 1 root root 81, 9 2013-01-18 10:59 dev/video9 +# +``` + +#### Opening a Device Node + +In the code environment of OpenHarmony, compile the following code as an executable program and execute the program on the development board. The node is opened successfully if no error is reported. + +```c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + // 1. Open the device. + int fd = open("/dev/video10", O_RDWR); + if (fd < 0) { + printf("open device fail\n"); + return -1; + } + + close(fd); + return 0; +} +``` + +#### Obtaining Parameters + +```c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + // 1. Open the device. + int fd = open("/dev/video10", O_RDWR); + if (fd < 0) { + printf("open device fail\n"); + return -1; + } + + // 2. Obtain the format ioctl (file descriptor, command, and structure corresponding to the command) supported by the camera. + struct v4l2_fmtdesc v4fmt; + v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int i = 0; + while(1) { + v4fmt.index = i++; + int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt); + if (ret < 0) { + printf("get fmt fail\n"); + } + unsigned char *p = (unsigned char*)&v4fmt.pixelformat; + printf("index=%d\n", v4fmt.index); + printf("flags=%d\n", v4fmt.flags); + printf("description=%s\n", v4fmt.description); + printf("pixelformat=%c%c%c%c\n", p[0], p[1], p[2], p[3]); + printf("reserved=%d\n", v4fmt.reserved[0]); + } + + close(fd); + return 0; +} +``` + +In the code environment of OpenHarmony, compile the preceding code as an executable program and execute the program on the development board. The result shows that the YUYV and MJPEG formats are supported. + + index=0 + flags=0 + description=YUYV 4:2:2 + pixelformat=YUYV + reserved=0 + + index=1 + flags=1 + description=Motion-JPEG + pixelformat=MJPG + reserved=0 +#### Setting the Buffer Queue + +```c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + // 1. Open the device. + int fd = open("/dev/video10", O_RDWR); + if (fd < 0) { + printf("open device fail\n"); + return -1; + } + + // 2. Set the collection format. + struct v4l2_format vfmt; + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vfmt.fmt.pix.width = 640; + vfmt.fmt.pix.height = 480; + vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // Set the video capture format (which must be the same as the one obtained above). + int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt); // Set the format. + if (ret < 0) { + printf("set fmt fail\n"); + return -1; + } + memset(&vfmt, 0, sizeof(vfmt)); + vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = ioctl(fd, VIDIOC_G_FMT, &vfmt); // Obtain the format. + if (ret < 0) { + printf("set->get fmt fail\n"); + return -1; + } + + // 3. Apply for the kernel buffer queue. + struct v4l2_requestbuffers reqbuffer; + reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + reqbuffer.count = 4; // Apply for four buffers. + reqbuffer.memory = V4L2_MEMORY_MMAP; // Memory mapping method: MMAP/USERPTR + ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); // Allocate memory. + if (ret < 0) { + printf("req buffer fail\n"); + return -1; + } + + // 4. Stop the device. + close(fd); + return 0; +} +``` + +In the code environment of OpenHarmony, compile the preceding code as an executable program and execute the program on the development board. + +Execution result: **req buffer fail** + +Cause analysis: ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); failure + +Troubleshooting method 1: Add logs to the kernel to locate the position where **VIDIOC_REQBUFS** fails. After the **ioctl** command is delivered, the **video_usercopy** API in **drivers/media/v4l2-core/v4l2-ioctl.c** will be used. However, the specific failure cause is still not found. + +Troubleshooting method 2: Contact Smart Device Tech to check whether the USB camera of the debian version of the Linux 4.19 kernel works properly. Result: After using the **gst-launch-1.0 v4l2src device=/dev/video10 ! image/jpeg, width= 1280, height=720, framerate=30/1 ! jpegparse ! mppjpegdec ! kmssink sync=false** command in the debian version, the HDMI screen can display the preview image properly. + +Through the preceding operations, you can determine that the UVC driver of V4L2 under Linux and the external USB camera driver work properly. Next, you need to debug the HDF camera of OpenHarmony. + +#### API Introduction + +Check the camera-related executable programs in OpenHarmony: **ohos_camera_demo** and **v4l2_main**. + +##### ohos_camera_demo + +Execution result: After you enter **o**, no preview image is displayed and no error logs are recorded. + +``` +# ohos_camera_demo +GetUintParameter debug.bytrace.tags.enableflags error. +Options: +-h | --help Print this message +-o | --offline stream offline test +-c | --capture capture one picture +-w | --set WB Set white balance Cloudy +-v | --video capture Viedeo of 10s +-a | --Set AE Set Auto exposure +-f | --Set Flashlight Set flashlight ON 5s OFF +-q | --quit stop preview and quit this app +o +Options: +-h | --help Print this message +-o | --offline stream offline test +-c | --capture capture one picture +-w | --set WB Set white balance Cloudy +-v | --video capture Viedeo of 10s +-a | --Set AE Set Auto exposure +-f | --Set Flashlight Set flashlight ON 5s OFF +-q | --quit stop preview and quit this app + +``` + +Cause analysis: Currently, **ohos_camera_demo** supports only MPP and does not support V4L2. Therefore, the debugging of this demo is canceled. + +##### v4l2_main + +Execution result: After you enter **u**, error message **ERROR:main test:cannot open framebuffer /dev/fb0 file node** is displayed. + +``` +Options: +-h | --help Print this message +-p | --preview start preview on platform sensor +-c | --capture capture one picture +-w | --set WB Set white balance Cloudy +-e | --Set AE Set exposure time +-v | --video capture Viedeo of 10s +-u | --uvc start preview on uvc preview +-a | --Set ATE Set Auto exposure +-q | --quit stop preview and quit this app +INFO:please input command(input -q exit this app) + +u +ERROR:main test:cannot open framebuffer /dev/fb0 file node + +INFO:V4L2OpenDevice /dev/video10 +``` + +Cause analysis: Check whether the **dev/fb0** node exists. The **fb0** node does not exist. Check whether there are other **fb0** nodes in the root directory. The **dev/graphics/fb0** node exists. + +``` +# ls -l dev/fb0 +ls: dev/fb0: No such file or directory + +# find -name fb0 +./dev/graphics/fb0 +./sys/class/graphics/fb0 +./sys/devices/platform/display-subsystem/graphics/fb0 +``` + +You need to change **dev/fb0** in the executable program **v4l2_main** to **dev/graphics/fb0**. Here, **fb0** is **framebuffer**, which is used to display the preview image on the screen. + +Modification: drivers/peripheral + +```diff +diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp +index b351f49..d9c4cb3 100755 +--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp ++++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp +@@ -186,9 +186,9 @@ RetCode FBInit() + if (g_fbFd) + return RC_OK; + +- g_fbFd = open("/dev/fb0", O_RDWR); ++ g_fbFd = open("/dev/graphics/fb0", O_RDWR); + if (g_fbFd < 0) { +- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0"); ++ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0"); + return RC_ERROR; + } + +diff --git a/camera/hal/test/v4l2/src/test_display.cpp b/camera/hal/test/v4l2/src/test_display.cpp +index db908e7..7025deb 100644 +--- a/camera/hal/test/v4l2/src/test_display.cpp ++++ b/camera/hal/test/v4l2/src/test_display.cpp +@@ -114,9 +114,9 @@ void TestDisplay::FBLog() + + RetCode TestDisplay::FBInit() + { +- fbFd_ = open("/dev/fb0", O_RDWR); ++ fbFd_ = open("/dev/graphics/fb0", O_RDWR); + if (fbFd_ < 0) { +- CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0"); ++ CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0"); + return RC_ERROR; + } + +@@ -439,4 +439,4 @@ void TestDisplay::StopStream(std::vector& captureIds, std::vector& str + std::cout << "==========[test log]check Capture: ReleaseStreams fail, rc = " << rc << std::endl; + } + } +-} +\ No newline at end of file ++} +diff --git a/display/hal/default/display_layer.c b/display/hal/default/display_layer.c +index ee7a825..e12a653 100644 +--- a/display/hal/default/display_layer.c ++++ b/display/hal/default/display_layer.c +@@ -24,7 +24,7 @@ + + #define DEV_ID 0 + #define LAYER_ID 0 +-#define FB_PATH "/dev/fb0" ++#define FB_PATH "/dev/graphics/fb0" + #define DISP_WIDTH 800 + #define DISP_HEIGHT 480 + #define BITS_PER_PIXEL 32 +``` + +Run the **./build.sh --product-name rk3399 --ccache --build-target v4l2_main** command to recompile **v4l2_main** after modification. + +The executable program path after successful compilation is **./out/rk3399/hdf/hdf/v4l2_main**. + +Push the newly compiled **v4l2_main** to the **system/bin** directory on the development board. + +``` +hdc shell "mount -o rw,remount /" +hdc file send D:\cyyanl\work\RockChip\bin\v4l2_main /system/bin +``` + +Continue to execute **v4l2_main**. If no **framebuffer** error is reported, the issue has been resolved. (Another idea is to analyze why **fb0** is in **/dev/graphics/fb0** instead of **/dev/fb0**, and then change **fb0** to **/dev/fb0**. This idea will be debugged later.) + +New error: **ERROR:error: ioctl VIDIOC_QUERYBUF failed.** + +``` +Options: +-h | --help Print this message +-p | --preview start preview on platform sensor +-c | --capture capture one picture +-w | --set WB Set white balance Cloudy +-e | --Set AE Set exposure time +-v | --video capture Viedeo of 10s +-u | --uvc start preview on uvc preview +-a | --Set ATE Set Auto exposure +-q | --quit stop preview and quit this app +INFO:please input command(input -q exit this app) +u +INFO:the fixed information is as follow: +INFO:id= +INFO:sem_start=0 +INFO:smem_len=2457600 +... +INFO:V4L2AllocBuffer +INFO:V4L2AllocBuffer:memoryType_ = 2 +INFO:V4L2AllocBuffer:V4L2_MEMORY_USERPTR = 2 +INFO:V4L2AllocBuffer:VIDIOC_QUERYBUF = 3226490377 +ERROR:error: ioctl VIDIOC_QUERYBUF failed. +ERROR:error: Creatbuffer: V4L2AllocBuffer error +ERROR:main test:V4L2PreviewThread CreatBuffer fail i = 0 +``` + +Cause analysis: ioctl(fd, VIDIOC_QUERYBUF, &buf) failure. In the section for setting the format and applying for the buffer queue in the Linux L4V2 UVC driver chapter, the following error is reported: + +ioctl(fd, VIDIOC_REQBUFS, &reqbuffer). Therefore, we can conclude that **ioctl VIDIOC_REQBUFS** on OpenHarmony will always report an error. The differences between the two failures are as follows: + +The memory mapping methods are different: V4L2_MEMORY_MMAP and V4L2_MEMORY_USERPTR + +According to issues of OpenHarmony, memory mapping method V4L2_MEMORY_MMAP is not supported. Next, we still use V4L2_MEMORY_USERPTR of **v4l2_main** for debugging and analysis. + +For details, see [Method of Applying for Memory by Adding MMAP on V4L2 Devices](https://gitee.com/openharmony/drivers_peripheral/issues/I4EFWP). + + +Now, analyze the **ioctl(fd, VIDIOC_QUERYBUF, &buf)** failure. View the definition of VIDIOC_QUERYBUF: **videodev2.h** + +```c +#define VIDIOC_QUERYBUF_IOWR('V', 9, struct v4l2_buffer) +``` + +Insert the **ioctl** definition **int ioctl(int fd, int cmd, …); VIDIOC_QUERYBUF** as the input parameter of **cmd**. The input parameter is of the **int** type. This is a digital command code. After the code is sent to the kernel using **ioctl**, the corresponding function operation will be performed. Therefore, the command code delivered in the user state must be consistent with that received by the kernel. Next, let's check the command code consistency. + +**videodev2.h** contains 77 command codes for interaction with the kernel. Print the command codes in kernel state and compare them with those in the user state. + +Printing in user state: drivers/peripheral + +```diff +diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp +index d7dd15f..f7254b4 100644 +--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp ++++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp +@@ -162,37 +162,119 @@ RetCode HosV4L2Buffers::V4L2DequeueBuffer(int fd) + return RC_OK; + } + ++static void cyyanl_printf_cmd(void) ++{ ++#if 1 ++ CAMERA_LOGD("*************************************************************************************"); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = 0x%x\n", VIDIOC_QUERYCAP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = 0x%x\n", VIDIOC_ENUM_FMT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = 0x%x\n", VIDIOC_G_FMT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = 0x%x\n", VIDIOC_S_FMT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = 0x%x\n", VIDIOC_REQBUFS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = 0x%x\n", VIDIOC_QUERYBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = 0x%x\n", VIDIOC_G_FBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = 0x%x\n", VIDIOC_S_FBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = 0x%x\n", VIDIOC_OVERLAY ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = 0x%x\n", VIDIOC_QBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = 0x%x\n", VIDIOC_EXPBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = 0x%x\n", VIDIOC_DQBUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = 0x%x\n", VIDIOC_STREAMON ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = 0x%x\n", VIDIOC_STREAMOFF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = 0x%x\n", VIDIOC_G_PARM ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = 0x%x\n", VIDIOC_S_PARM ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = 0x%x\n", VIDIOC_G_STD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = 0x%x\n", VIDIOC_S_STD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = 0x%x\n", VIDIOC_ENUMSTD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = 0x%x\n", VIDIOC_ENUMINPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = 0x%x\n", VIDIOC_G_CTRL ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = 0x%x\n", VIDIOC_S_CTRL ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = 0x%x\n", VIDIOC_G_TUNER ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = 0x%x\n", VIDIOC_S_TUNER ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = 0x%x\n", VIDIOC_G_AUDIO ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = 0x%x\n", VIDIOC_S_AUDIO ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = 0x%x\n", VIDIOC_QUERYCTRL ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = 0x%x\n", VIDIOC_QUERYMENU ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = 0x%x\n", VIDIOC_G_INPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = 0x%x\n", VIDIOC_S_INPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = 0x%x\n", VIDIOC_G_EDID ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = 0x%x\n", VIDIOC_S_EDID ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = 0x%x\n", VIDIOC_G_OUTPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = 0x%x\n", VIDIOC_S_OUTPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = 0x%x\n", VIDIOC_ENUMOUTPUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = 0x%x\n", VIDIOC_G_AUDOUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = 0x%x\n", VIDIOC_S_AUDOUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = 0x%x\n", VIDIOC_G_MODULATOR ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = 0x%x\n", VIDIOC_S_MODULATOR ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = 0x%x\n", VIDIOC_G_FREQUENCY ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = 0x%x\n", VIDIOC_S_FREQUENCY ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = 0x%x\n", VIDIOC_CROPCAP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = 0x%x\n", VIDIOC_G_CROP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = 0x%x\n", VIDIOC_S_CROP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = 0x%x\n", VIDIOC_G_JPEGCOMP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = 0x%x\n", VIDIOC_S_JPEGCOMP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = 0x%x\n", VIDIOC_QUERYSTD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = 0x%x\n", VIDIOC_TRY_FMT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = 0x%x\n", VIDIOC_ENUMAUDIO ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = 0x%x\n", VIDIOC_ENUMAUDOUT ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = 0x%x\n", VIDIOC_G_PRIORITY ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = 0x%x\n", VIDIOC_S_PRIORITY ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = 0x%x\n", VIDIOC_G_SLICED_VBI_CAP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = 0x%x\n", VIDIOC_LOG_STATUS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = 0x%x\n", VIDIOC_G_EXT_CTRLS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = 0x%x\n", VIDIOC_S_EXT_CTRLS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = 0x%x\n", VIDIOC_TRY_EXT_CTRLS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = 0x%x\n", VIDIOC_ENUM_FRAMESIZES ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = 0x%x\n", VIDIOC_ENUM_FRAMEINTERVALS); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = 0x%x\n", VIDIOC_G_ENC_INDEX ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = 0x%x\n", VIDIOC_ENCODER_CMD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = 0x%x\n", VIDIOC_TRY_ENCODER_CMD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = 0x%x\n", VIDIOC_S_HW_FREQ_SEEK ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = 0x%x\n", VIDIOC_CREATE_BUFS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = 0x%x\n", VIDIOC_PREPARE_BUF ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = 0x%x\n", VIDIOC_G_SELECTION ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = 0x%x\n", VIDIOC_S_SELECTION ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = 0x%x\n", VIDIOC_DECODER_CMD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = 0x%x\n", VIDIOC_TRY_DECODER_CMD ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = 0x%x\n", VIDIOC_ENUM_DV_TIMINGS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = 0x%x\n", VIDIOC_QUERY_DV_TIMINGS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = 0x%x\n", VIDIOC_DV_TIMINGS_CAP ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = 0x%x\n", VIDIOC_ENUM_FREQ_BANDS ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = 0x%x\n", VIDIOC_DBG_G_CHIP_INFO ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = 0x%x\n", VIDIOC_QUERY_EXT_CTRL ); ++ CAMERA_LOGD("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = 0x%x\n", BASE_VIDIOC_PRIVATE ); ++ CAMERA_LOGD("*************************************************************************************"); ++#endif ++} ++ + RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr& frameSpec) + { + struct v4l2_buffer buf = {}; + struct v4l2_plane planes[1] = {}; +- CAMERA_LOGD("V4L2AllocBuffer\n"); ++ CAMERA_LOGD("V4L2AllocBuffer enter\n"); + ++ cyyanl_printf_cmd(); + if (frameSpec == nullptr) { + CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n"); + return RC_ERROR; + } +- + switch (memoryType_) { + case V4L2_MEMORY_MMAP: + // to do something + break; + case V4L2_MEMORY_USERPTR: ++ CAMERA_LOGD("V4L2AllocBuffer:V4L2_MEMORY_USERPTR = %d\n", V4L2_MEMORY_USERPTR); + buf.type = bufferType_; + buf.memory = memoryType_; + buf.index = (uint32_t)frameSpec->buffer_->GetIndex(); +- + if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { ++ CAMERA_LOGD("V4L2AllocBuffer:V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = %d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + buf.m.planes = planes; + buf.length = 1; + } +``` + +Printing in kernel state: kernel/linux/linux-4.19/ + +```diff +diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c +index 0842a47c6..8aa60407f 100644 +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -2902,10 +2902,93 @@ struct uvc_driver uvc_driver = { + }, + }; + ++static void cyyanl_printk_cmd(void) ++{ ++ printk("*************************************************************************************"); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP ) = %ld\n", VIDIOC_QUERYCAP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT ) = %ld\n", VIDIOC_ENUM_FMT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT ) = %ld\n", VIDIOC_G_FMT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT ) = %ld\n", VIDIOC_S_FMT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS ) = %ld\n", VIDIOC_REQBUFS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF ) = %ld\n", VIDIOC_QUERYBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF ) = %ld\n", VIDIOC_G_FBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF ) = %ld\n", VIDIOC_S_FBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY ) = %ld\n", VIDIOC_OVERLAY ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF ) = %ld\n", VIDIOC_QBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF ) = %ld\n", VIDIOC_EXPBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF ) = %ld\n", VIDIOC_DQBUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON ) = %ld\n", VIDIOC_STREAMON ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF ) = %ld\n", VIDIOC_STREAMOFF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM ) = %ld\n", VIDIOC_G_PARM ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM ) = %ld\n", VIDIOC_S_PARM ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD ) = %ld\n", VIDIOC_G_STD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD ) = %ld\n", VIDIOC_S_STD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD ) = %ld\n", VIDIOC_ENUMSTD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT ) = %ld\n", VIDIOC_ENUMINPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL ) = %ld\n", VIDIOC_G_CTRL ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL ) = %ld\n", VIDIOC_S_CTRL ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER ) = %ld\n", VIDIOC_G_TUNER ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER ) = %ld\n", VIDIOC_S_TUNER ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO ) = %ld\n", VIDIOC_G_AUDIO ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO ) = %ld\n", VIDIOC_S_AUDIO ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL ) = %ld\n", VIDIOC_QUERYCTRL ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU ) = %ld\n", VIDIOC_QUERYMENU ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT ) = %ld\n", VIDIOC_G_INPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT ) = %ld\n", VIDIOC_S_INPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID ) = %ld\n", VIDIOC_G_EDID ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID ) = %ld\n", VIDIOC_S_EDID ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT ) = %ld\n", VIDIOC_G_OUTPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT ) = %ld\n", VIDIOC_S_OUTPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT ) = %ld\n", VIDIOC_ENUMOUTPUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT ) = %ld\n", VIDIOC_G_AUDOUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT ) = %ld\n", VIDIOC_S_AUDOUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR ) = %ld\n", VIDIOC_G_MODULATOR ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR ) = %ld\n", VIDIOC_S_MODULATOR ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY ) = %ld\n", VIDIOC_G_FREQUENCY ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY ) = %ld\n", VIDIOC_S_FREQUENCY ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP ) = %ld\n", VIDIOC_CROPCAP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP ) = %ld\n", VIDIOC_G_CROP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP ) = %ld\n", VIDIOC_S_CROP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP ) = %ld\n", VIDIOC_G_JPEGCOMP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP ) = %ld\n", VIDIOC_S_JPEGCOMP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD ) = %ld\n", VIDIOC_QUERYSTD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT ) = %ld\n", VIDIOC_TRY_FMT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO ) = %ld\n", VIDIOC_ENUMAUDIO ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT ) = %ld\n", VIDIOC_ENUMAUDOUT ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY ) = %ld\n", VIDIOC_G_PRIORITY ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY ) = %ld\n", VIDIOC_S_PRIORITY ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP ) = %ld\n", VIDIOC_G_SLICED_VBI_CAP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS ) = %ld\n", VIDIOC_LOG_STATUS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS ) = %ld\n", VIDIOC_G_EXT_CTRLS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS ) = %ld\n", VIDIOC_S_EXT_CTRLS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS ) = %ld\n", VIDIOC_TRY_EXT_CTRLS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES ) = %ld\n", VIDIOC_ENUM_FRAMESIZES ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = %ld\n", VIDIOC_ENUM_FRAMEINTERVALS); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX ) = %ld\n", VIDIOC_G_ENC_INDEX ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD ) = %ld\n", VIDIOC_ENCODER_CMD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD ) = %ld\n", VIDIOC_TRY_ENCODER_CMD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK ) = %ld\n", VIDIOC_S_HW_FREQ_SEEK ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS ) = %ld\n", VIDIOC_CREATE_BUFS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF ) = %ld\n", VIDIOC_PREPARE_BUF ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION ) = %ld\n", VIDIOC_G_SELECTION ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION ) = %ld\n", VIDIOC_S_SELECTION ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD ) = %ld\n", VIDIOC_DECODER_CMD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD ) = %ld\n", VIDIOC_TRY_DECODER_CMD ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS ) = %ld\n", VIDIOC_ENUM_DV_TIMINGS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS ) = %ld\n", VIDIOC_QUERY_DV_TIMINGS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP ) = %ld\n", VIDIOC_DV_TIMINGS_CAP ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS ) = %ld\n", VIDIOC_ENUM_FREQ_BANDS ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO ) = %ld\n", VIDIOC_DBG_G_CHIP_INFO ); ++ printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL ) = %ld\n", VIDIOC_QUERY_EXT_CTRL ); ++ printk("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE ) = %ld\n", BASE_VIDIOC_PRIVATE ); ++ printk("*************************************************************************************"); ++} ++ + static int __init uvc_init(void) + { + int ret; +- ++ printk("cyyanl enter uvc_init\n"); ++ cyyanl_printk_cmd(); + uvc_debugfs_init(); + + ret = usb_register(&uvc_driver.driver); +``` + +Printing result comparison: **VIDIOC_QUERYBUF** is **0xc0505609** in user state and **0xc0585609** in kernel state. There are several other inconsistent command codes. + +![](figures/isoftstone/yangfan-print-01.png) + +Analyze the cause for command code inconsistency. First, find the differences during compilation of **VIDIOC_QUERYBUF** in user state and kernel state. + +User state: kernel/linux/patches/linux-5.10/prebuilts/usr/include/linux/videodev2.h +1358 + +Kernel state: kernel/linux/linux-4.19/include/uapi/linux/videodev2.h +2361 + +The possible cause is that **VIDIOC_QUERYBUF** inconsistency is caused by header file differences. (Currently, whether the issue can be solved by using linux-4.19 is not verified.) Seek help from the customer and check whether there are similar issues in OpenHarmony. + +A similar issue is found: https://gitee.com/openharmony/drivers_peripheral/issues/I4NI4M?from=project-issue + + +Now, there are two debugging ideas: + +1. Unify **VIDIOC_QUERYBUF** in user state with that in kernel state. + + Solution: Change **0xc0505609** in user state to **0xc0585609** in kernel state. + + Modification: drivers/peripheral + + ```diff + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp + index d7dd15f..f7254b4 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp + buf.m.planes = planes; + buf.length = 1; + } + - CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index); + - + - if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) { + - CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno)); + + if (ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) < 0) { + + CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed.\n"); + return RC_ERROR; + } + ``` + + Run **v4l2_main** again. Result: Signal 4 reports an error. + + Cause analysis: **ioctl(fd, /*****VIDIOC_QUERYBUF*****/0xc0585609, &buf)** is successful. According to Signal 4 analysis, the error is possibly still caused by the command codes of **cmd**. + + This idea can solve the current issue, but it will cause a new Signal 4 error. + + ``` + INFO:main test:allocating display buffer memory + INFO:main test:do_mmap: pmem mmap fd 5 ptr 0xf7508000 len 2457600 + INFO:V4L2OpenDevice /dev/video10 + INFO:V4L2ReqBuffers buffCont 4 + INFO:Creatbuffer frameSpec->buffer index == 0 + INFO:V4L2AllocBuffer + Signal 4 + ``` + +2. Analyze the root cause of command code inconsistency and make modifications. + + Cause analysis: The size of the structure varies according to the 32-bit and 64-bit compilation, which affects the value of **VIDIOC_QUERYBUF**. + + Modification: Modify the timestamp definition of the **v4l2_buffer** structure compiled in user state, and replace the header file **#include ** compiled in user state with + + the modified **videodev2.h** file, that is, **#include "videodev2.h"**. (The actual operation is to copy the modified **videodev2.h** file to the **v4l2_main** compilation directory.) + + kernel/linux/linux-4.19/ + + ```diff + diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h + index ba548d7f0..b0fb48f65 100644 + --- a/include/uapi/linux/videodev2.h + +++ b/include/uapi/linux/videodev2.h + @@ -958,13 +958,20 @@ struct v4l2_plane { + * Contains data exchanged by application and driver using one of the Streaming + * I/O methods. + */ + + + +struct timeval_user { + + long tv_sec; + + long tv_usec; + +}; + + + struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + - struct timeval timestamp; + + //struct timeval timestamp; + + struct timeval_user timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + ``` + + drivers/peripheral + + ```diff + diff --git a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h + index 00ddea7..962ebc3 100755 + --- a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h + +++ b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h + @@ -15,7 +15,8 @@ + + #ifndef HOS_CAMERA_PROJET_HARDWARE_H + #define HOS_CAMERA_PROJET_HARDWARE_H + -#include + +//#include + +#include "videodev2.h" + + namespace OHOS::Camera { + #define PREVIEW_PIXEL_FORMAT V4L2_PIX_FMT_YUV420 + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h + index 6f45882..a8d6819 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h + @@ -19,7 +19,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include + #include "v4l2_common.h" + #if defined(V4L2_UTEST) || defined (V4L2_MAIN_TEST) + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h + index 5b93f36..05191a7 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h + @@ -16,7 +16,8 @@ + #ifndef HOS_CAMERA_V4L2_CONTROL_H + #define HOS_CAMERA_V4L2_CONTROL_H + + -#include + +//#include + +#include "videodev2.h" + #include + #include + #include "v4l2_common.h" + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h + index 10dc9b4..e3b3056 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h + @@ -19,7 +19,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include + #include + #include + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h + index de892e9..44bb1b4 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h + @@ -19,7 +19,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include + #include + #include + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h + index 1a62f37..96c70aa 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h + @@ -18,7 +18,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include + #include + #include zz + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp + index b351f49..5483b85 100755 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp + @@ -22,7 +22,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include "securec.h" + #include "v4l2_uvc.h" + #include "v4l2_dev.h" + diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp + index 778cf05..96618be 100644 + --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp + +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp + @@ -14,7 +14,8 @@ + */ + + #include + -#include + +//#include + +#include "videodev2.h" + #include + + #include "v4l2_stream.h" + diff --git a/camera/hal/test/v4l2/include/test_display.h b/camera/hal/test/v4l2/include/test_display.h + index d437e26..8e5205e 100644 + --- a/camera/hal/test/v4l2/include/test_display.h + +++ b/camera/hal/test/v4l2/include/test_display.h + @@ -44,7 +44,8 @@ + #include + #include + #include + -#include + +//#include + +#include "videodev2.h" + #include + #include + #include + @@ -138,4 +139,4 @@ public: + void StartCapture(int streamId, int captureId, bool shutterCallback, bool isStreaming); + float calTime(struct timeval start, struct timeval end); + }; + -#endif + \ No newline at end of file + +#endif + ``` + + Compile **v4l2_main** again and execute it. No error is recorded in logs. The HDMI screen displays the preview image. + + + +According to the entire debugging process, the camera supports the YUYV and MJPEG formats. By default, preview in **v4l2_main** uses the YUYV format. Change the format to MJPEG and try again. + +Modification: + +```diff +diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp +index b351f49..5483b85 100755 +--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp ++++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp +@@ -394,7 +395,9 @@ void V4L2SetDeviceFormat(DeviceFormat& format, const std::string devname) + + if (devname == "uvcvideo" || devname == "uvcvideo1") { + if (g_isPreviewOnUvc) { +- format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; ++ CAMERA_LOGD("cyyanl enter V4L2SetDeviceFormat : g_isPreviewOnUvc\n"); ++ //format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; ++ format.fmtdesc.pixelformat = V4L2_PIX_FMT_MJPEG; + format.fmtdesc.width = width; + format.fmtdesc.height = height; + } +``` + +Execution result of **v4l2_main**: The display is abnormal. Currently, do not analyze the MJPEG format. + +![](figures/isoftstone/yangfan-picture-v4l2_main.png) + + + +So far, **v4l2_main** runs properly and images can be previewed. (The abnormal color of the preview image is related to the screen display format and does not need to be modified currently.) Next, debug photographing and video recording. + +After **v4l2_main** is executed, enter **u** to preview the UVC, run **c** to take photos, and then run **v** to record videos. The photo and video files are generated in the current execution path. + +``` +# ls -l *.jpeg +-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC0.jpeg +-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC1.jpeg +-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC2.jpeg +-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC3.jpeg +# +# ls -l *.h264 +-rwxrw-rw- 1 root 29034400 85401600 2013-01-18 15:20 uvc.h264 +``` + +Export the preceding files to the PC and check them. + +image **uvc0.jpeg** + +![](figures/isoftstone/yangfan-picture-uvc0.png) + +The video file **uvc.h264** can be viewed on the mobile phone and played properly. + +### Appendixes + +```c +/** + * struct v4l2_buffer - video buffer info + * @index: id number of the buffer + * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for + * multiplanar buffers); + * @bytesused: number of bytes occupied by data in the buffer (payload); + * unused (set to 0) for multiplanar buffers + * @flags: buffer informational flags + * @field: enum v4l2_field; field order of the image in the buffer + * @timestamp: frame timestamp + * @timecode: frame timecode + * @sequence: sequence count of this frame + * @memory: enum v4l2_memory; the method, in which the actual video data is + * passed + * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; + * offset from the start of the device memory for this plane, + * (or a "cookie" that should be passed to mmap() as offset) + * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; + * a userspace pointer pointing to this buffer + * @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF; + * a userspace file descriptor associated with this buffer + * @planes: for multiplanar buffers; userspace pointer to the array of plane + * info structs for this buffer + * @length: size in bytes of the buffer (NOT its payload) for single-plane + * buffers (when type != *_MPLANE); number of elements in the + * planes array for multi-plane buffers + * + * Contains data exchanged by application and driver using one of the Streaming + * I/O methods. + */ + +struct v4l2_buffer { + __u32 index; + __u32 type; + __u32 bytesused; + __u32 flags; + __u32 field; + struct timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + /* memory location */ + __u32 memory; + union { + __u32 offset; + unsigned long userptr; + struct v4l2_plane *planes; + __s32 fd; + } m; + __u32 length; + __u32 reserved2; + __u32 reserved; +} +``` + +## TP + +### TP Driver Model + +This model mainly defines and implements the following types of Hardware Driver Interfaces (HDIs) of the input module, allowing upper-layer input services to perform operations for the input devices: + +- **Input Manager**: manages input devices, including enabling and disabling input devices and obtaining the device list. +- **Input Reporter**: reports input events, including registering and unregistering data reporting callbacks. +- **Input Controller**: controls input devices, including obtaining the device information and device type, and setting power status. + +**Figure 1** HDI architecture of the input module + +![dayu200-tp-01.png](figures/dayu200/dayu200-tp-01.png) + +The source code directory structure is as follows: + +``` +/drivers/peripheral/input +├── hal # HAL code +│ └── include # HAL header files +│ └── src # HAL code implementation +├── interfaces # Driver capability APIs provided for upper-layer services +│ └── include # APIs exposed +├── test # Test code +│ └── unittest # Unit test code +``` + +For details, see [README](https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md) of the input subsystem. + +### TP HDF Driver Adaptation + +#### Configuration Adaptation + +Disable the Linux TP driver by disabling **TOUCHSCREEN_GT9XX** in the **** file. Specifically, change **CONFIG_TOUCHSCREEN_GT9XX=y** as follows: +```makefile +# CONFIG_TOUCHSCREEN_GT9XX is not set +``` +#### Driver Adaptation + +1. Modify content in **vendor/rockchip/rk3399/hdf_config/khdf/device_info/device_info.hcs** as follows or add the following content to the file: + + ```json + device_touch_chip :: device { + device0 :: deviceNode { + policy = 0; + priority = 130; + preload = 0; + permission = 0660; + moduleName = "HDF_TOUCH_GT911"; + serviceName = "hdf_touch_gt911_service"; + deviceMatchAttr = "zsj_gt911_5p5"; + } + } + ``` + + + +2. Modify content in **vendor/rockchip/rk3399/hdf_config/khdf/input/input_config.hcs** as follows or add the following content to the file: + + ```json + busConfig { + // 0:i2c 1:spi + busType = 0; // I2C communication method + busNum = 7; // I2C code of the touch chip corresponding to the CPU + clkGpio = 72; // I/O pin code corresponding to I2C clk + dataGpio = 71; // I/O pin code corresponding to I2C data + i2cClkIomux = [0xFF77E004, 0x2]; // I2C [reuse register corresponding to the clk pin, which is configured to the I2C7_clk function] + i2cDataIomux = [0xFF77E000, 0x8000]; // I2C [reuse register corresponding to the data pin, which is configured to the I2C7_DATA function] + } + + pinConfig { + rstGpio = 150; // I/O pin code corresponding to rst of the touch chip + intGpio = 52; // I/O pin code corresponding to int of the touch chip + rstRegCfg = [0xFF77E028, 0x00000000]; // Reuse register corresponding to the rst pin, which is configured to the common I/O port + intRegCfg = [0xFF770018, 0x00000000]; // Reuse register corresponding to the int pin, which is configured to the common I/O port + } + ``` +#### FAQs + +##### How to solve the problem that the portrait and landscape modes are reversed for the touchscreen? + +Modify the **ParsePointData** function in **drivers/framework/model/input/driver/touchscreen/touch_gt911.c** to exchange the position of **x** and **y**. + +##### Modify the **InputPinMuxCfg** function: + +```c +static int32_t InputPinMuxCfg(uint32_t regAddr, int32_t regSize, uint32_t regValue) +{ + uint8_t *base = NULL; + uint32_t data = 0; + if (regAddr == 0) { + HDF_LOGE("%s: regAddr invalid", __func__); + return HDF_FAILURE; + } + + HDF_LOGE("regAddr = 0x%x, regSize = 0x%x", regAddr, regSize); + base = OsalIoRemap(regAddr, regSize); + if (base == NULL) { + HDF_LOGE("%s: ioremap failed", __func__); + return HDF_FAILURE; + } + // Read and save the original configurations before writing new configurations to the register. Modify the original other pin function configurations. + data = OSAL_READL(base); + data |= regValue; + OSAL_WRITEL(data, base); + OsalIoUnmap((void *)base); + return HDF_SUCCESS; +} +``` + +##### How to determine the pin GPIOn_KK code of RK3399? + +```shell +num = 32n+(K-A)8+X +# GPIO1_B2 = 1 * 32 + (B-A)*8 + 2 = 32 + 8 + 1 = 42 +``` + +| Function | I/O Name | I/O Code| +| ------ | -------- | ------ | +| TP_INT | GPIO1_C4 | 52 | +| TP_RST | GPIO4_C6 | 150 | +| TP_SDA | GPIO2_A7 | 71 | +| TP_SCL | GPIO2_B0 | 72 | + + + +##### How to configure the pin GPIOn_KX reuse function of RK3399? + +1. Find the base address of the GPIO reuse function register, which is **FF77_0000**. +2. Find the register offset of the GPIOn_K reuse function. +3. Determine the GPIOn_K reuse function register by **adding the base address and offset**. +4. Write the reuse function of the corresponding pin based on the reuse function register introduction. + +| Function | I/O Name | Reuse Register Address| Control Bit| Value | +| ------ | -------- | -------------- | ------ | ------------------- | +| TP_INT | GPIO1_C4 | 0xFF770018 | 9:8 | 2'b00: GPIO | +| TP_RST | GPIO4_C6 | 0xFF77E028 | 13:12 | 2'b00: GPIO | +| TP_SDA | GPIO2_A7 | 0xFF77E000 | 15:14 | 2'b10: i2c7nfc_sda| +| TP_SCL | GPIO2_B0 | 0xFF77E004 | 1:0 | 2'b10: i2c7nfc_scl| diff --git a/en/device-dev/quick-start/quickstart-appendix-hi3516-ide.md b/en/device-dev/quick-start/quickstart-appendix-hi3516-ide.md index 0a49925cc10a4d4643b71aa8c7c69c953e6545b0..3e884353789abb5806753e6544dc7ada58320bb6 100644 --- a/en/device-dev/quick-start/quickstart-appendix-hi3516-ide.md +++ b/en/device-dev/quick-start/quickstart-appendix-hi3516-ide.md @@ -6,10 +6,10 @@ > > If you still need to use Hi3516DV300 to develop standard-system devices, adaptation may fail. In this case, contact the chip supplier to obtain the adaptation guide or complete adaptation by yourself. -In addition to the small system, the Hi3516D V300 development board also supports the standard system. This topic describes how to develop the standard system on Hi3516DV300 by using DevEco Device Tool. +In addition to the small system, the Hi3516DV300 development board also supports the standard system. This topic describes how to develop the standard system on Hi3516DV300 by using DevEco Device Tool. -The following exemplifies how to run the first program on the development board. The created program outputs the message "Hello World!" . +The following exemplifies how to run the first program on the development board. This program displays the message "Hello World!" . Before development, complete the following preparations: @@ -48,8 +48,7 @@ Perform the steps below in the source code directory: 1. Create a directory and write the service code. - -Create the **applications/sample/hello/src/helloworld.c** directory and file whose code is shown in the following example. You can customize the content to be printed. For example, you can change **World** to **OHOS**. Declare the string printing function **HelloPrint** in the **helloworld.h** file. You can use either C or C++ to develop a program. + Create the **applications/sample/hello/src/helloworld.c** directory and file whose code is shown in the following example. You can customize the content to be printed. For example, you can change **World** to **OHOS**. Declare the string printing function **HelloPrint** in the **helloworld.h** file. You can use either C or C++ to develop a program. ``` @@ -68,7 +67,7 @@ Create the **applications/sample/hello/src/helloworld.c** directory and file who } ``` -Add the header file **applications/sample/hello/include/helloworld.h**. The sample code is as follows: + Add the header file **applications/sample/hello/include/helloworld.h**. The sample code is as follows: ``` @@ -166,6 +165,7 @@ Add the header file **applications/sample/hello/include/helloworld.h**. The samp In the **productdefine/common/products/Hi3516DV300.json** file, add the **hello** part after the existing part. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > > In this example, the OpenHarmony-v3.1-Release version is used, where the Hi3516 configuration file is **productdefine/common/products/Hi3516DV300.json**. In OpenHarmony-v3.2-Beta2 and later versions, the Hi3516 configuration file is **vendor/hisilicon/Hi3516DV300/config.json**. @@ -284,7 +284,7 @@ Hi3516DV300 supports burning for the standard system through the USB port and ne ![en-us_image_0000001275802150](figures/en-us_image_0000001275802150.png) -8. When the burning is successful, perform the operations in "Running" to start the system. +8. When the burning is successful, perform the operations in [Running an Image](#running-an-image) to start the system. ## Running an Image diff --git a/en/device-dev/quick-start/quickstart-ide-3516-burn.md b/en/device-dev/quick-start/quickstart-ide-3516-burn.md index 98899abfa096f432831fd07af16bcd46cf902e8f..fe5f043f5a3ea77d5cc87cb66ef1e2488d92ac1f 100644 --- a/en/device-dev/quick-start/quickstart-ide-3516-burn.md +++ b/en/device-dev/quick-start/quickstart-ide-3516-burn.md @@ -66,4 +66,4 @@ Hi3516DV300 supports burning for the small system through the USB port, network ![en-us_image_0000001276317464](figures/en-us_image_0000001276317464.png) -8. When the burning is successful, perform the operations in [Running](quickstart-ide-3516-running.md) to start the system. +8. When the burning is successful, perform the operations in [Running an Image](quickstart-ide-3516-running.md) to start the system. diff --git a/en/device-dev/quick-start/quickstart-ide-3568-helloworld.md b/en/device-dev/quick-start/quickstart-ide-3568-helloworld.md index 7305d53004776c7b548ea3b2d624125403359ada..847a732e1522dfc38caf76a03eb3dfc6e5a339b8 100644 --- a/en/device-dev/quick-start/quickstart-ide-3568-helloworld.md +++ b/en/device-dev/quick-start/quickstart-ide-3568-helloworld.md @@ -1,7 +1,7 @@ # Writing a Hello World Program -The following exemplifies how to run the first program on the development board. The created program outputs the message "Hello World!" . +The following exemplifies how to run the first program on the development board. This program displays the message "Hello World!". ## Prerequisites @@ -157,7 +157,7 @@ Perform the steps below in the source code directory: In the **productdefine/common/products/rk3568.json** file, add the **hello** part after the existing part. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** > - > In this example, the OpenHarmony-v3.1-Release version is used, where the RK3568 configuration file is **productdefine/common/products/rk3568.json**. In a version later than OpenHarmony-v3.1-Release, the RK3568 configuration file is **vendor/hihope/rk3568/config.json**. + > In this example, the OpenHarmony-v3.1-Release version is used, where the RK3568 configuration file is **productdefine/common/products/rk3568.json**. In OpenHarmony-v3.2-Beta2 and later versions, the RK3568 configuration file is **vendor/hihope/rk3568/config.json**. ``` diff --git a/en/device-dev/subsystems/Readme-EN.md b/en/device-dev/subsystems/Readme-EN.md index fc41e111278fdd33759146e368eeab728555586f..5394fc67e6a3187eb7b83f1aba77b5aa2d3373cb 100644 --- a/en/device-dev/subsystems/Readme-EN.md +++ b/en/device-dev/subsystems/Readme-EN.md @@ -8,16 +8,18 @@ - [Building a Product](subsys-build-product.md) - [Building a Subsystem](subsys-build-subsystem.md) - [Building a Component](subsys-build-component.md) + - [Component Building Specifications](subsys-build-component-building-rules.md) - [Building a Module](subsys-build-module.md) - [Building a Chipset Solution](subsys-build-chip_solution.md) - [Configuring Features](subsys-build-feature.md) - [Configuring System Capabilities](subsys-build-syscap.md) - - [Setting deps and external_deps](subsys-build-reference.md#deps-and-external_deps) - - [Information Collected by the Open Source Software Notice](subsys-build-reference.md#information-collected-by-the-open-source-software-notice) - - [Configuring Parameters for Accelerating Local Build](subsys-build-reference.md#parameters-for-accelerating-local-build) - - [Viewing Ninja Build Information](subsys-build-reference.md#viewing-ninja-build-information) + - [Setting deps and external_deps](subsys-build-reference.md) + - [Information Collected by the Open Source Software Notice](subsys-build-reference.md) + - [Configuring Parameters for Accelerating Local Build](subsys-build-reference.md) + - [Viewing Ninja Build Information](subsys-build-reference.md) - [HAP Build Guide](subsys-build-gn-hap-compilation-guide.md) - [FAQs](subsys-build-FAQ.md) +- [ArkCompiler Development](subsys-arkcompiler-guide.md) - [Distributed Remote Startup](subsys-remote-start.md) - Graphics - [Graphics Overview](subsys-graphics-overview.md) @@ -82,11 +84,12 @@ - [Parameter Management](subsys-boot-init-sysparam.md) - [Sandbox Management](subsys-boot-init-sandbox.md) - [Plug-in Management](subsys-boot-init-plugin.md) + - [Component-based Startup](subsys-boot-init-sub-unit.md) + - [init Run Log Standardization](subsys-boot-init-log.md) - [appspawn Module](subsys-boot-appspawn.md) - [bootstrap Module](subsys-boot-bootstrap.md) - [FAQs](subsys-boot-faqs.md) - [Reference](subsys-boot-ref.md) -- [Test Case Development](subsys-testguide-test.md) - DFX - [DFX Overview](subsys-dfx-overview.md) - [HiLog Development](subsys-dfx-hilog-rich.md) @@ -95,6 +98,7 @@ - [HiTraceMeter Development](subsys-dfx-hitracemeter.md) - [HiCollie Development](subsys-dfx-hicollie.md) - HiSysEvent Development + - [HiSysEvent Overview](subsys-dfx-hisysevent-overview.md) - [HiSysEvent Logging Configuration](subsys-dfx-hisysevent-logging-config.md) - [HiSysEvent Logging](subsys-dfx-hisysevent-logging.md) - [HiSysEvent Listening](subsys-dfx-hisysevent-listening.md) @@ -106,7 +110,5 @@ - [Hiview Development](subsys-dfx-hiview.md) - R&D Tools - [bytrace](subsys-toolchain-bytrace-guide.md) - - [hdc\_std](subsys-toolchain-hdc-guide.md) + - [hdc_std](subsys-toolchain-hdc-guide.md) - [hiperf](subsys-toolchain-hiperf.md) -- [XTS Test Case Development](subsys-xts-guide.md) - diff --git a/en/device-dev/subsystems/subsys-app-privilege-config-guide.md b/en/device-dev/subsystems/subsys-app-privilege-config-guide.md index ba1fa37f70c3e748144d4bc88efe50bebfbd2c58..665652b23e1832716680e14d5552c66aec744062 100644 --- a/en/device-dev/subsystems/subsys-app-privilege-config-guide.md +++ b/en/device-dev/subsystems/subsys-app-privilege-config-guide.md @@ -24,7 +24,7 @@ General application privileges are privileges available to applications on all t ### Configuration -1. In the [HarmonyAppProvision file](../../application-dev/quick-start/app-provision-structure.md), configure the general privileges in the **app-privilege-capabilities** field. +1. In the [HarmonyAppProvision file](../../application-dev/security/app-provision-structure.md), configure the general privileges in the **app-privilege-capabilities** field. 2. Use the signing tool hapsigner to sign the HarmonyAppProvision file and generate a **.p7b** file. 3. Use the **.p7b** file to sign the HAP. diff --git a/en/device-dev/subsystems/subsys-arkcompiler-guide.md b/en/device-dev/subsystems/subsys-arkcompiler-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..44633a8b7eab098dc9d31bb948051cc82b4313b8 --- /dev/null +++ b/en/device-dev/subsystems/subsys-arkcompiler-guide.md @@ -0,0 +1,77 @@ +# ArkCompiler Development + +## Introduction +ArkCompiler is a unified programming platform. Its key components include a compiler, toolchain, and runtime. ArkCompiler supports compilation and running of high-level programming languages on the multi-chip platform and accelerates the running of applications and services on mobile phones, PCs, tablets, TVs, automobiles, and smart wearables. + +## Environment Setup +Ubuntu 18.04 or later is recommended. + +1. Install dependent tools. + ```shell + sudo apt-get update && sudo apt-get install python ruby python3-pip git-lfs gcc-multilib g++-multilib zlib1g-dev libc++1 curl nodejs + ``` +2. Install the **repo** tool. + ```shell + mkdir ~/bin/ + curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > ~/bin/repo + chmod a+x ~/bin/repo + export PATH=~/bin:$PATH + pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple requests + ``` +3. Download source code. + ```shell + repo init -u https://gitee.com/ark-standalone-build/manifest.git -b master + repo sync -c -j8 + repo forall -c 'git lfs pull' + ``` + +4. Install the compiler and binary tool. + ```shell + ./prebuilts_download.sh + ``` + +## How to Develop + +1. Create the build products **ark_js_vm** and **ts2panda**. + ```shell + python ark.py x64.release + ``` + - **ark_js_vm**: executable program for running .abc files. + - **ts2panda**: tool that converts ArkTS files into ArkCompiler bytecode files. + +2. Use **ts2panda** to convert a TypeScript file to an .abc file. + ```shell + prebuilts/build-tools/common/nodejs/node-v12.18.4-linux-x64/bin/node --expose-gc out/x64.release/clang_x64/obj/arkcompiler/ets_frontend/ts2panda/build/src/index.js helloworld.ts --opt-level 0 + ``` + Code snippet of the TypeScript case file **helloworld.ts**: + ```JavaScript + declare function print(arg:any):string; + print("Hello world!"); + ``` + +3. Run the generated .abc file. + ```shell + out/x64.release/clang_x64/arkcompiler/ets_runtime/ark_js_vm helloworld.abc + ``` + .abc file: ArkCompiler bytecode file. + + The execution output is as follows: + ``` + Hello world! + ``` + +## Running the Test262 Test Suite +``` +python ark.py x64.release test262 +``` + +## Build Options + +Select a build mode, for example, mode for building a debug version on an x64 platform. +``` +python ark.py x64.debug +``` +Obtain help information. +``` +python ark.py --help +``` diff --git a/en/device-dev/subsystems/subsys-boot-init-log.md b/en/device-dev/subsystems/subsys-boot-init-log.md new file mode 100644 index 0000000000000000000000000000000000000000..db7500c6d0442d054fecac560fececac1bb11598 --- /dev/null +++ b/en/device-dev/subsystems/subsys-boot-init-log.md @@ -0,0 +1,75 @@ +# init Run Log Standardization +## Overview +### Introduction +Basic functions of a log include recording key nodes and troubleshooting faults during the init startup process. +- Logs help troubleshoot faults, check the startup duration of each subsystem, command execution duration, and more. + +- Log tags (including **param**, **uevent**, and **module**) for different modules can be viewed. + +- Logs can be printed for key phases, for example, the startup of the first phase, a required partition and its device node, **uevent** creation, service startup, and more. + +- Logs can be printed according to the specified log level. Currently, the following log levels are available: **INIT_DEBUG**, **INIT_INFO**, **INIT_WARN**, **INIT_ERROR**, and **INIT_FATAL**. + +### Basic Concepts + +The implementation of init logs varies according to the OpenHarmony version. +- OpenHarmony standard: The init log is implemented via **dmesg log** of the Linux kernel. +- OpenHarmony LiteOS L1: The init log is implemented via **hilog**. +- OpenHarmony LiteOS L0: The init log is implemented via **printf**. + +### Constraints +None + +## How to Develop +### Use Cases +init logs are mainly used to start modules (like **param**, **ueventd**, and **module**) during init startup, and are used in the open API **begetutils**. + +### Available APIs + +**Table 1** Log APIs +| API | Format and Example | Description | +| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| INIT_LOGV | INIT_LOGV("Add %s to job %s", service->name, jobName); | Prints the log of the DEBUG level. | +| INIT_LOGI | INIT_LOGI("Start init first stage."); | Prints the log of the INFO level. | +| INIT_LOGW | INIT_LOGW("initialize signal handler failed"); | Prints the log of the WARN level. | +| INIT_LOGE | INIT_LOGE("Failed to format other opt"); | Prints the log of the ERROR level. | +| INIT_LOGF | INIT_LOGF("Failed to init system"); | Prints the log of the FATAL level. | +| INIT_ERROR_CHECK | INIT_ERROR_CHECK(ctx != NULL, return NULL, "Failed to get cmd args "); | Prints a log and executes **return NULL** when **ctx != NULL** is not true.| +| INIT_INFO_CHECK | INIT_INFO_CHECK(sockopt != NULL, return SERVICE_FAILURE, "Failed to malloc for service %s", service->name); | Prints a log and executes **return SERVICE_FAILURE** when **sockopt != NULL** is not true.| +| INIT_WARNING_CHECK | INIT_WARNING_CHECK(argsCount <= SPACES_CNT_IN_CMD_MAX, argsCount = SPACES_CNT_IN_CMD_MAX, "Too much arguments for command, max number is %d", SPACES_CNT_IN_CMD_MAX); | Prints a log and executes **argsCount = SPACES_CNT_IN_CMD_MAX** when **argsCount <= SPACES_CNT_IN_CMD_MAX** is not true.| +| INIT_CHECK | INIT_CHECK(arg != NULL, return NULL); | Executes **return NULL** when **arg != NULL** is not true. | +| INIT_CHECK_RETURN_VALUE | INIT_CHECK_RETURN_VALUE(errno == 0, -1); | Executes **return -1** when **errno == 0** is not true. | +| INIT_CHECK_ONLY_RETURN | INIT_CHECK_ONLY_RETURN(cmd != NULL); | Executes **return** when **cmd != NULL** is not true. | +| INIT_CHECK_ONLY_ELOG | INIT_CHECK_ONLY_ELOG(execv(argv[0], argv) == 0, "execv %s failed! err %d.", argv[0], errno); | Prints only **log "execv %s failed! err %d."** when **execv(argv[0], argv) == 0** is not true.| + +### Development Example + + 1. Call an API to print the log. + + For example, call **INIT_LOGI("Start init first stage.")** in **//base/startup/init/services/init/standard/init.c** to print the log. + ```c + void SystemPrepare(void) + { + MountBasicFs(); + CreateDeviceNode(); + LogInit(); + // Make sure init logs are always printed to /dev/kmsg. + EnableDevKmsg(); + INIT_LOGI("Start init first stage."); + // Only the OpenHarmony standard system supports + // two stages of init. + // In updater mode, only one stage of init is supported. + if (InUpdaterMode() == 0) { + StartInitSecondStage(); + } + } + ``` + Run **dmesg** to check the printed log **Start init first stage.** + + 2. Set the log level by using the begetctl command. + + The command is **begetctl setloglevel level**, where **level** indicates one of the following log levels: **0** (**INIT_DEBUG**), **1** (**INIT_INFO**), **2** (**INIT_WARN**), **3** (**INIT_ERROR**), and **4** (**INIT_FATAL**). + + After the setting is complete, the level set via **g_logLevel** of init takes effect immediately, and log APIs can print logs only when their log levels are higher than or equal to this level. For example, **begetctl setloglevel 3** sets the log level to **INIT_ERROR**. In this case, only **INIT_LOGE** and **INIT_LOGF** will print logs. + + After the system is restarted, the configured log level will take effect after the **load_persist_params** command in the **init.cfg** file is executed. diff --git a/en/device-dev/subsystems/subsys-boot-init-sandbox.md b/en/device-dev/subsystems/subsys-boot-init-sandbox.md index dc195b4926be83351293e519a42e4157e84fad22..6580b9a99475109289ccaea62593c098a2b47022 100644 --- a/en/device-dev/subsystems/subsys-boot-init-sandbox.md +++ b/en/device-dev/subsystems/subsys-boot-init-sandbox.md @@ -24,6 +24,7 @@ The sandbox management module is available only for the standard system. | src-path | Source path of the directory or file to mount.| | sandbox-path | Target path in the sandbox.| | sandbox-flags | Mount flag. The default value is **bind rec**.| + | ignore | Whether to ignore a mounting failure. If the value is set to 1, the system ignores the mounting failure and proceeds with the subsequent step. | | target-name | Directory to link.| | link-name | Target link in the sandbox.| @@ -44,13 +45,13 @@ Logical storage structure of the sandbox: bool InitSandboxWithName(const char *name); // Parsing to the JSON structure typedef struct { - mountlist_t *mounts; // Directory to mount - mountlist_t *fileMounts; // File to mount - linklist_t *links; // Directory to link - char *rootPath; // Root path of the sandbox: /mnt/sandbox/system|vendor|xxx - char name[MAX_BUFFER_LEN]; // Sandbox name, for example, system sandbox or chipset sandbox - bool isCreated; // Sandbox creation flag - int ns; // namespace + ListNode pathMountsHead; // sandbox mount_path list head + ListNode fileMountsHead; // sandbox mount_file list head + ListNode linksHead; // sandbox symbolic link list head + char *rootPath; // /mnt/sandbox/system|vendor|xxx + char name[MAX_BUFFER_LEN]; // name of sandbox. i.e system, chipset etc. + bool isCreated; // sandbox already created or not + int ns; // namespace // namespace } sandbox_t; ``` ### How to Develop diff --git a/en/device-dev/subsystems/subsys-boot-init-service.md b/en/device-dev/subsystems/subsys-boot-init-service.md index 5b6954c4d595e03028365866b12ee81be22afeb7..1317afb01df7c41b24ca6e9012ce4fa2ae0bca54 100644 --- a/en/device-dev/subsystems/subsys-boot-init-service.md +++ b/en/device-dev/subsystems/subsys-boot-init-service.md @@ -89,6 +89,19 @@ Service configuration allows you to configure services on demand to create diffe "secon" : "u:r:distributedsche:s0" // SELinux tag setting for service processes. In this example, u:r:distributedsche:s0 is the SELinux tag. } ``` + +- SELinux tag + + To enable the SELinux policy for a service, add the SELinux tag to the service in the **secon** field. For example, to add the SELinux tag to watchdog_service, use the following code snippet: + + ``` + "services" : [{ + "name" : "watchdog_service", + "secon" : "u:r:watchdog_service:s0" + }] + ``` + Note that you need to define the tag in SELinux. For details, see the appropriate SELinux guide. + - init service FD proxy (for standard system or higher) FD proxy is an extended mechanism for on-demand startup. It can ensure that the FD state handle is not lost before the service process exits. Specifically, a service process sends the FD to the init process before it exits, and then reclaims the FD from the init process when it is started again. @@ -150,7 +163,7 @@ By parsing the *.cfg file, you can obtain **service** fields, a | API | Function| Description | | ---------- | ---------- |--------| | int *ServiceGetFd(const char *serviceName, size_t *outfdCount) | Obtains the proxy FD from the init process.| Return value: Returns the pointer to the fd array if the operation is successful; returns **NULL** otherwise. (Note: Manual release is required.)
Arguments:
**serviceName**: service name.
**outfdCount**: length of the returned FD array.| - | int ServiceSaveFd(const char *serviceName, int *fds, int fdCount) | Requests the init process for FD proxy.| Return value: Returns **0** if the operation is successful; returns **-1** otherwise.
Arguments:
**serviceName**: service name.
**fds**: pointer to the FD array for FD proxy.
**fdCount**: length of the FD array + | int ServiceSaveFd(const char *serviceName, int *fds, int fdCount) | Requests the init process for FD proxy.| Return value: Returns **0** if the operation is successful; returns **-1** otherwise.
Arguments:
**serviceName**: service name.
**fds**: pointer to the FD array for FD proxy.
**fdCount**: length of the FD array. | int ServiceSaveFdWithPoll(const char *serviceName, int *fds, int fdCount) | Requests FD proxy in poll mode.| Return value: Returns **0** if the operation is successful; returns **-1** otherwise.
Arguments:
**serviceName**: service name.
**fds**: pointer to the FD array.
**fdCount**: length of the FD array. **Table 4** Service control APIs diff --git a/en/device-dev/subsystems/subsys-boot-init-sub-unit.md b/en/device-dev/subsystems/subsys-boot-init-sub-unit.md new file mode 100644 index 0000000000000000000000000000000000000000..2e1407b9fd8dc6a29e74b91b4c1bc414a7eaae73 --- /dev/null +++ b/en/device-dev/subsystems/subsys-boot-init-sub-unit.md @@ -0,0 +1,121 @@ +# Component-based Startup +## Overview +### Function Introduction +This subsystem provides the following functions: +- Builds four basic component images and provides component-based directories, including: + - System component: **system** + - Common product configuration component: **sys_prod** + - Chipset component: **chipset** + - Hardware configuration component: **chip_prod** +- Ensures that system parameters and startup scripts can be scanned and initialized by component priority. +- Independently compiles and builds the **system** component and **chipset** component. + +### Basic Concepts +- Basic Components + + **system**: serves as the file system mount point, and functions as a platform service irrelevant to the chipset and hardware. + **sys_prod**: extends and customizes capabilities of the **system** component, bears product-level differentiated capabilities, and stores product-related configuration files. + **chipset**: serves as the file system mount point, and offers unified hardware abstraction services for the **system** component. Same chipset platforms can use the same **chipset** component. + **chip_prod**: offers specific hardware capabilities of a peripheral board and product-level differentiated hardware configurations, as well as chipset-related configuration files. + +- Component-based compilation and building + + Use **target_cpu** to specify the instruction set of the **system** component. Use **inherit** to inherit the common component set such as **base**, **headless**, and **rich**. Use **subsystems** to define more components for a specific product type. + +- System parameters and startup scripts scanned and initialized by component priority + + System parameters and startup scripts include the CFG configuration file, PARAM file, sandbox JSON configuration file, and module plugin library file of a service. The following are related files that are in ascending order in terms of priority: **/system**, **/chipset**, **/sys_prod**, and **/chip_prod**. A file with a higher priority replaces and updates the configuration file with a lower priority. + + +### Constraints +Standard and small systems support component-based compilation and building. System parameters and startup scripts are scanned and initialized by component priority. + +## How to Develop +### When to Use +Component-based startup enables vendors and hardware platforms to quickly develop products through modular combination. The following uses RK3568 as an example to illustrate component-based startup in detail. + +### Building and Compiling RK3568 Based on Components +The **//vendor/hihope/rk3568/config.json** configuration file implements components required for building the product: + + { + "product_name": "rk3568", + "device_company": "rockchip", + ... + "target_cpu": "arm", + ... + "inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ], + "subsystems": [ + { + "subsystem": "security", + "components": [ + { + "component": "selinux", + "features": [] + } + ] + } + ... + } + +The example above indicates the product name, chipset vendor, supported instruction set, and more. **inherit** indicates the dependent common components, and **subsystems** indicates other components. +The following illustrates the configuration of the **system** component in the **//productdefine/common/inherit/rich.json** file. The **system** component can also include **base.json** (list of minimal components that all products must contain) and **headless.json** (list of minimal components with which products having no UI allow for application installation and management). + + { + "version": "3.0", + "subsystems": [ + { + "subsystem": "arkui", + "components": [ + { + "component": "ace_engine", + "features": [] + }, + { + "component": "napi", + "features": [] + } + ] + }, + { + "subsystem": "account", + "components": [ + { + "component": "os_account", + "features": [] + } + ] + }, + ... + } + +### Scanning and Initializing System Parameters by Priority +The following are CFG files of a service, which are in ascending order in terms of priority: **/system/etc**, **/system/etc/init**, and **/chipset/etc**. A file with a higher priority replaces and updates the configuration file with a lower priority. The following uses **/system/etc/init/camera_service.cfg** as an example: + + { + "services" : [{ + "name" : "camera_service", + "path" : ["/system/bin/sa_main", "/system/profile/camera_service.xml"], + "uid" : "cameraserver", + "gid" : ["system", "shell"], + "secon" : "u:r:camera_service:s0", + "permission" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS"], + "permission_acls" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS"] + }] + } + +**/chipset/etc/camera_B_service.cfg** exists at the same time. + + { + "services" : [{ + "name" : "camera_service", + "path" : ["/system/bin/sa_main", "/system/profile/camera_B_service.xml"], + "uid" : "cameraserver", + "gid" : ["system", "shell"], + "secon" : "u:r:camera_service:s0", + "permission" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS"], + "permission_acls" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS"], + "disabled" : 1 + }] + } + +Based on the priority requirement, the **path** attribute of the **camera_service** service will be replaced by **["/system/bin/sa_main", "/system/profile/camera_B_service.xml"]** that has a higher priority, and the **disabled** attribute is added. diff --git a/en/device-dev/subsystems/subsys-build-all.md b/en/device-dev/subsystems/subsys-build-all.md index 16adb9777ce25268d76411ca9e15b6bf3cf7429c..229277ec3175717acf903d42765b1c526f3f6abe 100644 --- a/en/device-dev/subsystems/subsys-build-all.md +++ b/en/device-dev/subsystems/subsys-build-all.md @@ -122,7 +122,7 @@ You must install the software packages required for build. The command is as fol # export PATH=~/.local/bin:$PATH ``` - ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
The hb tool will be installed during the installation of ohos-build. If the hb installation fails, see [Installing hb](../../device-dev/quick-start/quickstart-pkg-install-tool.md). + ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
The hb tool will be installed during the installation of **ohos-build**. If hb tool fails to be installed, [install hb](../../device-dev/quick-start/quickstart-pkg-install-tool.md#hb-installation) again. ## Configuration Rules @@ -143,28 +143,28 @@ To ensure that chipset and product solutions are decoupled from OpenHarmony, you ```shell -/build # Directory for build +/build # Directory for build ├── __pycache__ -├── build_scripts/ # Python scripts for build +├── build_scripts/ # Python scripts for build ├── common/ -├── config/ # Build-related configuration +├── config/ # Build-related configuration ├── core -│ └── gn/ # BUILD.gn configuration +│ └── gn/ # BUILD.gn configuration └── build_scripts/ ├── docs gn_helpers.py* -lite/ # hb and preloader entry +lite/ # hb and preloader entry misc/ -├── ohos # Configuration of OpenHarmony building and packaging -│ ├── kits # Kits build and packaging templates and processing -│ ├── ndk # NDK templates and processing -│ ├── notice # Notice templates and processing -│ ├── packages # Distribution packaging templates and processing -│ ├── sa_profile # SA profiles and processing -│ ├── sdk # SDK templates and processing, which contains the module configuration in the SDK -│ └── testfwk # Testing-related processing -├── ohos.gni* # Common .gni files (facilitating one-time import of each module) +├── ohos # Configuration of OpenHarmony build and packaging +│ ├── kits # Kits build and packaging templates and processing +│ ├── ndk # NDK templates and processing +│ ├── notice # Notice templates and processing +│ ├── packages # Distribution packaging templates and processing +│ ├── sa_profile # SA profiles and processing +│ ├── sdk # SDK templates and processing, which contains the module configuration in the SDK +│ └── testfwk # Testing-related processing +├── ohos.gni* # Common .gni files (facilitating one-time import of each module) ├── ohos_system.prop ├── ohos_var.gni* ├── prebuilts_download.sh* @@ -195,179 +195,171 @@ Then, run the build commands in command line (CLI) mode or hb mode. 1. Using the CLI - - Run the following command in the root directory of the source code to build a full distribution: - - Release version: - - ```shell - ./build.sh --product-name {product_name} - ``` - - Debug version: - - ```shell - ./build.sh --product-name {product_name} --gn-args is_debug=true - ``` - - >![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
- >Due to the limitation of the image size, the full build for the debug version may fail to be burnt. You are advised to build the binary file for each module separately. Run the following command to build a module separately: - > - >``` - >./build.sh --product-name {product_name} --gn-args is_debug=true --build-target {target_name} - >``` - > - > - - **{product_name}** specifies the product platform supported by the current distribution, for example, **hispark_taurus_standard**. - - The image generated is stored in the **out/{device_name}/packages/phone/images/** directory. - - - The **./build.sh** command supports the following options: - - ```shell - -h, --help # Display help information and exit. - --source-root-dir=SOURCE_ROOT_DIR # Specify the path. - --product-name=PRODUCT_NAME # Specify the product name. - --device-name=DEVICE_NAME # Specify the device name. - --target-cpu=TARGET_CPU # Specify the CPU. - --target-os=TARGET_OS # Specify the operating system. - -T BUILD_TARGET, --build-target=BUILD_TARGET # Specify one or more targets to build. - --gn-args=GN_ARGS # Specify GN parameters. - --ninja-args=NINJA_ARGS # Specify Ninja parameters. - -v, --verbose # Display all commands used. - --keep-ninja-going # Keep Ninja going until 1,000,000 jobs fail. - --jobs=JOBS - --export-para=EXPORT_PARA - --build-only-gn # Perform GN parsing and do not run Ninja. - --ccache # (Optional) Use ccache for build. You need to install ccache locally. - --fast-rebuild # Specify whether to allow fast rebuild. The default value is False. - --log-level=LOG_LEVEL # Specify the log level used in the build process. The options are debug, info, and error. The default value is info. - --device-type=DEVICE_TYPE # Specify the device type. The default value is default. - --build-variant=BUILD_VARIANT #Specify the device operation mode. The default value is user. - ``` - - - - - 2. Using the hb tool - - **hb** is an OpenHarmony command line tool for executing build commands. Common hb commands are described as follows: - - **hb set** - - Sets the product to build. - - ```shell - hb set -h - usage: hb set [-h] [-root [ROOT_PATH]] [-p] - - optional arguments: - -h, --help show this help message and exit - -root [ROOT_PATH], --root_path [ROOT_PATH] - Set OHOS root path - -p, --product Set OHOS board and kernel - ``` - - - If you run **hb set** with no argument, the default setting process starts. - - You can run **hb set -root** *dir* to set the root directory of the source code. - - - You can run **hb set -p** to set the product to build. - - - - **hb env** - - Displays current settings. - - ```shell - hb env - [OHOS INFO] root path: xxx - [OHOS INFO] board: hispark_taurus - [OHOS INFO] kernel: liteos - [OHOS INFO] product: ipcamera - [OHOS INFO] product path: xxx/vendor/hisilicon/ipcamera - [OHOS INFO] device path: xxx/device/hisilicon/hispark_taurus/sdk_linux_4.19 - ``` - - - - **hb build** - - Builds a product, component, module, or chipset solution. - - ```shell - hb build -h - usage: hb build [-h] [-b BUILD_TYPE] [-c COMPILER] [-t [TEST [TEST ...]]] [-cpu TARGET_CPU] [--dmverity] [--tee] - [-p PRODUCT] [-f] [-n] [-T [TARGET [TARGET ...]]] [-v] [-shs] [--patch] [--compact-mode] - [--gn-args GN_ARGS] [--keep-ninja-going] [--build-only-gn] [--log-level LOG_LEVEL] [--fast-rebuild] - [--device-type DEVICE_TYPE] [--build-variant BUILD_VARIANT] - [component [component ...]] - - positional arguments: - component name of the component, mini/small only - - optional arguments: - -h, --help show this help message and exit - -b BUILD_TYPE, --build_type BUILD_TYPE - release or debug version, mini/small only - -c COMPILER, --compiler COMPILER - specify compiler, mini/small only - -t [TEST [TEST ...]], --test [TEST [TEST ...]] - compile test suit - -cpu TARGET_CPU, --target-cpu TARGET_CPU - select cpu - --dmverity enable dmverity - --tee Enable tee - -p PRODUCT, --product PRODUCT - build a specified product with {product_name}@{company} - -f, --full full code compilation - -n, --ndk compile ndk - -T [TARGET [TARGET ...]], --target [TARGET [TARGET ...]] - compile single target - -v, --verbose show all command lines while building - -shs, --sign_haps_by_server - sign haps by server - --patch apply product patch before compiling - --compact-mode compatible with standard build system set to false if we use build.sh as build entrance - --gn-args GN_ARGS specifies gn build arguments, eg: --gn-args="foo="bar" enable=true blah=7" - --keep-ninja-going keeps ninja going until 1000000 jobs fail - --build-only-gn only do gn parse, do not run ninja - --log-level LOG_LEVEL - specifies the log level during compilationyou can select three levels: debug, info and error - --fast-rebuild it will skip prepare, preloader, gn_gen steps so we can enable it only when there is no change - for gn related script - --device-type DEVICE_TYPE - specifies device type - --build-variant BUILD_VARIANT - specifies device operating mode - ``` - - - If you run **hb build** with no argument, the previously configured code directory, product, and options are used for the build. The **-f** option deletes all products to be built. It is equivalent to running **hb clean** and **hb build**. - - You can run **hb build** *{component_name}* to build product components separately based on the development board and kernel set for the product, for example, **hb build kv_store**. - - You can run **hb build -p ipcamera@hisilicon** to skip the setting step and build the product directly. - - - You can run **hb build** in **device/board/device_company** to select the kernel and build an image that contains the kernel and drivers only based on the current development board and the selected kernel. - - - - **hb clean** - - Deletes all the files except **args.gn** and **build.log** in the **out** directory (default). To clear files in a specified directory, add the directory parameter to the command, for example, **hb clean out/board/product**. - - ```shell - hb clean - usage: hb clean [-h] [out_path] - - positional arguments: - out_path clean a specified path. - - optional arguments: - -h, --help show this help message and exit - ``` - -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> - For details about how to set up the build environment and perform the build, see the related topics in [Getting Started](../quick-start/Readme-EN.md). -> - OpenHarmony also provides the Docker environment, which spares the installation of the build tool. For details, see [Docker Environment](../get-code/gettools-acquire.md). +- Run the following command in the root directory of the source code to build a full distribution: + + Release version: + ```shell + ./build.sh --product-name {product_name} + ``` + Debug version: + ```shell + ./build.sh --product-name {product_name} --gn-args is_debug=true + ``` + > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+ > Due to the limitation of the image size, the full build for the debug version may fail to be burnt. You are advised to build the binary file for each module separately. Run the following command to build a module separately: + > + > + > ``` + > ./build.sh --product-name {product_name} --gn-args is_debug=true --build-target {target_name} + > ``` + > + **{product_name}** specifies the product platform supported by the current distribution, for example, **hispark_taurus_standard**. + + The image generated is stored in the **out/{device_name}/packages/phone/images/** directory. + + + +- The **./build.sh** command supports the following options: + + ```shell + -h, --help # Display help information and exit. + --source-root-dir=SOURCE_ROOT_DIR # Specify the path. + --product-name=PRODUCT_NAME # Specify the product name. + --device-name=DEVICE_NAME # Specify the device name. + --target-cpu=TARGET_CPU # Specify the CPU. + --target-os=TARGET_OS # Specify the operating system. + -T BUILD_TARGET, --build-target=BUILD_TARGET # Specify one or more targets to build. + --gn-args=GN_ARGS # Specify GN parameters. + --ninja-args=NINJA_ARGS # Specify Ninja parameters. + -v, --verbose # Display all commands used. + --keep-ninja-going # Keep Ninja going until 1,000,000 jobs fail. + --jobs=JOBS + --export-para=EXPORT_PARA + --build-only-gn # Perform GN parsing and do not run Ninja. + --ccache # (Optional) Use ccache for build. You need to install ccache locally. + --fast-rebuild # Specify whether to allow fast rebuild. The default value is False. + --log-level=LOG_LEVEL # Specify the log level used in the build process. The options are debug, info, and error. The default value is info. + --device-type=DEVICE_TYPE # Specify the device type. The default value is default. + --build-variant=BUILD_VARIANT # Specify the device operation mode. The default value is user. + ``` + +2. Using the hb tool + +**hb** is an OpenHarmony command line tool for executing build commands. Common hb commands are described as follows: + +**hb set** + +Sets the product to build. + +```shell +hb set -h +usage: hb set [-h] [-root [ROOT_PATH]] [-p] + +optional arguments: + -h, --help show this help message and exit + -root [ROOT_PATH], --root_path [ROOT_PATH] + Set OHOS root path + -p, --product Set OHOS board and kernel +``` + +- If you run **hb set** with no argument, the default setting process starts. + +- You can run **hb set -root** *dir* to set the root directory of the source code. + +- You can run **hb set -p** to set the product to build. + +**hb env** + +Displays current settings. + +```shell +hb env +[OHOS INFO] root path: xxx +[OHOS INFO] board: hispark_taurus +[OHOS INFO] kernel: liteos +[OHOS INFO] product: ipcamera +[OHOS INFO] product path: xxx/vendor/hisilicon/ipcamera +[OHOS INFO] device path: xxx/device/hisilicon/hispark_taurus/sdk_linux_4.19 +``` + +**hb build** + +Builds a product, component, module, or chipset solution. + +```shell +hb build -h +usage: hb build [-h] [-b BUILD_TYPE] [-c COMPILER] [-t [TEST [TEST ...]]] [-cpu TARGET_CPU] [--dmverity] [--tee] + [-p PRODUCT] [-f] [-n] [-T [TARGET [TARGET ...]]] [-v] [-shs] [--patch] [--compact-mode] + [--gn-args GN_ARGS] [--keep-ninja-going] [--build-only-gn] [--log-level LOG_LEVEL] [--fast-rebuild] + [--device-type DEVICE_TYPE] [--build-variant BUILD_VARIANT] + [component [component ...]] + +positional arguments: + component name of the component, mini/small only + +optional arguments: + -h, --help show this help message and exit + -b BUILD_TYPE, --build_type BUILD_TYPE + release or debug version, mini/small only + -c COMPILER, --compiler COMPILER + specify compiler, mini/small only + -t [TEST [TEST ...]], --test [TEST [TEST ...]] + compile test suit + -cpu TARGET_CPU, --target-cpu TARGET_CPU + select cpu + --dmverity enable dmverity + --tee Enable tee + -p PRODUCT, --product PRODUCT + build a specified product with {product_name}@{company} + -f, --full full code compilation + -n, --ndk compile ndk + -T [TARGET [TARGET ...]], --target [TARGET [TARGET ...]] + compile single target + -v, --verbose show all command lines while building + -shs, --sign_haps_by_server + sign haps by server + --patch apply product patch before compiling + --compact-mode compatible with standard build system set to false if we use build.sh as build entrance + --gn-args GN_ARGS specifies gn build arguments, eg: --gn-args="foo="bar" enable=true blah=7" + --keep-ninja-going keeps ninja going until 1000000 jobs fail + --build-only-gn only do gn parse, do not run ninja + --log-level LOG_LEVEL + specifies the log level during compilationyou can select three levels: debug, info and error + --fast-rebuild it will skip prepare, preloader, gn_gen steps so we can enable it only when there is no change + for gn related script + --device-type DEVICE_TYPE + specifies device type + --build-variant BUILD_VARIANT + specifies device operating mode +``` + +- If you run **hb build** with no argument, the previously configured code directory, product, and options are used for the build. The **-f** option deletes all products to be built. It is equivalent to running **hb clean** and **hb build**. + +- You can run **hb build** *{component_name}* to build product components separately based on the development board and kernel set for the product, for example, **hb build kv_store**. + +- You can run **hb build -p ipcamera@hisilicon** to skip the setting step and build the product directly. + +- You can run **hb build** in **device/board/device_company** to select the kernel and build an image that contains the kernel and drivers only based on the current development board and the selected kernel. + +**hb clean** + +Deletes all the files except **args.gn** and **build.log** in the **out** directory (default). To clear files in a specified directory, add the directory parameter to the command, for example, **hb clean out/board/product**. + +```shell +hb clean +usage: hb clean [-h] [out_path] + +positional arguments: + out_path clean a specified path. + +optional arguments: + -h, --help show this help message and exit +``` + +> **NOTE** +> +> - For details about how to set up the build environment and perform the build, see the related topics in [Getting Started](../quick-start/quickstart-overview.md) +> - OpenHarmony also provides the Docker environment, which spares the installation of the build tool. For details, see [Docker Environment](../get-code/gettools-acquire.md) ### Building Procedures @@ -389,3 +381,4 @@ You can add and build a product, component, chipset solution, and module. For de - [Information Collected by the Open Source Software Notice](subsys-build-reference.md#information-collected-by-the-open-source-software-notice) - [Parameters for Accelerating Local Build](subsys-build-reference.md#parameters-for-accelerating-local-build) - [Viewing Ninja Build Information](subsys-build-reference.md#viewing-ninja-build-information) + diff --git a/en/device-dev/subsystems/subsys-build-component-building-rules.md b/en/device-dev/subsystems/subsys-build-component-building-rules.md new file mode 100644 index 0000000000000000000000000000000000000000..be4627c78d1f180f9ed373f61069a85149277a10 --- /dev/null +++ b/en/device-dev/subsystems/subsys-build-component-building-rules.md @@ -0,0 +1,282 @@ +# Component Building Specifications + +## About This Document + +### Purpose + +Compilation and building is an important part for implementing the component-based design. A component in the compiled state should be maintainable, portable, and loosely coupled. This document provides guidelines for writing build scripts so that the components to build are configurable, reusable, and tailorable, with reasonable dependencies. + +### Basic Concepts + +**Component** + +A component is a basic unit that constitutes OpenHarmony system capabilities. It contains the minimum required source code and has independent repositories and directories. It can be instantiated into different libraries or binary files on different devices. + +**Feature** + +Component features are configurable options in the build of a product. A component can be configured with different features to adapt to different product form factors. The configuration of component features affects only the implementations of component internal functions. It does not affect the public APIs (APIs provided by the component for applications) or inner APIs (APIs between components). + +**Dependency** + +A component in the compiled state has the following dependencies: + +- Conditional dependency: The dependency can be tailored in specific scenarios, and the removal of the dependency does not affect the public or inner APIs of the component. For example, audio has conditional dependency on Bluetooth. +- Strong dependency: The dependency between components cannot be tailored. For example, the syscap component has strong dependency on the security library functions. + +### General Principles + +The compilation and building of components must comply with the following: + +**Independent and Autonomous** + +The component in the compiled state must be highly cohesive. Exercise caution when adding external dependencies, and minimize static dependencies in build. + +**Reasonable Dependency** + +Set up inter-component dependency based on the interfaces between components. Avoid the dependency on the modules or header files of other components. + +**Product Independent** + +The component in the compiled state should be applicable to multiple products. Do not use product names in build scripts. + +### Conventions + +**Rule**: a convention that must be complied with + +**Suggestion**: a convention that must be taken into consideration + +### Guarding Method + +The gated check-in conducts the following checks on the build configuration files: + +Prebuild check: The files are checked in the prebuild phase. If an error is detected, the build stops. + +Static check: The check tool scans and checks configuration files for errors. + +### Exception + +An exception is allowed only with sufficient reasons as long as the general principles are not violated. + +However, exceptions compromise code consistency and should be avoided as far as possible. Exceptions to the "rules" should be rare. + +## Naming + +The names of variables and build targets in build scripts, templates, gni files, and objects and data in component description files must be in the kernel style (unix_like). That is, the words are in lowercase and separated by underscores (_). For example, **startup_init**. + +### Rule 1.1 Use consistent format for component names. +- The component name must reflect the functions of the component. +- The component name must be globally unique in the system. +- The component name cannot exceed 63 valid characters. +- The component name consists of lowercase letters and underscores (_), for example, **unix_like**. + +> **Exception**: Third-party open-source software can use the native naming format, for example, **cJson**. + +### Rule 1.2 Name features in the *component name*_*feature name* format. + +A feature is a configurable option declared by a component. Adding a component name prefix can prevent duplicate feature names. + +Example: + +```py +declare_args() { + dsoftbus_conn_p2p = true # dsoftbus is the component name, and conn_p2p is the feature name. + dsoftbus_conn_ble = false # dsoftbus is the component name, and conn_ble is the feature name. +} +``` + +Guarding method: prebuild check + +### Suggestion 1.1: Name build targets in the *component name*_*module name* format. + +A component may have multiple build targets (modules). The component and module names help you quickly locate the component based on the output (library or executable file) and prevents duplicate names. + +Example: +```py +ohos_shared_library("data") # Inaccurate, too general, and easy to duplicate +``` +Example: +```py +ohos_shared_library("cellular_data_napi") # Good example +``` + +## Description File + +The **bundle.json** file defines a component. It contains information, such as the root directory, name, function description, version number, interface definition, and build entry of a component. The information in **bundle.json** file must be correct. + +### Rule 2.1 Ensure correct component information in the file. + +| Field| Type| Mandatory| Description| Guarding Method| +|---|---|---|---|---| +|name|String|Yes|OpenHarmony package manager (HPM) package name of the component.
Naming rule: @{organization}/{component_name}
{component_name} must comply with **Rule 1.1**.|Static check| +|version|String|Yes|Component version number, which must be consistent with that of OpenHarmony.|Static check| +|destPath|String|Yes|Root directory of the component source code.
The root directory of each component must be unique.|Static check| +|component:name|String|Yes|Component name, which must comply with **Rule 1.1**.|Static check| +|component:subsystem|String|Yes|Subsystem to which the component belongs. The subsystem name consists of lowercase letters and cannot contain underscores (_).|Static check| +|component:syscap|String list|No|System capabilities.
Naming rule: **SystemCapability**.*Subsystem*.*Component capability*.*Sub-capability* (optional) in upper camel case
Example: **SystemCapability.Media.Camera,SystemCapability.Media.Camera.Front**|Static check| +|component:features|String list|No|Features that can be configured for the component. The feature names must comply with **Rule 1.2**.|Static check| +|component:adapted_system_type|String list|Yes|Type of the system to which the component applies. The value can be **mini**, **small**, **standard**,or their combination.|Static check| +|component:rom|String|Yes|ROM size, in KB.|Static check| +|component:ram|String|Yes|RAM size, in KB.|Static check| +|component:deps|String list|Yes|Dependencies of the component, including other components and third-party open-source software. The dependencies must be the same as that in the component build script.|Prebuild check| + + +### Suggestion 2.1 Place the component description file in the root directory of the component. + +The component directory is independent. Place the **bundle.json** file in the root directory of the component. + +## Variables + +The built-in variable values of the build target determine the content to compile, dependencies, and data to package. + +### Rule 3.1 Do not include the absolute or relative paths of other components in the build script of a component. + +Use **external_deps** to include the dependency between components. The variables **sources**, **include_dirs**, **configs**, **public_configs**, **deps**, and **public_deps** of the component build target cannot reference the relative or absolute paths of other components. + +- sources + + **Sources** can contain only the source code of a component. If **sources** contain the source code of other components, the independence of the component's source code directories will be violated. + +- include_dirs + + **include_dirs** can contain only the header file paths of a component. The dependencies on other components are automatically imported through **external_deps**. + +- configs + + **configs** can reference only the configuration paths of a component. Referencing the **configs** of other components may cause interface dependency. + +- pulic_configs + + **public_configs** can reference only the configuration paths of a component. Referencing the **configs** of other components may cause interface dependency. + +- deps + + **deps** can contain only dependencies between the modules in a component. Referencing a module in another component may cause dependency on the module and interfaces of the referenced component. + + Example: + + base/foos/foo_a/BUILD.gn + + ```py + deps = [ "//base/foo/foo_b:b" ] # Not recommended. deps contains the absolute path of another component. + deps = [ "../../foo_b:b" ] # Not recommended. deps contains the relative path of another component. + deps = [ "a" ] # Recommended. + ``` + + > **Exception**: References to third-party open-source software are allowed. + +- public_deps + + **Public_deps** can contain only dependencies between the modules in a component. Referencing a module in another component may cause dependency on the module and interfaces of the referenced component. + + > **Exception**: References to third-party open-source software are allowed. + + +Guarding method: static check + +### Rule 3.2 The component name and subsystem name must be specified for the component build target. + +**part_name** and **subsystem_name** are mandatory for **ohos_shared_library**, **ohos_static_library**, **ohos_executable_library**, and **ohos_source_set** of a component. + +The following uses **ohos_shared_library** in **developtools/syscap_codec/BUILD.gn** as an example. + +```py +ohos_shared_library("syscap_interface_shared") { + include_dirs = [ + "include", + "src", + ] + public_configs = [ ":syscap_interface_public_config" ] + sources = [ + "./interfaces/inner_api/syscap_interface.c", + "./src/endian_internal.c", + "./src/syscap_tool.c", + ] + deps = [ + "//third_party/bounds_checking_function:libsec_static", + "//third_party/cJSON:cjson_static", + ] + + subsystem_name = "developtools" # subsystem_name is mandatory. + part_name = "syscap_codec" # part_name is mandatory. +} +``` + +Guarding method: static check + +### Suggestion 3.1 Use relative paths for internal references of a component. + +Using relative paths for references within a component can: + +- Make the script simpler. + +- Decouple the build script of a component from the root directory of the component. + +Example: + +base/foos/foo_a/BUILD.gn + +```py +include_dirs = [ + "./include", # Recommended. + "//base/foo/foo_a/include" # Not recommended. +] + +deps = [ + "sub_module:foo", # Recommended. + "base/foo/foo_a/sub_module:foo" # Not recommended. +] +``` + +Guarding method: static check + +### Suggestion 3.2 Make the internal modules of a component only visible to the component. + +Set **visibility** for the internal modules of a component to prevent them from being dependencies of other components. + +Example: + +base/foos/foo_a/BUILD.gn + +```py +ohos_shared_library("foo_a") { + visibility = [ "./*" ] # foo_a is visible only to base/foo/foo_a and its subdirectories. +} + +ohos_shared_library("foo_a") { + visibility = [ ":*" ] # foo_a is visible only to this BUILD.gn. +} +``` + +## Others + +### Rule 4.1 Do not use the product name variable in the component build script. + +A component is a common system capability and is irrelevant to a specific product. If the product name is used in the build script, the component functions are bound to the product, which violates the commonality of the component. Abstract the differences between components in different product forms as features or runtime plug-ins. + +> **Exception**: The build scripts of third-party components in the **vendor** and **device** directories are excluded. + +Guarding method: static check + +### Suggestion 4.1 Do not import .gni files in directories of other components. + +The **.gni** file declares the internal build variables and templates of the component. Importing the **.gni** file of another component means to use the internal variables and templates of another component, which introduces the dependency on that component. The variables, args, and templates that affect multiple components should be defined in the **.gni** file of the build framework. + +> **Exception**: The **.gni** file (containing global build options) of the build framework in the **build** directory can be imported by all components. + +Guarding method: static check + +### Suggestion 4.2 Use a unified template for component build units. + +Use **ohos_** templates, such as **ohos_shared_library**, **ohos_static_library**, **ohos_executable**, and **ohos_source_set**, for the build units of mini, small, and standard systems. + +Example: + +```py + executable ("foundation") {# The built-in template is not recommended. + ... + } + + ohos_executable("foundation") {# The ohos template is recommended. + ... + } +``` diff --git a/en/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md b/en/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md index 864fcc51756bf4c99a8c8be2f4578e98b88fee5d..1826640aa51bd09a66a8f72c5d5a0bacdbca76ea 100644 --- a/en/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md +++ b/en/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md @@ -9,7 +9,7 @@ | Ability | An abstraction of a functionality that an application can provide. It is the minimum unit for the system to schedule the application. An application can contain one or more **Ability** instances.| | FA | Feature Ability, an ability that provides a UI for user interaction in the ability framework of the FA model. The FA supports only the Page ability template.| | PA | Particle Ability, an ability that does not have a UI in the ability framework of the FA model. It provides services and support for FAs. For example, a PA can function as a background service to provide computing power or as a data store to provide data access capabilities. The PA supports three types of templates: Service, Data, and Form ability templates.| -| FA model | One of the two ability framework models. The FA model applies to application development using API version 8 and earlier versions. The FA model provides FAs and PAs. The FA supports the Page ability template, and the PA supports the Service, Data, and Form ability templates. For details, see [FA Model Overview](../../application-dev/ability/fa-brief.md).| +| FA model | One of the two ability framework models. The FA model applies to application development using API version 8 and earlier versions. The FA model provides FAs and PAs. The FA supports the Page ability template, and the PA supports the Service, Data, and Form ability templates. For details, see [FA Model Overview](../../application-dev/ability-deprecated/fa-brief.md).| | Stage model| One of the two ability framework models. The stage model applies to application development using API version 9 and later versions. The stage model provides abilities and Extension abilities. The latter ones are extended to Service Extension abilities, Form Extension abilities, Data Share Extension abilities, and more.| ### Function diff --git a/en/device-dev/subsystems/subsys-dfx-hilog-rich.md b/en/device-dev/subsystems/subsys-dfx-hilog-rich.md index 674b777668c0fb362d2a7ddcdcce2016f31f2e77..5c2cb29001194d2f7e70a2685f249684522fb7e3 100644 --- a/en/device-dev/subsystems/subsys-dfx-hilog-rich.md +++ b/en/device-dev/subsystems/subsys-dfx-hilog-rich.md @@ -166,7 +166,7 @@ This development guide is applicable to standard-system devices \(reference memo ``` #undef LOG_DOMAIN #undef LOG_TAG - #define LOG_DOMAIN 0 // Indicates the service domain. The value ranges from 0x0 to 0xFFFFF. + #define LOG_DOMAIN 0xD003200 // Indicates the service domain. The value ranges from 0xD000000 to 0xFFFFF. #define LOG_TAG "MY_TAG" ``` @@ -195,7 +195,7 @@ This development guide is applicable to standard-system devices \(reference memo ``` class MyClass { - static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, 0, "MY_TAG"}; + static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, 0xD003200, "MY_TAG"}; ... } ``` @@ -204,7 +204,7 @@ This development guide is applicable to standard-system devices \(reference memo ``` using namespace OHOS::HiviewDFX; - static constexpr HiLogLabel LABEL = {LOG_CORE, 0, "MY_TAG"}; + static constexpr HiLogLabel LABEL = {LOG_CORE, 0xD003200, "MY_TAG"}; ``` Print logs. diff --git a/en/device-dev/subsystems/subsys-dfx-hisysevent-logging.md b/en/device-dev/subsystems/subsys-dfx-hisysevent-logging.md index 2bb67bf541229f541a5461db0c04beecbeabd18e..f03d6ef5972806fbb04b728bf95ce7542bf3fff6 100644 --- a/en/device-dev/subsystems/subsys-dfx-hisysevent-logging.md +++ b/en/device-dev/subsystems/subsys-dfx-hisysevent-logging.md @@ -2,83 +2,192 @@ ## Overview -### Function +### Function Introduction HiSysEvent provides event logging APIs for OpenHarmony to record important information of key processes during system running. Besides, it supports shielding of event logging by event domain, helping you to evaluate the impact of event logging. ### Working Principles -Before logging system events, you need to complete HiSysEvent logging configuration. For details, see [HiSysEvent Logging Configuration](subsys-dfx-hisysevent-logging-config.md). +Before logging system events, you need to configure HiSysEvent logging. For details, see [HiSysEvent Logging Configuration](subsys-dfx-hisysevent-logging-config.md). ## How to Develop ### Use Cases -Use HiSysEvent logging to flush logged event data to disks. +Use HiSysEvent logging to flush logged event data to the event file. ### Available APIs -#### C++ Event Logging APIs +#### C++ Event Logging API -HiSysEvent logging is implemented using the API provided by the **HiSysEvent** class. For details, see the API Reference. +HiSysEvent logging is implemented using the API provided by the **HiSysEvent** class. For details, see the [API Header Files](/base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent/include/). -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> **NOTE** > > In OpenHarmony-3.2-Beta3, HiSysEvent logging is open for restricted use to avoid event storms. The **HiSysEvent::Write** API in Table 1 is replaced by the **HiSysEventWrite** API in Table 2. The **HiSysEvent::Write** API has been deprecated. Use the **HiSysEventWrite** API instead for HiSysEvent logging. -**Table 1** C++ event logging API (deprecated) +**Table 1** Description of the C++ event logging API (deprecated) | API | Description | -| ------------------------------------------------------------ | ---------------------- | -| template<typename... Types>
static int Write(const std::string &domain, const std::string &eventName, EventType type, Types... keyValues) | Flushes logged event data to disks.| +| ------------------------------------------------------------ | --------------------- | +| template<typename... Types> 
static int Write(const std::string &domain, const std::string &eventName, EventType type, Types... keyValues) | Flushes logged event data to the event file.| -**Table 2** C++ event logging API (in use) -| API | Description | -| ------------------------------------------------------------ | ---------------------- | -| HiSysEventWrite(domain, eventName, type, ...) | Flushes logged event data to disks.| - - **Table 3** Event types +**Table 2** Description of the C++ event logging API (in use) -| API | Description | -| --------- | ------------ | -| FAULT | Fault event| -| STATISTIC | Statistical event| -| SECURITY | Security event| -| BEHAVIOR | Behavior event| +| API | Description | +| ------------------------------------------------------------ | --------------------- | +| HiSysEventWrite(domain, eventName, type, ...) | Flushes logged event data to the event file.| + + **Table 3** Description of EventType enums + +| Event Type | Description | +| --------- | ----------- | +| FAULT | Fault event.| +| STATISTIC | Statistical event.| +| SECURITY | Security event.| +| BEHAVIOR | Behavior event.| + +#### C Event Logging API + +HiSysEvent logging is implemented using the API provided in the following table. For details, see the [API Header Files](/base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent/include/). + +**Table 4** Description of the C event logging API + +| API | Description | +| ------------------------------------------------------------ | ------------------------ | +| int OH_HiSysEvent_Write(const char\* domain, const char\* name, HiSysEventEventType type, HiSysEventParam params[], size_t size); | Flushes logged event data to the event file.| + +**Table 5** Description of HiSysEventEventType enums + +| Event Type | Description | +| -------------------- | -------------- | +| HISYSEVENT_FAULT | Fault event.| +| HISYSEVENT_STATISTIC | Statistical event.| +| HISYSEVENT_SECURITY | Security event.| +| HISYSEVENT_BEHAVIOR | Behavior event.| + +**Table 6** Description of the HiSysEventParam structure + +| Attribute | Type | Description | +| --------- | -------------------- | ---------------------------------- | +| name | char name[] | Event parameter name. | +| t | HiSysEventParamType | Event parameter type. | +| v | HiSysEventParamValue | Event parameter value. | +| arraySize | size_t | Array length when the event parameter value is of the array type.| + +**Table 7** Description of HiSysEventParamType enums + +| Type | Description | +| ----------------------- | -------------------------- | +| HISYSEVENT_INVALID | Invalid event parameter. | +| HISYSEVENT_BOOL | Event parameter of the bool type. | +| HISYSEVENT_INT8 | Event parameter of the int8_t type. | +| HISYSEVENT_UINT8 | Event parameter of the uint8_t type. | +| HISYSEVENT_INT16 | Event parameter of the int16_t type. | +| HISYSEVENT_UINT16 | Event parameter of the uint16_t type. | +| HISYSEVENT_INT32 | Event parameter of the int32_t type. | +| HISYSEVENT_UINT32 | Event parameter of the uint32_t type. | +| HISYSEVENT_INT64 | Event parameter of the int64_t type. | +| HISYSEVENT_UINT64 | Event parameter of the uint64_t type. | +| HISYSEVENT_FLOAT | Event parameter of the float type. | +| HISYSEVENT_DOUBLE | Event parameter of the double type. | +| HISYSEVENT_STRING | Event parameter of the char* type. | +| HISYSEVENT_BOOL_ARRAY | Event parameter of the bool array type. | +| HISYSEVENT_INT8_ARRAY | Event parameter of the int8_t array type. | +| HISYSEVENT_UINT8_ARRAY | Event parameter of the uint8_t array type. | +| HISYSEVENT_INT16_ARRAY | Event parameter of the int16_t array type. | +| HISYSEVENT_UINT16_ARRAY | Event parameter of the uint16_t array type.| +| HISYSEVENT_INT32_ARRAY | Event parameter of the int32_t array type. | +| HISYSEVENT_UINT32_ARRAY | Event parameter of the uint32_t array type.| +| HISYSEVENT_INT64_ARRAY | Event parameter of the int64_t array type. | +| HISYSEVENT_UINT64_ARRAY | Event parameter of the uint64_t array type.| +| HISYSEVENT_FLOAT_ARRAY | Event parameter of the float array type. | +| HISYSEVENT_DOUBLE_ARRAY | Event parameter of the double array type. | +| HISYSEVENT_STRING_ARRAY | Event parameter of the char* array type. | + +**Table 8** Description of the HiSysEventParamValue union + +| Attribute| Type| Description | +| -------- | -------- | ------------------------ | +| b | bool | Event parameter value of the bool type. | +| i8 | int8_t | Event parameter value of the int8_t type. | +| ui8 | uint8_t | Event parameter value of the uint8_t type. | +| i16 | int16_t | Event parameter value of the int16_t type. | +| ui16 | uint16_t | Event parameter value of the uint16_t type.| +| i32 | int32_t | Event parameter value of the int32_t type. | +| ui32 | uint32_t | Event parameter value of the uint32_t type.| +| i64 | int64_t | Event parameter value of the int64_t type. | +| ui64 | uint64_t | Event parameter value of the uint64_t type.| +| f | float | Event parameter value of the float type. | +| d | double | Event parameter value of the double type. | +| s | char* | Event parameter value of the char* type. | +| array | void* | Event parameter value of the array type. | #### Kernel Event Logging APIs -The following table describes the kernel event logging APIs. +Kernel event logging is implemented using the APIs provided in the following table. For details, see the [API Header File](/kernel/linux/linux-5.10/include/dfx/hiview_hisysevent.h). -**Table 4** Kernel event logging APIs +**Table 9** Description of kernel event logging APIs | API | Description | -| ------------------------------------------------------------ | ------------------------------------ | -| struct hiview_hisysevent *hisysevent_create(const char *domain, const char *name, enum hisysevent_type type); | Creates a **hisysevent** object. | -| void hisysevent_destroy(struct hiview_hisysevent *event); | Destroys a **hisysevent** object. | +| ------------------------------------------------------------ | ----------------------------------- | +| struct hiview_hisysevent *hisysevent_create(const char *domain, const char *name, enum hisysevent_type type); | Creates a **hisysevent** object. | +| void hisysevent_destroy(struct hiview_hisysevent *event); | Destroys a **hisysevent** object. | | int hisysevent_put_integer(struct hiview_hisysevent *event, const char *key, long long value); | Adds event parameters of the integer type to a **hisysevent** object. | | int hisysevent_put_string(struct hiview_hisysevent *event, const char *key, const char *value); | Adds event parameters of the string type to a **hisysevent** object.| -| int hisysevent_write(struct hiview_hisysevent *event); | Flushes **hisysevent** object data to disks. | +| int hisysevent_write(struct hiview_hisysevent *event); | Flushes **hisysevent** object data to the event file. | -**Table 5** Kernel event types +**Table 10** Description of hisysevent_type enums -| API | Description | -| --------- | ------------ | -| FAULT | Fault event| -| STATISTIC | Statistical event| -| SECURITY | Security event| -| BEHAVIOR | Behavior event| +| Event Type | Description | +| --------- | ----------- | +| FAULT | Fault event.| +| STATISTIC | Statistical event.| +| SECURITY | Security event.| +| BEHAVIOR | Behavior event.| ### How to Develop #### C++ Event Logging -1. Call the event logging API wherever needed, with required event parameters passed to the API. +Call the event logging API wherever needed, with required event parameters passed to the API. ```c++ HiSysEventWrite(HiSysEvent::Domain::AAFWK, "START_APP", HiSysEvent::EventType::BEHAVIOR, "APP_NAME", "com.ohos.demo"); ``` +#### C Event Logging + +1. If you want to pass custom event parameters to the event logging API, create an event parameter object based on the event parameter type and add the object to the event parameter array. + + ```c + // Create an event parameter of the int32_t type. + HiSysEventParam param1 = { + .name = "KEY_INT32", + .t = HISYSEVENT_INT32, + .v = { .i32 = 1 }, + .arraySize = 0, + }; + + // Create an event parameter of the int32_t array type. + int32_t int32Arr[] = { 1, 2, 3 }; + HiSysEventParam param2 = { + .name = "KEY_INT32_ARR", + .t = HISYSEVENT_INT32_ARRAY, + .v = { .array = int32Arr }, + .arraySize = sizeof(int32Arr) / sizeof(int32Arr[0]), + }; + + // Add the event parameter object to the created event parameter array. + HiSysEventParam params[] = { param1, param2 }; + ``` + +2. Call the event logging API wherever needed, with required event parameters passed to the API. + + ```c + OH_HiSysEvent_Write("TEST_DOMAIN", "TEST_NAME", HISYSEVENT_BEHAVIOR, params, sizeof(params) / sizeof(params[0])); + ``` + #### Kernel Event Logging 1. Create a **hisysevent** object based on the specified event domain, event name, and event type. @@ -151,7 +260,7 @@ Assume that a service module needs to trigger event logging during application s external_deps = [ "hisysevent_native:libhisysevent" ] ``` -2. In the application startup function **StartAbility()** of the service module, call the event logging API with the event parameters passed in. +2. In the application startup function **StartAbility()** of the service module, call the event logging API with event parameters passed in. ```c++ #include "hisysevent.h" @@ -164,6 +273,37 @@ Assume that a service module needs to trigger event logging during application s } ``` +#### C Event Logging + +Assume that a service module needs to trigger event logging during application startup to record the application startup event and application bundle name. The following is the complete sample code: + +1. Add the HiSysEvent component dependency to the **BUILD.gn** file of the service module. + + ```c++ + external_deps = [ "hisysevent_native:libhisysevent" ] + ``` + +2. In the application startup function **StartAbility()** of the service module, call the event logging API with event parameters passed in. + + ```c + #include "hisysevent_c.h" + + int StartAbility() + { + ... // Other service logic + char packageName[] = "com.ohos.demo"; + HiSysEventParam param = { + .name = "APP_NAME", + .t = HISYSEVENT_STRING, + .v = { .s = packageName }, + .arraySize = 0, + }; + HiSysEventParam params[] = { param }; + int ret = OH_HiSysEvent_Write("AAFWK", "START_APP", HISYSEVENT_BEHAVIOR, params, sizeof(params) / sizeof(params[0])); + ... // Other service logic + } + ``` + #### Kernel Event Logging Assume that the kernel service module needs to trigger event logging during device startup to record the device startup event. The following is the complete sample code: @@ -200,11 +340,10 @@ Assume that the kernel service module needs to trigger event logging during devi } ``` -#### Shielding of Event Logging by Event Domain +#### Shielding of Event Logging by Event Domain - If you want to shield event logging for the **AAFWK** and **POWER** domains in a **.cpp** file, define the **DOMAIN_MASKS** macro before including the **hisysevent.h** header file to the **.cpp** file. ```c++ - #define DOMAIN_MASKS "AAFWK|POWER" #include "hisysevent.h" @@ -212,14 +351,13 @@ Assume that the kernel service module needs to trigger event logging during devi HiSysEventWrite(OHOS:HiviewDFX::HiSysEvent::Domain::AAFWK, "JS_ERROR", OHOS:HiviewDFX::HiSysEvent::EventType::FAULT, "MODULE", "com.ohos.module"); // HiSysEvent logging is not performed. ... // Other service logic HiSysEventWrite(OHOS:HiviewDFX::HiSysEvent::Domain::POWER, "POWER_RUNNINGLOCK", OHOS:HiviewDFX::HiSysEvent::EventType::FAULT, "NAME", "com.ohos.module"); // HiSysEvent logging is not performed. - ``` - If you want to shield event logging for the **AAFWK** and **POWER** domains of the entire service module, define the **DOMAIN_MASKS** macro as follows in the **BUILG.gn** file of the service module. ```gn config("module_a") { - ... // Other configuration items - cflags_cc += ["-DDOMAIN_MASKS=\"AAFWK|POWER\""] + ... // Other configuration items + cflags_cc += ["-DDOMAIN_MASKS=\"AAFWK|POWER\""] } ``` diff --git a/en/device-dev/subsystems/subsys-dfx-hisysevent-query.md b/en/device-dev/subsystems/subsys-dfx-hisysevent-query.md index 66cd39343e583f36efb4fade8d4818f84b6278af..2f5cd08ffab5ac1499f9e4a6e324caf1de64f106 100644 --- a/en/device-dev/subsystems/subsys-dfx-hisysevent-query.md +++ b/en/device-dev/subsystems/subsys-dfx-hisysevent-query.md @@ -6,106 +6,382 @@ HiSysEvent allows you to query system events by specifying search criteria. For example, for a power consumption module, you can query required system events for analysis. -## Development Guidelines - +## How to Develop ### Available APIs -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +#### C++ Event Query API + +HiSysEvent query is implemented using the API provided by the **HiSysEventManager** class. For details, see the [API Header Files](/base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent_manager/include/). + +> **NOTE** > -> For details about the **HiSysEventRecord** argument in the **OnQuery()** method of **HiSysEventQueryCallback**, see Table 5 in [HiSysEvent Listening](subsys-dfx-hisysevent-listening.md). +> For details about **HiSysEventRecord** in the **OnQuery()** API of **HiSysEventQueryCallback**, see Table 5 in [HiSysEvent Listening](subsys-dfx-hisysevent-listening.md). -**Table 1** Description of the HiSysEvent query API + **Table 1** Description of the HiSysEvent query API -| API | Description | -| --- | ----------- | -| int32_t HiSysEventManager::Query(struct QueryArg& arg, std::vector<QueryRule>& rules, std::shared_ptr<HiSysEventQueryCallback> callback) | Queries system events by specifying search criteria such as the time segment, event domain, and event name.
Input arguments:
- **arg**: event query parameter.
- **rules**: rules for event filtering.
- **callback**: callback object for event query.
Return value:
- **0**: Query is successful.
- A negative value: Query has failed.| +| API| Description| +| -------- | -------- | +| int32_t Query(struct QueryArg& arg,
std::vector<QueryRule>& rules,
std::shared_ptr<HiSysEventQueryCallback> callback) | Queries system events by search criteria such as the time segment, event domain, and event name.
Input arguments:
- **arg**: event query parameter.
- **rules**: rules for event filtering.
- **callback**: callback object for event query.
Return value:
- **0**: Query is successful.
- A negative value: Query has failed.| -**Table 2** Description of QueryArg + **Table 2** Description of QueryArg objects -| Attribute | Description | -| --------- | ----------- | -| beginTime | Start time, in the **long long int** format.| -| endTime | End time, in the **long long int** format.| -| maxEvents | Maximum number of returned events, in the **int** format.| +| Attribute| Type| Description| +| -------- | -------- | -------- | +| beginTime | long long | Start time for query. The value is a Unix timestamp, in milliseconds.| +| endTime | long long | End time for query. The value is a Unix timestamp, in milliseconds.| +| maxEvents | int | Maximum number of returned events.| -**Table 3** Description of QueryRule + **Table 3** Description of QueryRule objects | API| Description| | -------- | -------- | -| QueryRule(const std::string& domain, const std::vector<std::string>& eventList) | Constructor used to create a **QueryRule** object.
Input arguments:
- **domain**: domain to which the event of the **QueryRule** object belongs, in the string format. By default, an empty string indicates that the domain is successfully matched.
- **eventList**: event name list, in the **std::vector<std::string>** format. By default, an empty string indicates that the event names on the list are successfully matched.| +| QueryRule(const std::string& domain,
const std::vector<std::string>& eventList) | Constructor used to create a **QueryRule** object.
Input arguments:
- **domain**: domain to which the event of the **QueryRule** object belongs, in the string format. By default, an empty string indicates that the domain is successfully matched.
**eventList**: event name list, in the **std::vector<std::string>** format. By default, an empty string indicates that the event names on the list are successfully matched.| -**Table 4** Description of HiSysEventQueryCallback + **Table 4** Description of HiSysEventQueryCallback objects | API| Description| | -------- | -------- | -| void HiSysEventQueryCallback::OnQuery(std::shared_ptr<std::vector<HiSysEventRecord>> sysEvents) | Callback object for event query.
Input arguments:
- **sysEvents**: event list.
Return value:
None.| -| void HiSysEventQueryCallback::OnComplete(int32_t reason, int32_t total) | Callback object for completion of event query.
Input arguments:
- **reason**: reason for completion of event query. The default value is **0**.
- **total**: total number of events returned in this query.
Return value:
None.| +| void HiSysEventQueryCallback::OnQuery(std::shared_ptr<std::vector<HiSysEventRecord>> sysEvents) | Callback object for event query.
Input arguments:
- **sysEvents**: event list.| +| void HiSysEventQueryCallback::OnComplete(int32_t reason, int32_t total) | Callback object for completion of event query.
Input arguments:
- **reason**: reason for completion of event query. The value **0** indicates that the query is normal, and any other value indicates that the query has failed.
- **total**: total number of events returned in this query.| + +#### C Event Query API + +HiSysEvent query is implemented using the API provided in the following table. For details, see the [API Header Files](/base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent_manager/include/). + + **Table 5** Description of the HiSysEvent query API + +| API | Description | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| int OH_HiSysEvent_Query(const HiSysEventQueryArg& arg, HiSysEventQueryRule rules[], size_t ruleSize, HiSysEventQueryCallback& callback); | Queries system events by search criteria such as the time segment, event domain, event name, and event parameter.
Input arguments:
- **arg**: event query parameter.
- **rules**: rules for event filtering.
- **ruleSize**: number of event filtering rules.
- **callback**: callback object for event query.
Return value:
- **0**: Query is successful.
- A negative value: Query has failed.| + + **Table 6** Description of the HiSysEventQueryArg structure + +| Attribute | Type| Description | +| --------- | -------- | ---------------------------------------------------- | +| beginTime | int64_t | Start time for query. The value is a Unix timestamp, in milliseconds.| +| endTime | int64_t | End time for query. The value is a Unix timestamp, in milliseconds.| +| maxEvents | int32_t | Maximum number of returned events. | + +**Table 7** Description of the HiSysEventQueryRule structure + +| Attribute | Type | Description | +| ------------- | --------- | ---------------------------------- | +| domain | char[] | Event domain. | +| eventList | char\[][] | Event name list. | +| eventListSize | size_t | Size of the event name list. | +| condition | char* | Custom event parameter conditions for the query.| + +The **condition** parameter must be in the specified JSON string format. For example: + +```json +{ + "version":"V1", + "condition":{ + "and":[ + {"param":"type_","op":">","value":0}, + {"param":"uid_","op":"=","value":1201} + ], + "or":[ + {"param":"NAME","op":"=","value":"SysEventService"}, + {"param":"NAME","op":"=","value":"SysEventSource"} + ] + } +} +``` + +- The **version** field is mandatory, indicating the supported version of the input condition. Currently, only **V1** is supported. +- The **condition** field is mandatory, indicating the input condition. + - The **and** field is optional, indicating the AND relationship between conditions. + - The **or** field is optional, indicating the OR relationship between conditions. + - The **param** field is mandatory, indicating the parameter name for condition matching. The value must be a string. + - The **op** field is mandatory, indicating the parameter comparison operator for condition matching. The value must be a string. Supported comparison operators include the following: =, >, <, >=, and <=. + - The **value** field is mandatory, indicating the parameter value for condition matching. The value must be a string or an integer. + +**Table 8** Description of the HiSysEventQueryCallback structure + +| Attribute | Type | Description | +| ---------- | -------------------------------------------------- | ------------------------------------------------------------ | +| OnQuery | void (*)(HiSysEventRecord records[], size_t size); | Callback object for event query.
Input arguments:
- **records**: event list.
- **size**: size of the event list.| +| OnComplete | void (*)(int32_t reason, int32_t total); | Callback object for completion of event query.
Input arguments:
- **reason**: reason for completion of event query. The value **0** indicates that the query is normal, and any other value indicates that the query has failed.
- **total**: total number of events returned in this query.| + +**Table 9** Description of the HiSysEventRecord event structure + +| Attribute | Type | Description | +| --------- | ------------------- | -------------------------- | +| domain | char[] | Event domain. | +| eventName | char\[] | Event name. | +| type | HiSysEventEventType | Event type. | +| time | uint64_t | Event timestamp. | +| tz | char\[] | Event time zone. | +| pid | int64_t | Process ID of the event. | +| tid | int64_t | Thread ID of the event. | +| uid | int64_t | User ID of the event. | +| traceId | uint64_t | Distributed call chain trace ID of the event. | +| spandId | uint64_t | Span ID for the distributed call chain trace of the event. | +| pspanId | uint64_t | Parent span ID for the distributed call chain trace of the event.| +| traceFlag | int | Distributed call chain trace flag of the event. | +| level | char* | Event level. | +| tag | char* | Event tag. | +| jsonStr | char* | Event content. | + +**Table 10** Description of HiSysEventRecord APIs + +| API | | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| void OH_HiSysEvent_GetParamNames(const HiSysEventRecord& record, char*** params, size_t& len); | Obtains all parameter names of an event.
Input arguments:
- **record**: event structure.
- **params**: parameter name array.
- **len**: size of the parameter name array.| +| int OH_HiSysEvent_GetParamInt64Value(const HiSysEventRecord& record, const char* name, int64_t& value); | Parses the parameter value in the event to an int64_t value and assigns the value to **value**.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: parameter value of the int64_t type.| +| int OH_HiSysEvent_GetParamUint64Value(const HiSysEventRecord& record, const char* name, uint64_t& value); | Parses the parameter value in the event to an uint64_t value and assigns the value to **value**.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: parameter value of the uint64_t type.| +| int OH_HiSysEvent_GetParamDoubleValue(const HiSysEventRecord& record, const char* name, double& value); | Parses the parameter value in the event to a double value and assigns the value to **value**.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: parameter value of the double type.| +| int OH_HiSysEvent_GetParamStringValue(const HiSysEventRecord& record, const char* name, char** value); | Parses the parameter value in the event to a char array value and assigns the value to **value**. You need to release the memory manually after usage.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: char\* reference.| +| int OH_HiSysEvent_GetParamInt64Values(const HiSysEventRecord& record, const char* name, int64_t** value, size_t& len); | Parses the parameter value in the event to a int64_t array value and assigns the value to **value**. You need to release the memory manually after usage.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: int64_t\* reference.
- **len**: array size.| +| int OH_HiSysEvent_GetParamUint64Values(const HiSysEventRecord& record, const char* name, uint64_t** value, size_t& len); | Parses the parameter value in the event to a uint64_t array value and assigns the value to **value**. You need to release the memory manually after usage.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: uint64_t\* reference.
- **len**: array size.| +| int OH_HiSysEvent_GetParamDoubleValues(const HiSysEventRecord& record, const char* name, double** value, size_t& len); | Parses the parameter value in the event to a double array value and assigns the value to **value**. You need to release the memory manually after usage.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: double\* reference.
- **len**: array size.| +| int OH_HiSysEvent_GetParamStringValues(const HiSysEventRecord& record, const char* name, char*** value, size_t& len); | Parses the parameter value in the event to a char* array value and assigns the value to **value**. You need to release the memory manually after usage.
Input arguments:
- **record**: event structure.
- **name**: parameter name.
- **value**: char\*\* reference.
- **len**: array size.| + +The return values of the HiSysEventRecord APIs are described as follows: + +- **0**: The parsing is successful. +- -**1**: The event fails to be initialized. +- -**2**: The parameter name does not exist. +- -**3**: The type of the parameter value to be parsed does not match the type of the input parameter value. ### How to Develop -**C++** +#### C++ HiSysEvent Query API -1. Develop the source code. - Import the corresponding header file: +1. Import the corresponding header file: - ``` + ```c++ #include "hisysevent_manager.h" ``` - Implement the callback API. +2. Implement the callback API. - ``` - void HiSysEventQueryCallback::OnQuery(std::shared_ptr> sysEvents) - void HiSysEventQueryCallback::OnComplete(int32_t reason, int32_t total) + ```c++ + class TestQueryCallback : public HiSysEventQueryCallback { + public: + void OnQuery(std::shared_ptr> sysEvents) override + { + if (sysEvents == nullptr) { + return; + } + for_each((*sysEvents).cbegin(), (*sysEvents).cend(), [](const HiSysEventRecord& event) { + std::cout << event.AsJson() << std::endl; + }); + } + + void OnComplete(int32_t reason, int32_t total) override + { + std::cout << "Query completed" << std::endl; + return; + } + }; ``` - Call the query API in the corresponding service logic. +3. Call the query API while passing in the query parameter, rule, and callback object. + ```c++ + // Create a query parameter object. + long long startTime = 0; + long long endTime = 1668245644000; //2022-11-12 09:34:04 + int queryCount = 10; + QueryArg arg(startTime, endTime, queryCount); + + // Create a query rule object. + QueryRule rule("HIVIEWDFX", { "PLUGIN_LOAD" }); + std::vector queryRules = { rule }; + + // Create a query callback object. + auto queryCallback = std::make_shared(); + + // Call the query API. + HiSysEventManager::Query(arg, queryRules, queryCallback); ``` - HiSysEventManager::Query(struct QueryArg& queryArg, - std::vector& queryRules, std::shared_ptr queryCallBack) - ``` - In this example, you'll query all system events. +#### C HiSysEvent Query API + +1. Import the corresponding header file: + ```c++ + #include "hisysevent_manager_c.h" ``` - #include "hisysevent_manager.h" - #include - namespace OHOS { - namespace HiviewDFX { - // Implement the query callback API. - void HiSysEventToolQuery::OnQuery(std::shared_ptr> sysEvents) +2. Implement the callback API. + + ```c++ + void OnQueryTest(HiSysEventRecord records[], size_t size) { - if (sysEvents == nullptr) { - return; + for (size_t i = 0; i < size; i++) { + printf("OnQuery: event=%s", records[i].jsonStr); } - for_each((*sysEvents).cbegin(), (*sysEvents).cend(), [](const HiSysEventRecord& event) { - std::cout << event.AsJson() << std::endl; - }); } - - void HiSysEventToolQuery::OnComplete(int32_t reason, int32_t total) + + void OnCompleteTest(int32_t reason, int32_t total) { - return; + printf("OnCompleted, res=%d, total=%d\n", reason, total); } - } // namespace HiviewDFX - } // namespace OHOS - - // Call the query callback API to obtain system events. - auto queryCallBack = std::make_shared(); - struct QueryArg args(clientCmdArg.beginTime, clientCmdArg.endTime, clientCmdArg.maxEvents); - std::vector rules; - HiSysEventManager::QueryHiSysEvent(args, rules, queryCallBack); ``` -2. Modify the **BUILD.gn** file. - In the **BUILD.gn** file, add the **libhisysevent** and **libhisyseventmanager** libraries that depend on the **hisysevent_native** component. - +3. Call the query API while passing in the query parameter, rule, and callback object. + + ```c++ + // Create a query parameter object. + HiSysEventQueryArg arg; + arg.beginTime = 0; + arg.endTime = 1668245644000; //2022-11-12 09:34:04 + arg.maxEvents = 10; + + // Create a query rule object. + constexpr char TEST_DOMAIN[] = "HIVIEWDFX"; + constexpr char TEST_NAME[] = "PLUGIN_LOAD"; + HiSysEventQueryRule rule; + (void)strcpy_s(rule.domain, strlen(TEST_DOMAIN) + 1, TEST_DOMAIN); + (void)strcpy_s(rule.eventList[0], strlen(TEST_NAME) + 1, TEST_NAME); + rule.eventListSize = 1; + rule.condition = nullptr; + HiSysEventQueryRule rules[] = { rule }; + + // Create a query callback object. + HiSysEventQueryCallback callback; + callback.OnQuery = OnQueryTest; + callback.OnComplete = OnCompleteTest; + + // Call the query API. + OH_HiSysEvent_Query(arg, rules, sizeof(rules) / sizeof(HiSysEventQueryRule), callback); ``` + +### Development Example + +#### C++ HiSysEvent Query + +Assume that you need to query all **PLUGIN_LOAD** events that are generated for the **HIVIEWDFX** domain until the current time on a service module. The development procedure is as follows: + +1. Add the **libhisysevent** and **libhisyseventmanager** dependencies of the **hisysevent_native** component to **BUILD.gn** of the service module. + + ```c++ external_deps = [ "hisysevent_native:libhisysevent", "hisysevent_native:libhisyseventmanager", ] ``` + +2. Call the query API in the **TestQuery()** function of the service module. + + ```c++ + #include "hisysevent_manager.h" + #include + #include + + using namespace OHOS::HiviewDFX; + + class TestQueryCallback : public HiSysEventQueryCallback { + public: + void OnQuery(std::shared_ptr> sysEvents) override + { + if (sysEvents == nullptr) { + return; + } + for_each((*sysEvents).cbegin(), (*sysEvents).cend(), [](const HiSysEventRecord& event) { + std::cout << event.AsJson() << std::endl; + }); + } + + void OnComplete(int32_t reason, int32_t total) override + { + std::cout << "Query completed" << std::endl; + return; + } + }; + + int64_t GetMilliseconds() + { + auto now = std::chrono::system_clock::now(); + auto millisecs = std::chrono::duration_cast(now.time_since_epoch()); + return millisecs.count(); + } + + void TestQuery() + { + // Create a query parameter object. + long long startTime = 0; + long long endTime = GetMilliseconds(); + int maxEvents = 100; + QueryArg arg(startTime, endTime, maxEvents); + + // Create a query rule object. + QueryRule rule("HIVIEWDFX", { "PLUGIN_LOAD" }); + std::vector queryRules = { rule }; + + // Create a query callback object. + auto queryCallback = std::make_shared(); + + // Call the query API. + int ret = HiSysEventManager::Query(arg, queryRules, queryCallback); + } + ``` + +#### C HiSysEvent Query + +Assume that you need to query all **PLUGIN_LOAD** events that are generated for the **HIVIEWDFX** domain until the current time on a service module. The development procedure is as follows: + +1. Add the **libhisyseventmanager** dependency of the **hisysevent_native** component to the **BUILD.gn** file of the service module. + + ```c++ + external_deps = [ "hisysevent_native:libhisyseventmanager" ] + + // for strcpy_s + deps = [ "//third_party/bounds_checking_function:libsec_shared" ] + ``` + +2. Call the query API in the **TestQuery()** function of the service module. + + ```c++ + #include "hisysevent_manager_c.h" + #include + #include + + void OnQueryTest(HiSysEventRecord records[], size_t size) + { + for (size_t i = 0; i < size; i++) { + printf("OnQuery: event=%s", records[i].jsonStr); + } + } + + void OnCompleteTest(int32_t reason, int32_t total) + { + printf("OnCompleted, res=%d, total=%d\n", reason, total); + } + + int64_t GetMilliseconds() + { + return time(NULL); + } + + void TestQuery() + { + // Create a query parameter object. + HiSysEventQueryArg arg; + arg.beginTime = 0; + arg.endTime = GetMilliseconds(); + arg.maxEvents = 100; + + // Create a query rule object. + constexpr char TEST_DOMAIN[] = "HIVIEWDFX"; + constexpr char TEST_NAME[] = "PLUGIN_LOAD"; + HiSysEventQueryRule rule; + (void)strcpy_s(rule.domain, strlen(TEST_DOMAIN) + 1, TEST_DOMAIN); + (void)strcpy_s(rule.eventList[0], strlen(TEST_NAME) + 1, TEST_NAME); + rule.eventListSize = 1; + rule.condition = nullptr; + HiSysEventQueryRule rules[] = { rule }; + + // Create a query callback object. + HiSysEventQueryCallback callback; + callback.OnQuery = OnQueryTest; + callback.OnComplete = OnCompleteTest; + + // Call the query API. + int ret = OH_HiSysEvent_Query(arg, rules, sizeof(rules) / sizeof(HiSysEventQueryRule), callback); + } + ``` diff --git a/en/device-dev/subsystems/subsys-dfx-hisysevent-tool.md b/en/device-dev/subsystems/subsys-dfx-hisysevent-tool.md index 4dccc4dbf082cc1121443c00adbef531b2b5ed23..cb0f8cead65cd1faed98c723d30f557b4302bbab 100644 --- a/en/device-dev/subsystems/subsys-dfx-hisysevent-tool.md +++ b/en/device-dev/subsystems/subsys-dfx-hisysevent-tool.md @@ -42,7 +42,7 @@ The HiSysEvent tool is a command line tool preconfigured in the **/system/bin** | Option| Description| | -------- | -------- | | -t | Event tag used to filter subscribed real-time system events.| - | -c | Matching rule for event tags. The options can be **WHOLE_WORD**, **PREFIX**, or **REGULAR**.| + | -c | Matching rule for event tags. The option can be **WHOLE_WORD**, **PREFIX**, or **REGULAR**.| Example: @@ -67,7 +67,7 @@ The HiSysEvent tool is a command line tool preconfigured in the **/system/bin** | -------- | -------- | | -o | Event domain used to filter subscribed real-time system events.| | -n | Event name used to filter subscribed real-time system events.| - | -c | Matching rule for event domains and event names. The options can be **WHOLE_WORD**, PREFIX, or **REGULAR**.| + | -c | Matching rule for event domains and event names. The option can be **WHOLE_WORD**, PREFIX, or **REGULAR**.| Example: @@ -83,6 +83,30 @@ The HiSysEvent tool is a command line tool preconfigured in the **/system/bin** > **NOTE** > If **-t**, **-o**, and **-n** are specified, the system checks whether the configured event tag is null. If the event tag is not null, the system filters system events based on the matching rules for the event tag. Otherwise, the system filters system events based on the matching rules for the event domain and event name. +- Command for subscribing to real-time system events by event type: + + ``` + hisysevent -r -g [FAULT|STATISTIC|SECURITY|BEHAVIOR] + ``` + + Description of command options: + + | Option| Description| + | -------- | -------- | + | -g | Type of the system events to be subscribed to. The option can be **FAULT**, **STATISTIC**, **SECURITY**, or **BEHAVIOR**.| + + Example: + + ``` + # hisysevent -r -o "RELIABILITY" -n "APP_FREEZE" -g FAULT + {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501963989773,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201309","HAPPEN_TIME":1501963989773,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"16367997008075110557","info_":""} + # hisysevent -r -o "POWER\w{0,8}" -n "POWER_RUNNINGLOCK" -c REGULAR -g STATISTIC + {"domain_":"POWER","name_":"POWER_RUNNINGLOCK","type_":2,"time_":1667485283785,"tz_":"+0000","pid_":538,"tid_":684,"uid_":5523,"PID":360,"UID":1001,"STATE":0,"TYPE":1,"NAME":"telRilRequestRunningLock","LOG_LEVEL":2,"TAG":"DUBAI_TAG_RUNNINGLOCK_REMOVE","MESSAGE":"token=25956496","level_":"MINOR","tag_":"PowerStats","id_":"11994072552538324655","info_":""} + # hisysevent -r -o "ACCOU\w+" -c REGULAR -g SECURITY + {"domain_":"ACCOUNT","name_":"PERMISSION_EXCEPTION","type_":3,"time_":1667484405993,"tz_":"+0000","pid_":614,"tid_":614,"uid_":3058,"CALLER_UID":1024,"CALLER_PID":523,"PERMISSION_NAME":"ohos.permission.MANAGE_LOCAL_ACCOUNTS","level_":"CRITICAL","tag_":"security","id_":"15077995598140341422","info_":""} + # hisysevent -r -o MULTIMODALINPUT -g BEHAVIOR + {"domain_":"MULTIMODALINPUT","name_":"Z_ORDER_WINDOW_CHANGE","type_":4,"time_":1667549852735,"tz_":"+0000","pid_":2577,"tid_":2588,"uid_":6696,"OLD_ZORDER_FIRST_WINDOWID":-1,"NEW_ZORDER_FIRST_WINDOWID":2,"OLD_ZORDER_FIRST_WINDOWPID":-1,"NEW_ZORDER_FIRST_WINDOWPID":1458,"MSG":"The ZorderFirstWindow changing succeeded","level_":"MINOR","tag_":"PowerStats","id_":"16847308118559691400","info_":""} + ``` ## Querying Historical System Events @@ -139,6 +163,56 @@ The HiSysEvent tool is a command line tool preconfigured in the **/system/bin** {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501964222980,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201702","HAPPEN_TIME":1501964222980,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"10435592800188571430","info_":""} ``` +- Command for querying historical system events by event domain and event name: + + ``` + hisysevent -l -o -n [-c WHOLE_WORD] + ``` + + Description of command options: + + | Option| Description| + | -------- | -------- | + | -o | Domain based on which historical system events are queried.| + | -n | Name based on which historical system events are queried.| + | -c | Rule for matching the domain and name of historical system events. The option can only be **WHOLE_WORD**.| + + Example: + + ``` + # hisysevent -l -n "APP_FREEZE" + {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501963989773,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201309","HAPPEN_TIME":1501963989773,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"16367997008075110557","info_":""} + # hisysevent -r -o "RELIABILITY" + {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501963989773,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201544","HAPPEN_TIME":1501963989773,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"13456525196455104060","info_":""} + # hisysevent -r -o "RELIABILITY" -n "APP_FREEZE" -c WHOLE_WORD + {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501963989773,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201633","HAPPEN_TIME":1501963989773,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"12675246910904037271","info_":""} + ``` + +- Command for querying historical system events by event type: + + ``` + hisysevent -l -g [FAULT|STATISTIC|SECURITY|BEHAVIOR] + ``` + + Description of command options: + + | Option| Description| + | -------- | -------- | + | -g | Type of the historical system events to be queried. The option can be **FAULT**, **STATISTIC**, **SECURITY**, or **BEHAVIOR**.| + + Example: + + ``` + # hisysevent -l -o "RELIABILITY" -g FAULT + {"domain_":"RELIABILITY","name_":"APP_FREEZE","type_":1,"time_":1501963989773,"pid_":1505,"uid_":10002,"FAULT_TYPE":"4","MODULE":"com.ohos.screenlock","REASON":"NO_DRAW","SUMMARY":"SUMMARY:\n","LOG_PATH":"/data/log/faultlog/faultlogger/appfreeze-com.ohos.screenlock-10002-20170805201309","HAPPEN_TIME":1501963989773,"VERSION":"1.0.0","level_":"CRITICAL","tag_":"STABILITY","id_":"16367997008075110557","info_":""} + # hisysevent -l -n "POWER_RUNNINGLOCK" -c WHOLE_WORD -g STATISTIC + {"domain_":"POWER","name_":"POWER_RUNNINGLOCK","type_":2,"time_":1667485283785,"tz_":"+0000","pid_":538,"tid_":684,"uid_":5523,"PID":360,"UID":1001,"STATE":0,"TYPE":1,"NAME":"telRilRequestRunningLock","LOG_LEVEL":2,"TAG":"DUBAI_TAG_RUNNINGLOCK_REMOVE","MESSAGE":"token=25956496","level_":"MINOR","tag_":"PowerStats","id_":"11994072552538324655","info_":""} + # hisysevent -l -g SECURITY + {"domain_":"ACCOUNT","name_":"PERMISSION_EXCEPTION","type_":3,"time_":1667484405993,"tz_":"+0000","pid_":614,"tid_":614,"uid_":3058,"CALLER_UID":1024,"CALLER_PID":523,"PERMISSION_NAME":"ohos.permission.MANAGE_LOCAL_ACCOUNTS","level_":"CRITICAL","tag_":"security","id_":"15077995598140341422","info_":""} + # hisysevent -l -o MULTIMODALINPUT -g BEHAVIOR + {"domain_":"MULTIMODALINPUT","name_":"Z_ORDER_WINDOW_CHANGE","type_":4,"time_":1667549852735,"tz_":"+0000","pid_":2577,"tid_":2588,"uid_":6696,"OLD_ZORDER_FIRST_WINDOWID":-1,"NEW_ZORDER_FIRST_WINDOWID":2,"OLD_ZORDER_FIRST_WINDOWPID":-1,"NEW_ZORDER_FIRST_WINDOWPID":1458,"MSG":"The ZorderFirstWindow changing succeeded","level_":"MINOR","tag_":"PowerStats","id_":"16847308118559691400","info_":""} + ``` + ## System Event Validity Check - Enabling system event validity check diff --git a/en/device-dev/subsystems/subsys-ota-guide.md b/en/device-dev/subsystems/subsys-ota-guide.md index 9f10a82773d3a151fa56041b1ec5951a4e7f4c42..3b2e5d680d743fc8d32a6b5d4601709a47005603 100644 --- a/en/device-dev/subsystems/subsys-ota-guide.md +++ b/en/device-dev/subsystems/subsys-ota-guide.md @@ -6,7 +6,7 @@ ### Overview -With the rapid development of device systems, it has become a critical challenge for device vendors to help their users to access system updates in a timely manner to experience the new features and improve the system stability and security. +With the rapid development of device systems, it has become a critical challenge for device vendors to help their users to access system updates in a timely manner to experience the new features and improve the system stability and security Over the Air (OTA) answers this challenge with the support for remote updating of device systems. By providing unified update APIs externally, the update subsystem shields the differences of underlying chips. After secondary development based on the update APIs, device vendors can easily implement remote updating for their devices, such as IP cameras. @@ -18,51 +18,48 @@ Over the Air (OTA) answers this challenge with the support for remote updating o - Differential package: an update package that packs the differential images between the source version and target version. -### Implementation Principle +### Principles To implement an OTA update, you first need to use the packaging tool to pack the build version into an update package and upload the update package to the OTA server. Devices integrating the OTA update capability can then detect the update package and download it to implement a remote update. -A/B Update: a typical application of OTA update. In this update mode, a device has a backup system B. When system A is running, system B is updated silently. After the update is successful, the device restarts and switches to the new system. +A/B Update: a typical application of OTA update. In this update mode, a device has a backup system B. When system A is running, system B is updated silently. After the update is successful, the device restarts and switches to the new system. ### Constraints -- Only the open-source suites for devices powered by Hi3861, Hi3518E V300, and Hi3516D V300 are supported. +- Only the open-source suites for devices powered by Hi3861, Hi3516D V300, and RK3568 are supported. -- Devices developed based on Hi3518E V300 and Hi3516D V300 must support the SD card in the Virtual Festival of Aerobatic Teams (VFAT) format. +- Devices developed based on Hi3516D V300 and RK3568 must support the SD card in the Virtual Festival of Aerobatic Teams (VFAT) format. - Generation of update packages can only be performed on the Linux system. - Currently, the mini and small systems support update using a full package, but not a differential package or an update package with partitions changed. +- To implement an A/B update, ensure that the devices running the standard system support booting from partitions A and B. + -## Environment Preparation +## Preparations - On Windows, download and install the OpenSSL tool and configure environment variables. - Prepare the packaging tool. - Build version images using the packaging tool. -- On Linux, pack version images to generate the update package. -- To implement an A/B update, ensure that the devices running the standard system support booting from partitions A and B. - -## OTA Update Guide +## How to Develop -### Development Procedure - -1. Use the OpenSSL tool to generate a public/private key pair. +1. Use the OpenSSL tool to generate a public/private key pair. 2. Use the packaging tool to generate an update package. -  2.1 Mini and small systems +  2.1 Mini and small systems -  2.2 Standard system +  2.2 Standard system -3. Upload the update package to the vendor's OTA server. +3. Upload the update package to the vendor's OTA server. 4. Download the update package from the vendor's OTA server. -5. Integrate the OTA update capability. +5. Integrate the OTA update capability.   5.1 API application scenario (default) @@ -71,31 +68,33 @@ To implement an OTA update, you first need to use the packaging tool to pack the   5.3 A/B update scenario -### How to Develop +## How to Develop -#### Generating a Public/Private Key Pair +### Generating a Public/Private Key Pair 1. Use the OpenSSL tool to generate a public/private key pair. -3. Keep the private key file properly as this file stores the private key used to sign the update package. You need to specify the private key file in the command used for preparing the update package. The public key is used to verify the signature of the update package during the update. For the mini and small systems, the generated public key is preset in the code. You need to implement the **HotaHalGetPubKey** API to obtain the key. For the standard system, the generated public key is stored in the **./device/hisilicon/hi3516dv300/build/updater_config/signing_cert.crt** file. +3. Keep the private key file properly as this file stores the private key used to sign the update package. You need to specify the private key file in the command used for preparing the update package. The public key is used to verify the signature of the update package during the update. For the mini and small systems, the generated public key is preset in the code. The vendor needs to implement the **HotaHalGetPubKey** API to obtain the key. For the standard system, the generated public key is stored in the **/hisilicon/hi3516dv300/build/updater\_config/signing\_cert.crt** file in the **device** or **vendor** directory. -5. For the mini and small systems that use the Hi3518E V300 or Hi3516D V300 suite, use the content in **public\_arr.txt** to replace the content in **g\_pub\_key** in the **device\hisilicon\third_party\uboot\u-boot-2020.01\product\hiupdate\verify\update\_public\_key.c** file of the U-Boot module. +5. For the mini and small systems that use the Hi3516D V300 suite, use the content in **third\_party\u-boot\u-boot-2020.01\product\hiupdate\verify\update\_public\_key.c** file of the U-Boot module. Example configuration: ```c - static unsigned char g_pub_key[PUBKEY_LEN] = { + static unsigned char g_pub_key[] = { 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBF, 0xAA, 0xA5, 0xB3, 0xC2, 0x78, 0x5E, } ``` -#### Making an Update Package +### Making an Update Package -##### Mini and Small Systems +#### Mini and Small Systems 1. Create the **target\_package** folder with the following directory structure: + + OTA.tag and config are not required for lightweight systems and small systems upgraded from AB. ```text target_package @@ -103,16 +102,15 @@ To implement an OTA update, you first need to use the packaging tool to pack the ├── config ├── {component_1} ├── {component_2} - ├── ... + ├── ...... ├── {component_N} └── updater_config └── updater_specified_config.xml ``` -2. Place the components to be updated, including the image file (for example, **rootfs.img**), as **{component\_N}** in the root directory of the **target\_package** folder. +2. Place the components to be updated, including the image file (for example, **rootfs.img**), as the substitute of **{component\_N}** in the root directory of the **target\_package** folder. 3. Configure the **updater\_specified\_config.xml** file in the **updater_config** folder. - Example configuration: @@ -130,17 +128,16 @@ To implement an OTA update, you first need to use the packaging tool to pack the ``` - - **Table 1** Description of nodes in the component configuration file + **Table 1** Description of nodes in the component configuration file | Type| Node Name| Node Label| Mandatory| Description| | -------- | -------- | -------- | -------- | -------- | | Header information (head node)| info| / | Yes| Content of this node: head info| | Header information (head node)| info| fileVersion | Yes| This field is reserved and does not affect the generation of the update package.| | Header information (head node)| info| prdID | Yes| This field is reserved and does not affect the generation of the update package.| - | Header information (head node)| info| softVersion | Yes| Software version number, that is, the version number of the update package. The version number must be within the range specified by **VERSION.mbn**. Otherwise, an update package will not be generated.| - | Header information (head node)| info| date| Yes| Date when the update package is generated. This field is reserved and does not affect the generation of the update package.| - | Header information (head node)| info| time| Yes| Time when the update package is generated. This field is reserved and does not affect the generation of the update package.| + | Header information (head node)| info| softVersion | Yes| Software version number, that is, the version number of the update package. Ensure that the version number is later than the basic version number and OpenHarmony is not followed by any other letter. Otherwise, the update cannot be performed.| + | Header information (head node)| info| *date* | Yes| Date when the update package is generated. This field is reserved and does not affect the generation of the update package.| + | Header information (head node)| info| *time* | Yes| Time when the update package is generated. This field is reserved and does not affect the generation of the update package.| | Component information (group node)| component| / | Yes| Content of this node: path of the component or image file to be packed into the update package. It is the root directory of the version package by default.| | Component information (group node)| component| compAddr | Yes| Name of the partition corresponding to the component, for example, **system** or **vendor**.| | Component information (group node)| component| compId | Yes| Component ID, which must be unique.| @@ -180,8 +177,7 @@ To implement an OTA update, you first need to use the packaging tool to pack the - -**nl2**: non-standard system mode -##### Standard System - +#### Standard System 1. Create the **target\_package** folder with the following directory structure: @@ -189,7 +185,7 @@ To implement an OTA update, you first need to use the packaging tool to pack the target_package ├── {component_1} ├── {component_2} - ├── ... + ├── ...... ├── {component_N} └── updater_config ├── BOARD.list @@ -197,7 +193,11 @@ To implement an OTA update, you first need to use the packaging tool to pack the └── updater_specified_config.xml ``` -2. Place the components to be updated, including the image file (for example, **system.img**), as **{component\_N}** in the root directory of the **target\_package** folder. +2. Place the components to be updated, including their image file and **updater_binary** file, as the substitute of **{component\_N}** in the root directory of the **target\_package** folder. + + Take RK3568 as an example. Place the image file in the following build directory: out/rk3568/packages/phone/images/. + + Place the **updater_binary** file in the following build directory: out/rk3568/packages/phone/system/bin/. 3. Configure the component configuration file in the **updater\_config** folder. @@ -208,9 +208,11 @@ To implement an OTA update, you first need to use the packaging tool to pack the ```text HI3516 - HI3518 + RK3568 ``` + Vendors can configure **Local BoardId** by calling **GetLocalBoardId()** in the **base/updater/updater/utils/utils.cpp** file. Ensure that **Local BoardId** configured in the **utils.cpp** file is present in **BOARD.list**. Otherwise, the update cannot be performed. + 5. Configure the versions supported by the current update package in **VERSION.mbn** in the **updater\_config** folder. Version number format: Hi3516DV300-eng 10 QP1A.XXXXXX.{Major version number (6 digits)}.XXX{Minor version number (3 digits)} @@ -223,10 +225,11 @@ To implement an OTA update, you first need to use the packaging tool to pack the ```text Hi3516DV300-eng 10 QP1A.190711.001 Hi3516DV300-eng 10 QP1A.190711.020 - Hi3518DV300-eng 10 QP1A.190711.021 ``` -6. For an update using the incremental (differential) package, also prepare a source version package (source\_package) in the same format as the target version package (target\_package), and then compress it as a **.zip** file, that is, **source\_package.zip**. + Ensure that the basic version number is contained in **VERSION.mbn**. + +6. For update using an incremental (differential) package, also prepare a source version package (source\_package) in the same format as the target version package (target\_package), and then compress it as a **.zip** file, that is, **source\_package.zip**. 7. If you create an update package with partitions changed, also provide the partition table file named **partition\_file.xml**. You can specify the file using the **-pf** parameter. For details about the configuration nodes, see the description below. @@ -243,8 +246,8 @@ To implement an OTA update, you first need to use the packaging tool to pack the **Table 2** Description of labels in the partition table - | Name | Description | - | ---- | ----------- | + | Name| Description| + | -------- | -------- | | Sel | Whether the partition is effective. The value **1** indicates that the partition is effective, and value **0** indicates the opposite.| | PartitionName | Partition name, for example, **fastboot** or **boot**.| | FlashType | Flash type, for example, **emmc** or **ufs**.| @@ -266,7 +269,7 @@ To implement an OTA update, you first need to use the packaging tool to pack the - **./target\_package/**: path of **target\_package** - **./output\_package/**: output path of the update package - - -**pk ./rsa\_private\_key3072.pem**: path of the private key file + - -**pk ./rsa\_private\_key2048.pem**: path of the private key file **Incremental (differential) package** @@ -280,7 +283,7 @@ To implement an OTA update, you first need to use the packaging tool to pack the - **./target\_package/**: path of **target\_package** - **./output\_package/**: output path of the update package - -**s ./source\_package.zip**: path of the **source\_package.zip** file. For update using a differential package, use the **-s** parameter to specify the source version package. - - -**pk ./rsa\_private\_key3072.pem**: path of the private key file + - -**pk ./rsa\_private\_key2048.pem**: path of the private key file **Update package with partitions changed** @@ -291,42 +294,42 @@ To implement an OTA update, you first need to use the packaging tool to pack the python build_update.py ./target_package/ ./output_package/ -pk ./rsa_private_key2048.pem -pf ./partition_file.xml ``` - - **./target\_package/**: path of **target_package** + - **./target\_package/**: path of **target\_package** - **./output\_package/**: output path of the update package - - -**pk ./rsa\_private_key3072.pem**: path of the private key file + - -**pk ./rsa\_private\_key2048.pem**: path of the private key file - -**pf ./partition\_file.xml**: path of the partition table file -#### **Uploading the Update Package** +### Uploading the Update Package Upload the update package to the vendor's OTA server. -#### **Downloading the Update Package** +### **Downloading the Update Package** 1. Download the update package from the OTA server. -2. (Optional) Insert an SD card (with a capacity greater than 100 MB) if the device is developed based on Hi3518E V300 or Hi3516D V300. +2. (Optional) Insert an SD card (with a capacity greater than 100 MB) if the device is developed based on Hi3516D V300. -#### Integrating OTA Update Capabilities +### Integrating OTA Update Capabilities 1. For mini and small systems - - Call the dynamic library **libhota.so**. The corresponding header files **hota\_partition.h** and **hota\_updater.h** are located in **base\update\sys_installer_lite\interfaces\kits**. - - The **libhota.so** source code is located in **base\update\sys_installer_lite\frameworks\source**. + - Call the dynamic library **libhota.so**. The corresponding header files **hota\_partition.h** and **hota\_updater.h** are located in **base\update\sys\_installer\_lite\interfaces\kits\**. + - The **libhota.so** source code is located in **base\update\sys\_installer\_lite\frameworks\source**. - For details about how to use APIs, see *API Application Scenarios* and update APIs in *API Reference*. - - If the development board needs to be adapted, see the **base\update\sys_installer_lite\hals\hal\_hota\_board.h** file. + - If the development board needs to be adapted, see the **base\update\sys\_installer\_lite\hals\hal\_hota\_board.h** file. 2. For the standard system, see the [JS API Reference](../../application-dev/reference/apis/js-apis-update.md) for details. -##### API Application Scenario (Default) +#### API Application Scenario (Default) The update package is generated by following the instructions provided in Generating a Public/Private Key Pair and Generating an Update Package. -###### How to Develop +##### How to Develop 1. Download the update package for the current device, and then call the **HotaInit** function to initialize the OTA module. @@ -335,7 +338,7 @@ The update package is generated by following the instructions provided in Genera 3. Call the **HotaRestart** function to restart the system for the update to take effect. Call the **HotaCancel** function if you want to cancel the update. -###### Sample Code +##### Sample Code Perform an OTA update using the update package format and verification method provided by OpenHarmony. @@ -390,12 +393,12 @@ int main(int argc, char **argv) ``` -##### API Application Scenario (Custom) +#### API Application Scenario (Custom) The update package is generated in other ways instead of following the instructions provided in "Generating a Public/Private Key Pair" and "Generating an Update Package." -###### How to Develop +##### **How to Develop** 1. Download the update package for the current device, and then call the **HotaInit** function to initialize the OTA module. @@ -410,7 +413,7 @@ The update package is generated in other ways instead of following the instructi 6. Call the **HotaRestart** function to restart the system for the update to take effect. Call the **HotaCancel** function if you want to cancel the update. -###### Sample Code +##### Sample Code Perform an OTA update using the update package format and verification method not provided by OpenHarmony. @@ -482,11 +485,11 @@ int main(int argc, char **argv) ``` -###### System Updating +##### Upgrading the System An application calls APIs of the OTA module to perform functions such as signature verification of the update package, anti-rollback, as well as burning and flushing to disk. After the update is complete, the system automatically restarts. -For the mini and small systems that use the Hi3518E V300 or Hi3516D V300 open source suite, add the value of **LOCAL\_VERSION** to the version that needs to implement the anti-rollback function. For example, for **"ohos default 1.0"->"ohos default 1.1"**, add the value of **LOCAL\_VERSION** in **device\hisilicon\third\_party\uboot\u-boot-2020.01\product\hiupdate\ota\_update\ota\_local_info.c**. +For the mini and small systems that use the Hi3516D V300 open source suite, add the value of **LOCAL\_VERSION** to the version that needs to implement the anti-rollback function. For example, for **"ohos default 1.0"->"ohos default 1.1"**, add the value of **LOCAL\_VERSION** in **device\hisilicon\third\_party\uboot\u-boot-2020.01\product\hiupdate\ota\_update\ota\_local_info.c**. Example configuration: @@ -494,16 +497,15 @@ For the mini and small systems that use the Hi3518E V300 or Hi3516D V300 open so const char *get_local_version(void) { #if defined(CONFIG_TARGET_HI3516EV200) || \ - defined(CONFIG_TARGET_HI3516DV300) || \ - defined(CONFIG_TARGET_HI3518EV300) + defined(CONFIG_TARGET_HI3516DV300) #define LOCAL_VERSION "ohos default 1.0" /* increase: default release version */ ``` -##### A/B Update Scenario +#### A/B Update Scenario -###### Development Procedure +##### How to Develop 1. Download the update package through the update application. 2. Invoke update_service to start the system installation service through SAMGR. @@ -511,7 +513,7 @@ const char *get_local_version(void) 4. Activate the new version upon restarting. -###### How to Develop +##### How to Develop - Invoke update_service to call JS APIs to implement the related service logic in an A/B update. @@ -530,7 +532,7 @@ const char *get_local_version(void) 1. Start the system installation service and set up an IPC connection. ```cpp - int SysInstallerInit(void * callback) + int SysInstallerInit(void* callback) ``` 2. Install the A/B update package in the specified path. @@ -540,7 +542,7 @@ const char *get_local_version(void) 3. Set the update progress callback. ```cpp - int SetUpdateProgressCallback(void * callback) + int SetUpdateProgressCallback(void* callback) ``` 4. Obtain the installation status of the update package (0: not started; 1: installing; 2: installed). @@ -572,7 +574,7 @@ const char *get_local_version(void) ``` -###### FAQs +##### FAQs 1. An exception occurs during installation of the update package.
The system keeps running with the current version. It will attempt a new update in the next package search period. @@ -581,6 +583,6 @@ const char *get_local_version(void)
Perform a rollback and set the partition to the **unbootable** state so that the system does not boot from this partition. -###### Verification +##### Verification In normal cases, the device can download the update package from the OTA server in the background, perform a silent update, and then restart according to the preconfigured activation policy for the new version to take effect. diff --git a/en/device-dev/subsystems/subsys-testguide-test.md b/en/device-dev/subsystems/subsys-testguide-test.md deleted file mode 100644 index 40d4905715e2a555ef38f298fec411cd5cb82e3a..0000000000000000000000000000000000000000 --- a/en/device-dev/subsystems/subsys-testguide-test.md +++ /dev/null @@ -1,930 +0,0 @@ -# Test -OpenHarmony provides a comprehensive auto-test framework for designing test cases. Detecting defects in the development process can improve code quality. - -This document describes how to use the OpenHarmony test framework. -## Setting Up the Environment -- The test framework depends on Python. Before using the test framework, you need to set up the environment. -- For details about how to obtain the source code, see [Obtaining Source Code](../get-code/sourcecode-acquire.md). -### Environment Configuration -#### Basic Test Framework Environment - -|Environment|Version|Description| -|------------|------------|------------| -|Operating system|Ubuntu 18.04 or later|Provides the build environment.| -|Linux extend component|libreadline-dev|Allows users to edit command lines.| -|Python|3.7.5 or later|Provides the programming language for the test framework.| -|Python Plug-ins|pyserial 3.3 or later
paramiko 2.7.1 or later
setuptools 40.8.0 or later
RSA 4.0 or later|- pyserial: supports serial port communication in Python.
- paramiko: allows SSH in Python.
- setuptools: allows creation and distribution of Python packages.
-RSA: implements RSA encryption in Python.| -|NFS Server|haneWIN NFS Server 1.2.50 or later or NFS v4 or later|Allows devices to be connected over a serial port.| -|HDC|1.1.0 or later|Allows devices to be connected by using the OpenHarmony Device Connector (HDC).| - - -#### Installation Process - -1. Run the following command to install the Linux extended component libreadline: - ``` - sudo apt-get install libreadline-dev - ``` - The installation is successful if the following information is displayed: - ``` - Reading package lists... Done - Building dependency tree - Reading state information... Done - libreadline-dev is already the newest version (7.0-3). - 0 upgraded, 0 newly installed, 0 to remove and 11 not upgraded. - ``` -2. Run the following command to install the setuptools plug-in: - ``` - pip3 install setuptools - ``` - The installation is successful if the following information is displayed: - ``` - Requirement already satisfied: setuptools in d:\programs\python37\lib\site-packages (41.2.0) - ``` -3. Run the following command to install the paramiko plug-in: - ``` - pip3 install paramiko - ``` - The installation is successful if the following information is displayed: - ``` - Installing collected packages: pycparser, cffi, pynacl, bcrypt, cryptography, paramiko - Successfully installed bcrypt-3.2.0 cffi-1.14.4 cryptography-3.3.1 paramiko-2.7.2 pycparser-2.20 pynacl-1.4.0 - ``` -4. Run the following command to install the rsa plug-in: - ``` - pip3 install rsa - ``` - The installation is successful if the following information is displayed: - ``` - Installing collected packages: pyasn1, rsa - Successfully installed pyasn1-0.4.8 rsa-4.7 - ``` -5. Run the following command to install the pyserial plug-in: - ``` - pip3 install pyserial - ``` - The installation is successful if the following information is displayed: - ``` - Requirement already satisfied: pyserial in d:\programs\python37\lib\site-packages\pyserial-3.4-py3.7.egg (3.4) - ``` -6. Install the NFS server if the device outputs results only through the serial port. - - For Windows, install, for example, haneWIN NFS Server 1.2.50. - - For Linux, run the following command to install the NFS server: - ``` - sudo apt install nfs-kernel-server - ``` - The installation is successful if the following information is displayed: - ``` - Reading package lists... Done - Building dependency tree - Reading state information... Done - nfs-kernel-server is already the newest version (1:1.3.4-2.1ubuntu5.3). - 0 upgraded, 0 newly installed, 0 to remove and 11 not upgraded. - ``` -7. Install the HDC tool if the device supports HDC connections. - - For details, see https://gitee.com/openharmony/developtools_hdc_standard/blob/master/README.md - -## Checking the Installation Environment - -| Check Item|Operation |Requirements | -| --- | --- | --- | -| Check whether Python is installed successfully.|Run the **python --version** command. |The Python version is 3.7.5 or later.| -| Check whether Python plug-ins are successfully installed.|Go to the **test/developertest** directory and run **start.bat** or **start.sh**.| The **>>>** prompt is displayed.| -|Check the NFS server status (for the devices that support only serial port output). |Log in to the development board through the serial port and run the **mount** command to mount the NFS. |The file directory can be mounted. | -|Check whether the HDC is successfully installed. |Run the **hdc_std -v** command.|The HDC version is 1.1.0 or later.| - - - -## Directory Structure -The directory structure of the test framework is as follows: -``` -test # Test subsystem -├── developertest # Developer test module -│ ├── aw # Static library of the test framework -│ ├── config # Test framework configuration -│ │ │ ... -│ │ └── user_config.xml # User configuration -│ ├── examples # Examples of test cases -│ ├── src # Source code of the test framework -│ ├── third_party # Adaptation code for third-party components on which the test framework depends -│ ├── reports # Test reports -│ ├── BUILD.gn # Build entry of the test framework -│ ├── start.bat # Test entry for Windows -│ └── start.sh # Test entry for Linux -└── xdevice # Modules on which the test framework depends -``` -## Writing Test Cases -### Designing the Test Case Directory -Design the test case directory as follows: -``` -subsystem # Subsystem -├── partA # Part A -│ ├── moduleA # Module A -│ │ ├── include -│ │ ├── src # Service code -│ │ └── test # Test directory -│ │ ├── unittest # Unit test -│ │ │ ├── common # Common test cases -│ │ │ │ ├── BUILD.gn # Build file of test cases -│ │ │ │ └── testA_test.cpp # Source code of unit test cases -│ │ │ ├── phone # Test cases for mobile phones -│ │ │ ├── ivi # Test cases for head units -│ │ │ └── liteos-a # Test cases for IP cameras using LiteOS -│ │ ├── moduletest # Module test -│ │ ... -│ │ -│ ├── moduleB # Module B -│ ├── test -│ │ └── resource # Dependency resources -│ │ ├── moduleA # Module A -│ │ │ ├── ohos_test.xml # Resource configuration file -│ │ ... └── 1.txt # Resource file -│ │ -│ ├── ohos_build # Build entry configuration -│ ... -│ -... -``` -> **CAUTION**
Test cases are classified into common test cases and device-specific test cases. You are advised to place common test cases in the **common** directory and device-specific test cases in the directories of the related devices. - -### Writing Test Cases -This test framework supports test cases written in multiple programming languages and provides different templates for different languages. - -**C++ Test Case Example** - -- Naming rules for source files - - The source file name of test cases must be the same as that of the test suite. The file names must use lowercase letters and in the [Function]\_[Sub-function]\_**test** format. More specific sub-functions can be added as required. -Example: - ``` - calculator_sub_test.cpp - ``` - -- Test case example - ``` - /* - * Copyright (c) 2022 XXXX Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - #include "calculator.h" - #include - - using namespace testing::ext; - - class CalculatorSubTest : public testing::Test { - public: - static void SetUpTestCase(void); - static void TearDownTestCase(void); - void SetUp(); - void TearDown(); - }; - - void CalculatorSubTest::SetUpTestCase(void) - { - // Set a setup function, which will be called before all test cases. - } - - void CalculatorSubTest::TearDownTestCase(void) - { - // Set a teardown function, which will be called after all test cases. - } - - void CalculatorSubTest::SetUp(void) - { - // Set a setup function, which will be called before each test case. - } - - void CalculatorSubTest::TearDown(void) - { - // Set a teardown function, which will be called after each test case. - } - - /** - * @tc.name: integer_sub_001 - * @tc.desc: Verify the sub function. - * @tc.type: FUNC - * @tc.require: Issue Number - */ - HWTEST_F(CalculatorSubTest, integer_sub_001, TestSize.Level1) - { - // Step 1 Call the function to obtain the result. - int actual = Sub(4, 0); - - // Step 2 Use an assertion to compare the obtained result with the expected result. - EXPECT_EQ(4, actual); - } - ``` - The procedure is as follows: - 1. Add comment information to the test case file header. - ``` - /* - * Copyright (c) 2022 XXXX Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - ``` - 2. Add the test framework header file and namespace. - ``` - #include - - using namespace testing::ext; - ``` - 3. Add the header file of the test class. - ``` - #include "calculator.h" - ``` - 4. Define the test suite (test class). - ``` - class CalculatorSubTest : public testing::Test { - public: - static void SetUpTestCase(void); - static void TearDownTestCase(void); - void SetUp(); - void TearDown(); - }; - - void CalculatorSubTest::SetUpTestCase(void) - { - // Set a setup function, which will be called before all test cases. - } - - void CalculatorSubTest::TearDownTestCase(void) - { - // Set a teardown function, which will be called after all test cases. - } - - void CalculatorSubTest::SetUp(void) - { - // Set a setup function, which will be called before each test case. - } - - void CalculatorSubTest::TearDown(void) - { - // Set a teardown function, which will be called after each test case. - } - ``` - > **CAUTION**:
When defining a test suite, ensure that the test suite name is the same as the target to build and uses the upper camel case style. - - 5. Add implementation of the test cases, including test case comments and logic. - ``` - /** - * @tc.name: integer_sub_001 - * @tc.desc: Verify the sub function. - * @tc.type: FUNC - * @tc.require: Issue Number - */ - HWTEST_F(CalculatorSubTest, integer_sub_001, TestSize.Level1) - { - // Step 1 Call the function to obtain the test result. - int actual = Sub(4, 0); - - // Step 2 Use an assertion to compare the obtained result with the expected result. - EXPECT_EQ(4, actual); - } - ``` - The following test case templates are provided for your reference. - - | Type| Description| - | ------------| ------------| - | HWTEST(A,B,C)| Use this template if the test case execution does not depend on setup or teardown.| - | HWTEST_F(A,B,C)| Use this template if the test case execution (excluding parameters) depends on setup and teardown.| - | HWTEST_P(A,B,C)| Use this template if the test case execution (including parameters) depends on setup and teardown.| - - In the template names: - - *A* indicates the test suite name. - - *B* indicates the test case name, which is in the *Function*\_*No.* format. The *No.* is a three-digit number starting from **001**. - - *C* indicates the test case level. There are five test case levels: guard-control level 0 and non-guard-control level 1 to level 4. Of levels 1 to 4, a smaller value indicates a more important function verified by the test case. - - **CAUTION**
- - The expected result of each test case must have an assertion. - - The test case level must be specified. - - It is recommended that the test be implemented step by step according to the template. - - The comment must contain the test case name, description, type, and requirement number, which are in the @tc.*xxx*: *value* format. The test case type @**tc.type** can be any of the following: - - | Test Case Type|Code| - | ------------|------------| - |Function test |FUNC| - |Performance Test |PERF| - |Reliability test |RELI| - |Security test |SECU| - |Fuzzing |FUZZ| - - -**JavaScript Test Case Example** - -- Naming rules for source files - - The source file name of a test case must be in the [Function]\[Sub-function]Test format, and each part must use the upper camel case style. More specific sub-functions can be added as required. -Example: - ``` - AppInfoTest.js - ``` - -- Test case example - ``` - /* - * Copyright (C) 2022 XXXX Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - import app from '@system.app' - - import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index' - - describe("AppInfoTest", function () { - beforeAll(function() { - // Set a setup function, which will be called before all test cases. - console.info('beforeAll called') - }) - - afterAll(function() { - // Set a teardown function, which will be called after all test cases. - console.info('afterAll called') - }) - - beforeEach(function() { - // Set a setup function, which will be called before each test case. - console.info('beforeEach called') - }) - - afterEach(function() { - // Set a teardown function, which will be called after each test case. - console.info('afterEach called') - }) - - /* - * @tc.name:appInfoTest001 - * @tc.desc:verify app info is not null - * @tc.type: FUNC - * @tc.require: Issue Number - */ - it("appInfoTest001", 0, function () { - // Step 1 Call the function to obtain the test result. - var info = app.getInfo() - - // Step 2 Use an assertion to compare the obtained result with the expected result. - expect(info != null).assertEqual(true) - }) - }) - ``` - The procedure is as follows: - 1. Add comment information to the test case file header. - ``` - /* - * Copyright (C) 2022 XXXX Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - ``` - 2. Import the APIs and JSUnit test library to test. - ``` - import app from '@system.app' - - import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index' - ``` - 3. Define the test suite (test class). - ``` - describe("AppInfoTest", function () { - beforeAll(function() { - // Set a setup function, which will be called before all test cases. - console.info('beforeAll called') - }) - - afterAll(function() { - // Set a teardown function, which will be called after all test cases. - console.info('afterAll called') - }) - - beforeEach(function() { - // Set a setup function, which will be called before each test case. - console.info('beforeEach called') - }) - - afterEach(function() { - // Set a teardown function, which will be called after each test case. - console.info('afterEach called') - }) - ``` - 4. Add implementation of the test cases. - ``` - /* - * @tc.name:appInfoTest001 - * @tc.desc:verify app info is not null - * @tc.type: FUNC - * @tc.require: Issue Number - */ - it("appInfoTest001", 0, function () { - // Step 1 Call the function to obtain the test result. - var info = app.getInfo() - - // Step 2 Use an assertion to compare the obtained result with the expected result. - expect(info != null).assertEqual(true) - }) - ``` - -### Writing the Build File for Test Cases -When a test case is executed, the test framework searches for the build file of the test case in the test case directory and builds the test case located. The following describes how to write build files (GN files) in different programming languages. - -#### Writing Build Files for Test Cases -The following provides templates for different languages for your reference. - -- **Test case build file example (C++)** - ``` - # Copyright (c) 2022 XXXX Device Co., Ltd. - - import("//build/test.gni") - - module_output_path = "subsystem_examples/calculator" - - config("module_private_config") { - visibility = [ ":*" ] - - include_dirs = [ "../../../include" ] - } - - ohos_unittest("CalculatorSubTest") { - module_out_path = module_output_path - - sources = [ - "../../../include/calculator.h", - "../../../src/calculator.cpp", - ] - - sources += [ "calculator_sub_test.cpp" ] - - configs = [ ":module_private_config" ] - - deps = [ "//third_party/googletest:gtest_main" ] - } - - group("unittest") { - testonly = true - deps = [":CalculatorSubTest"] - } - ``` - The procedure is as follows: - - 1. Add comment information for the file header. - ``` - # Copyright (c) 2022 XXXX Device Co., Ltd. - ``` - 2. Import the build template. - ``` - import("//build/test.gni") - ``` - 3. Specify the file output path. - ``` - module_output_path = "subsystem_examples/calculator" - ``` - > **NOTE**
The output path is ***Part_name*/*Module_name***. - - 4. Configure the directories for dependencies. - - ``` - config("module_private_config") { - visibility = [ ":*" ] - - include_dirs = [ "../../../include" ] - } - ``` - > **NOTE**
Generally, the dependency directories are configured here and directly referenced in the build script of the test case. - - 5. Set the output build file for the test cases. - - ``` - ohos_unittest("CalculatorSubTest") { - } - ``` - 6. Write the build script (add the source file, configuration, and dependencies) for the test cases. - ``` - ohos_unittest("CalculatorSubTest") { - module_out_path = module_output_path - sources = [ - "../../../include/calculator.h", - "../../../src/calculator.cpp", - "../../../test/calculator_sub_test.cpp" - ] - sources += [ "calculator_sub_test.cpp" ] - configs = [ ":module_private_config" ] - deps = [ "//third_party/googletest:gtest_main" ] - } - ``` - - > **NOTE**
Set the test type based on actual requirements. The following test types are available: - > - **ohos_unittest**: unit test - > - **ohos_moduletest**: module test - > - **ohos_systemtest**: system test - > - **ohos_performancetest**: performance test - > - **ohos_securitytest**: security test - > - **ohos_reliabilitytest**: reliability test - > - **ohos_distributedtest**: distributed test - - 7. Group the test case files by test type. - - ``` - group("unittest") { - testonly = true - deps = [":CalculatorSubTest"] - } - ``` - > **NOTE**
Grouping test cases by test type allows you to execute a specific type of test cases when required. - -- Test case build file example (JavaScript) - - ``` - # Copyright (C) 2022 XXXX Device Co., Ltd. - - import("//build/test.gni") - - module_output_path = "subsystem_examples/app_info" - - ohos_js_unittest("GetAppInfoJsTest") { - module_out_path = module_output_path - - hap_profile = "./config.json" - certificate_profile = "//test/developertest/signature/openharmony_sx.p7b" - } - - group("unittest") { - testonly = true - deps = [ ":GetAppInfoJsTest" ] - } - ``` - - The procedure is as follows: - - 1. Add comment information for the file header. - - ``` - # Copyright (C) 2022 XXXX Device Co., Ltd. - ``` - 2. Import the build template. - - ``` - import("//build/test.gni") - ``` - 3. Specify the file output path. - - ``` - module_output_path = "subsystem_examples/app_info" - ``` - > **NOTE**
The output path is ***Part_name*/*Module_name***. - - 4. Set the output build file for the test cases. - - ``` - ohos_js_unittest("GetAppInfoJsTest") { - } - ``` - > **NOTE**
- >- Use the **ohos\_js\_unittest** template to define the JavaScript test suite. Pay attention to the difference between JavaScript and C++. - >- The file generated for the JavaScript test suite must be in .hap format and named after the test suite name defined here. The test suite name must end with **JsTest**. - - 5. Configure the **config.json** file and signature file, which are mandatory. - - ``` - ohos_js_unittest("GetAppInfoJsTest") { - module_out_path = module_output_path - - hap_profile = "./config.json" - certificate_profile = "//test/developertest/signature/openharmony_sx.p7b" - } - ``` - **config.json** is the configuration file required for HAP build. You need to set **target** based on the tested SDK version. Default values can be retained for other items. The following is an example: - - ``` - { - "app": { - "bundleName": "com.example.myapplication", - "vendor": "example", - "version": { - "code": 1, - "name": "1.0" - }, - "apiVersion": { - "compatible": 4, - "target": 5 // Set it based on the tested SDK version. In this example, SDK5 is used. - } - }, - "deviceConfig": {}, - "module": { - "package": "com.example.myapplication", - "name": ".MyApplication", - "deviceType": [ - "phone" - ], - "distro": { - "deliveryWithInstall": true, - "moduleName": "entry", - "moduleType": "entry" - }, - "abilities": [ - { - "skills": [ - { - "entities": [ - "entity.system.home" - ], - "actions": [ - "action.system.home" - ] - } - ], - "name": "com.example.myapplication.MainAbility", - "icon": "$media:icon", - "description": "$string:mainability_description", - "label": "MyApplication", - "type": "page", - "launchType": "standard" - } - ], - "js": [ - { - "pages": [ - "pages/index/index" - ], - "name": "default", - "window": { - "designWidth": 720, - "autoDesignWidth": false - } - } - ] - } - } - ``` - 6. Group the test case files by test type. - ``` - group("unittest") { - testonly = true - deps = [ ":GetAppInfoJsTest" ] - } - ``` - > **NOTE**
Grouping test cases by test type allows you to execute a specific type of test cases when required. - -#### Configuring ohos.build - -Configure the part build file to associate with specific test cases. -``` -"partA": { - "module_list": [ - - ], - "inner_list": [ - - ], - "system_kits": [ - - ], - "test_list": [ - "//system/subsystem/partA/calculator/test:unittest" // Configure test under calculator. - ] - } -``` -> **NOTE**
**test_list** contains the test cases of the corresponding module. - -### Configuring Test Case Resources -Test case resources include external file resources, such as image files, video files, and third-party libraries, required for test case execution. - -Perform the following steps: -1. Create the **resource** directory in the **test** directory of the part, and create a directory for the module in the **resource** directory to store resource files of the module. - -2. In the module directory under **resource**, create the **ohos_test.xml** file in the following format: - ``` - - - - - - - - ``` -3. In the build file of the test cases, configure **resource_config_file** to point to the resource file **ohos_test.xml**. - ``` - ohos_unittest("CalculatorSubTest") { - resource_config_file = "//system/subsystem/partA/test/resource/calculator/ohos_test.xml" - } - ``` - >**NOTE** - >- **target_name** indicates the test suite name defined in the **BUILD.gn** file in the **test** directory. - >- **preparer** indicates the action to perform before the test suite is executed. - >- **src="res"** indicates that the test resources are in the **resource** directory under the **test** directory. - >- **src="out"** indicates that the test resources are in the **out/release/$(*part*)** directory. - -## Executing Test Cases -Before executing test cases, you need to modify the configuration based on the device used. - -### Modifying user_config.xml -``` - - - - false - - false - - true - - - - - - - - - - - - - cmd - 115200 - 8 - 1 - 1 - - - - - - - - - - - - - - - - - - -``` ->**NOTE**
If HDC is connected to the device before the test cases are executed, you only need to configure the device IP address and port number, and retain the default settings for other parameters. - -### Executing Test Cases on Windows -#### Building Test Cases - -Test cases cannot be built on Windows. You need to run the following command to build test cases on Linux: -``` -./build.sh --product-name hispark_taurus_standard --build-target make_test -``` ->**NOTE** -> ->- **product-name**: specifies the name of the product to build, for example, **hispark_taurus_standard**. ->- **build-target**: specifies the test case to build. **make_test** indicates all test cases. You can specify the test cases based on requirements. - -When the build is complete, the test cases are automatically saved in **out/hispark_taurus/packages/phone/tests**. - -#### Setting Up the Execution Environment -1. On Windows, create the **Test** directory in the test framework and then create the **testcase** directory in the **Test** directory. - -2. Copy **developertest** and **xdevice** from the Linux environment to the **Test** directory on Windows, and copy the test cases to the **testcase** directory. - - >**NOTE**
Port the test framework and test cases from the Linux environment to the Windows environment for subsequent execution. - -3. Modify the **user_config.xml** file. - ``` - - - false - - - - D:\Test\testcase\tests - - ``` - >**NOTE**
**** indicates whether to build test cases. **** indicates the path for searching for test cases. - -#### Executing Test Cases -1. Start the test framework. - ``` - start.bat - ``` -2. Select the product. - - After the test framework starts, you are asked to select a product. Select the development board to test, for example, **Hi3516DV300**. - -3. Execute test cases. - - Run the following command to execute test cases: - ``` - run -t UT -ts CalculatorSubTest -tc integer_sub_00l - ``` - In the command: - ``` - -t [TESTTYPE]: specifies the test case type, which can be UT, MST, ST, or PERF. This parameter is mandatory. - -tp [TESTPART]: specifies the part to test. This parameter can be used independently. - -tm [TESTMODULE]: specifies the module to test. This parameter must be specified together with -tp. - -ts [TESTSUITE]: specifies the test suite. This parameter can be used independently. - -tc [TESTCASE]: specifies the test case. This parameter must be specified together with -ts. - You can run h to display help information. - ``` -### Executing Test Cases on Linux -#### Mapping the Remote Port -To enable test cases to be executed on a remote Linux server or a Linux VM, map the port to enable communication between the device and the remote server or VM. Configure port mapping as follows: -1. On the HDC server, run the following commands: - ``` - hdc_std kill - hdc_std -m -s 0.0.0.0:8710 - ``` - >**NOTE**
The IP address and port number are default values. - -2. On the HDC client, run the following command: - ``` - hdc_std -s xx.xx.xx.xx:8710 list targets - ``` - >**NOTE**
Enter the IP address of the device to test. - -#### Executing Test Cases -1. Start the test framework. - ``` - ./start.sh - ``` -2. Select the product. - - After the test framework starts, you are asked to select a product. Select the development board to test, for example, **Hi3516DV300**. - -3. Execute test cases. - - The test framework locates the test cases based on the command, and automatically builds and executes the test cases. - ``` - run -t UT -ts CalculatorSubTest -tc integer_sub_00l - ``` - In the command: - ``` - -t [TESTTYPE]: specifies the test case type, which can be UT, MST, ST, or PERF. This parameter is mandatory. - -tp [TESTPART]: specifies the part to test. This parameter can be used independently. - -tm [TESTMODULE]: specifies the module to test. This parameter must be specified together with -tp. - -ts [TESTSUITE]: specifies the test suite. This parameter can be used independently. - -tc [TESTCASE]: specifies the test case. This parameter must be specified together with -ts. - You can run h to display help information. - ``` - -## Viewing the Test Report -After the test cases are executed, the test result will be automatically generated. You can view the detailed test result in the related directory. - -### Test Result -You can obtain the test result in the following directory: -``` -test/developertest/reports/xxxx_xx_xx_xx_xx_xx -``` ->**NOTE**
The folder for test reports is automatically generated. - -The folder contains the following files: -| Type| Description| -| ------------ | ------------ | -| result/ |Test cases in standard format.| -| log/plan_log_xxxx_xx_xx_xx_xx_xx.log | Test case logs.| -| summary_report.html | Test report summary.| -| details_report.html | Detailed test report.| - -### Test Framework Logs -``` -reports/platform_log_xxxx_xx_xx_xx_xx_xx.log -``` - -### Latest Test Report -``` -reports/latest -``` diff --git a/en/device-dev/subsystems/subsys-toolchain-hdc-guide.md b/en/device-dev/subsystems/subsys-toolchain-hdc-guide.md index 6a911f7a29b1e21fb73b2c14ed770f32bccb0331..504b52a3a4774a88fdc4db8c6dcc4d53999c9761 100644 --- a/en/device-dev/subsystems/subsys-toolchain-hdc-guide.md +++ b/en/device-dev/subsystems/subsys-toolchain-hdc-guide.md @@ -1,26 +1,30 @@ -# hdc_std +# hdc -OpenHarmony Device Connector (hdc_std) is a command-line tool used for debugging. You can use it on a Windows, Linux, or macOS system to interact with real devices or simulators. +OpenHarmony Device Connector (hdc) is a command-line tool used for debugging. You can use it on a Windows, Linux, or macOS system to interact with real devices or simulators. -The following describes how to obtain and use hdc_std. +The following describes how to obtain and use hdc. ## How to Obtain -Obtain hdc_std from the **toolchains** directory of the OpenHarmony SDK. +**Obtaining hdc** + +Obtain hdc from the **toolchains** directory of the OpenHarmony SDK. **Example** -If you use hdc_std on Windows, obtain the SDK for Windows and copy **hdc_std.exe** from **toolchains** to a directory on the disk. +The following uses the Windows operating system as an example: + +Obtain the SDK for Windows, and copy **hdc.exe** from **toolchains** to a directory on the disk. ## NOTICE -- If an exception occurs when you are using hdc_std, run the **hdc_std kill** command to terminate the hdc_std service or run the **hdc_std start -r** command to restart the service process. +- If an exception occurs when you are using hdc, run the **hdc kill** command to terminate the hdc service or run the **hdc start -r** command to restart the service process. -- If no device information is obtained after **hdc_std list targets** is executed, check whether the hdc_std process exists in the **Task Manager**. If yes, terminate it. +- If no device information is obtained after **hdc list targets** is executed, check whether the hdc process exists in the **Task Manager**. If yes, terminate the process. ## Option-related Commands @@ -29,30 +33,29 @@ The following commands are available: - **-h/help -v/version** - - Displays hdc_std help or version information. - + Displays hdc help or version information. + **Table 1** Command description - | **Return Value**| **Description**| + + | Return Value| Description| | -------- | -------- | - | Required information| hdc_std help or version information.| + | Required information| hdc help or version information.| Example: - + + ``` - hdc_std -h / hdc_std help + hdc -h / hdc help ``` - + + ``` - hdc_std -v / hdc_std version + hdc -v / hdc version ``` - - - **-l 0-5** - Sets the levels of the logs generated during the running of the tool. The default value is **LOG_INFO**. - + **Table 2** Command description | Parameter| Description| @@ -65,38 +68,36 @@ The following commands are available: | 5 | LOG_ALL | Example: - + ``` - hdc_std -l5 start + hdc -l5 start ``` - **-t key** - Connects to a device based on the specified key. - + **Table 3** Command description | Parameter| Description| | -------- | -------- | | key | Key that identifies the device to connect. The value can be in the *IP address:port number* format or be a USB serial number.| - | **Return Value** | **Description** | - | 1. error: device '\*\*\*' not found
2. Nothing to do ..| 1. The device does not exist.
2. The command appended to **-t key** does not exist.| + | Return Value| Description| + | 1. error: device '\*\*\*' not found
2. Nothing to do ...| 1. The device does not exist.
2. The command appended to **-t key** does not exist.| Example: **-t key** must be used with a command. The following uses **shell** as an example: - **hdc_std list targets** (obtain device information) + **hdc list targets** (Obtain device information.) - **hdc_std -t *key* shell** (replace *key* with the device identifier obtained) + **hdc -t *key* shell** (Replace *key* with the device identifier obtained) > **NOTE**
> You can connect to multiple devices from the device you use for development. Each device has a unique key. The key can be *IP address:port number* for a device to be connected over the network or the serial number for a device to be connected through USB. A specific command must be used with this command. - **checkserver** - Obtains the client and server version information. - + **Table 4** Command description | Return Value| Description| @@ -106,7 +107,7 @@ The following commands are available: Example: ``` - hdc_std checkserver + hdc checkserver ``` @@ -124,18 +125,22 @@ list targets[-v] | Parameter| Description| | -------- | -------- | | -v | Displays detailed device information.| -| **Return Value** | **Description**| +| Return Value| **Description**| | 1. Device information
2. [Empty]| 1. A list of connected devices.
2. No device information is found.| Example: + + ``` -hdc_std list targets +hdc list targets ``` + + ``` -hdc_std list targets -v +hdc list targets -v ``` @@ -143,8 +148,8 @@ hdc_std list targets -v The following commands are available: -- **target mount** +- **target mount** Mounts the **/system** partition in read/write mode. **Table 6** Command description @@ -152,92 +157,82 @@ The following commands are available: | Parameter| Description| | -------- | -------- | | –| –| - | **Return Value**| **Description**| + | Return Value| **Description**| | 1. Mount finish
2. Error information| 1. The operation is successful.
2. The operation fails.| Example: - + + ``` - hdc_std target mount + hdc target mount ``` - - - - **target boot** - Boots the device. - - Example: - + + Example: + + ``` - hdc_std target boot + hdc target boot ``` - - - **smode [-r]** - Grants the **root** permission to the background hdc service. Use **off** to revoke the granted permissions. - + Example: - + + ``` - hdc_std smode + hdc smode ``` - + + ``` - hdc_std smode -r + hdc smode -r ``` - - - **kill [-r]** - Terminates the hdc process. - + **Table 7** Command description | Parameter| Description| | -------- | -------- | | -r | Restarts the hdc process.| - | **Return Value**| **Description**| + | Return Value| **Description**| | 1. Kill server finish
2. Error information| 1. The operation is successful.
2. The operation fails.| - Example: + Example: ``` - hdc_std kill + hdc kill ``` - **start [-r]** - Starts the hdc process. - - **Table 8** Command description + + **Table 8** Command description | Parameter| Description| | -------- | -------- | | -r | Restarts the hdc process if it has started.| - | **Return Value**| **Description**| + | Return Value| Description| | –| –| - Example: + Example: ``` - hdc_std start + hdc start ``` - - ## Network Commands The following commands are available: - **tconn host[:port][-remove]** - Connects to a device with the specified IP address and port number. **Table 9** Command description @@ -246,19 +241,17 @@ The following commands are available: | -------- | -------- | | host[:port] | IP address and port number for the device to connect.| | -remove | Disconnects from the specified device.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std tconn 192.168.0.100:8710 + hdc tconn 192.168.0.100:8710 ``` - - - **tmode usb** - Restarts the daemon process and connects to the device using USB preferentially. **Table 10** Command description @@ -266,96 +259,85 @@ The following commands are available: | Parameter| Description| | -------- | -------- | | –| –| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std tmode usb + hdc tmode usb ``` - - - **tmode port port-number** - Restarts the daemon process and connects to the device over TCP preferentially. If the TCP connection fails, a USB connection will be initiated. - + **Table 11** Command description | Parameter| Description| | -------- | -------- | | port-number | Port used to connect to the device.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std tmode port 8710 + hdc tmode port 8710 ``` - - > **NOTE**
> After this command is executed, the remote daemon process exits and restarts, and a TCP connection is set up by default. If the port number is not specified in the command, a random port will be used to connect to the device. - **fport localnode remotenode** - Forwards data from a host port to a device port. - + Example: - + + ``` - hdc_std fport tcp:1234 tcp:1080 + hdc fport tcp:1234 tcp:1080 ``` - - - **rport remotenode localnode** - Forwards data from a device port to a host port. - + Example: - + + ``` - hdc_std rport tcp:2080 tcp:2345 + hdc rport tcp:2080 tcp:2345 ``` - - - **fport ls** - Lists all port forwarding tasks. - - **Table 12** Command description + + **Table 12** Command description | Parameter| Description| | -------- | -------- | | –| –| - | **Return Value**| **Description**| + | Return Value| **Description**| | 'tcp:1234 tcp:1080' [Forward] | Forward port forwarding task.| | 'tcp:2080 tcp:2345' [Reverse] | Reverse port forwarding task.| Example: - + + ``` - hdc_std fport ls + hdc fport ls ``` - - - **fport rm** - Deletes a port forwarding task. - + Example: - + + ``` - hdc_std fport rm tcp:1234 tcp:1080 + hdc fport rm tcp:1234 tcp:1080 ``` - - ## File Commands @@ -364,30 +346,27 @@ The following commands are available: - **file send local remote** - Sends a file to a remote device. - - **Table 13** Command description + + **Table 13** Command description | Parameter| Description| | -------- | -------- | | local | Path of the file to send.| | remote | Destination path on the remote device.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. File transfer result| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std file send E:\a.txt /data/local/tmp/a.txt + hdc file send E:\a.txt /data/local/tmp/a.txt ``` - - - **file recv [-a] remote local** - Receives a file from a remote device. - + **Table 14** Command description | Parameter| Description| @@ -395,16 +374,15 @@ The following commands are available: | -a | Retains the file timestamp.| | local | Destination path on the local device.| | remote | File path on the remote device.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std file recv /data/local/tmp/a.txt ./a.txt + hdc file recv /data/local/tmp/a.txt ./a.txt ``` - - ## App Commands @@ -412,48 +390,45 @@ The following commands are available: The following commands are available: -- **install [-r/-d/-g]** *package* - +- **install [-r/-d/-g] *package*** Installs an OpenHarmony app. - - **Table 15** Command description + + **Table 15** Command description + | Parameter| Description| | -------- | -------- | | package | OpenHarmony app installation package.| | -r | Replaces the existing app.| | -d | Allows downgrade installation.| | -g | Grants permissions dynamically.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std install hwadmin.hap + hdc install hwadmin.hap ``` - - - **uninstall [-k] package** - Uninstalls an OpenHarmony app. - - **Table 16** Command description + + **Table 16** Command description | Parameter| Description| | -------- | -------- | | package | OpenHarmony app installation package.| | -k | Retains **/data/cache**.| - | **Return Value**| **Description**| + | Return Value| Description| | 1. Error information
2. –| 1. The operation fails.
2. The operation is successful.| Example: - + + ``` - hdc_std uninstall package + hdc uninstall package ``` - - ## Debugging Commands @@ -462,120 +437,104 @@ The following commands are available: - **hilog** - Obtains logs for debugging. - **Table 17** Command description + **Table 17** Command description | Parameter| Description| | -------- | -------- | | –| –| - | **Return Value**| **Description**| + | Return Value| **Description**| | Log obtained| Log information obtained.| Example: Obtain log information. - + + ``` - hdc_std hilog + hdc hilog ``` - + Clear the cached logs. - + + ``` - hdc_std shell "hilog -r" + hdc shell "hilog -r" ``` - - ​ -- **shell** [*command*] - +- **shell [_command_]** Executes a command remotely or enters an interactive command environment. - - **Table 18** Command description + + **Table 18** Command description | Parameter| Description| | -------- | -------- | | command | Command to execute.| - | **Return Value**| **Description**| + | Return Value| **Description**| | Returned information| Command execution result.| Example: - + + ``` - hdc_std shell + hdc shell ``` - - - **jpid** - Obtains the list of processes that can be debugged. - + Example: - + + ``` - hdc_std jpid + hdc jpid ``` - - ## FAQs -### Failed to Connect to a Device from hdc_std +### Failed to Connect the Target Device - **Symptom** - - After the **hdc_std list targets** command is executed, **[Empty]** is displayed. + After the **hdc list targets** command is executed, **[Empty]** is displayed. - **Solution** - - The device cannot be identified. - - Check whether **HDC Device** exists under the **Universal Serial Bus controllers** in the **Device Manager**. If **HDC Device** does not exist, the device cannot be connected. In this case, disconnect and then reconnect the USB connection between the test PC and the OpenHarmony device, or burn the latest image. - - - hdc_std works improperly. - - Run the **hdc kill** command to terminate the hdc_std process or run the **hdc start -r** command to restart the hdc service. Then, run the **hdc list targets** command to check whether device information can be obtained. - - - hdc_std does not match the device. - - If the latest image is burnt on the device, the latest hdc_std version must be used. + Check whether **HDC Device** exists under the **Universal Serial Bus controllers** in the **Device Manager**. If **HDC Device** does not exist, the device cannot be connected. In this case, disconnect and then reconnect the USB connection between the test PC and the OpenHarmony device, or burn the latest image. + - hdc works improperly. + Run the **hdc kill** command to terminate the hdc process or run the **hdc start -r** command to restart the hdc service. Then, run the **hdc list targets** command to check whether device information can be obtained. + - The hdc version does not match the device. + If the latest image is burnt on the device, the latest hdc version must be used. -### hdc_std Fails to Run +### Failed to Run hdc - **Symptom** - - After you click **hdc_std.exe**, the file fails to execute. - -- **Solution** - - **hdc_std.exe** requires no installation. You can use it after placing it to a local directory or adding the tool path to environment variables. Run the **cmd** command and then run the **hdc_std** command to start the tool. + The **hdc.exe** file cannot be executed. - - -### Accessing a Server from the Client - -1. Run the **kill** command to stop the local server. - -2. Run the **-s [ip:]port -m** command to start the remote server. - - Example: - - ``` - hdc_std -s severIP:8710 -m - ``` - - - -3. Run **-s [ip:]port** *command* to execute a command on the remote server. - - Example: - - ``` - hdc_std -s severIP:8710 list targets - ``` - - +- **Solution** + 1. Check the running environment. + Ubuntu 18.04 64-bit or later is recommended for Linux.
If **libc++.so** is incorrectly referenced, run the **ldd** or **readelf** command to check the referenced library.
Windows 10 64-bit is recommended for Windows. If the Windows winusb library of an earlier version is missing, use Zadig to update the library. For composite devices, use Zadig tool to install the libusb-win32 driver. + 2. Run **hdc.exe**. + **hdc.exe** requires no installation. You can use it after placing it to a local directory or adding the tool path to environment variables. Run **cmd** and then the **hdc** command to start the tool. + + +### Accessing a hdc Server from the hdc Client + +- **Scenario** + + Use hdc to connect to a remote server from the local client. + +- **Procedure** + 1. Shut down the local hdc server. + ``` + hdc kill + ``` + 2. Run **-s [ip:]port -m** to start the remote server. + ``` + hdc -s severIP:8710 -m + ``` + 3. Run **-s [ip:]port** *command* to execute a command on the remote server. + ``` + hdc -s severIP:8710 list targets + ``` diff --git a/en/device-dev/subsystems/subsys-usbservice-overview.md b/en/device-dev/subsystems/subsys-usbservice-overview.md index f4ed4c4d9a79c24bff2157ec1d2c2392fbab2396..8981a02b997f2946b9cacdec98a087a4729da6eb 100644 --- a/en/device-dev/subsystems/subsys-usbservice-overview.md +++ b/en/device-dev/subsystems/subsys-usbservice-overview.md @@ -1,24 +1,51 @@ -# USB Overview - +# USB ## Introduction +### Function Description + USB devices are classified into two types: USB host and USB device. On OpenHarmony, you can use the port service to switch between the host mode and device mode. In host mode, you can obtain the list of connected USB devices, manage device access permissions, and perform bulk transfer or control transfer between the host and connected devices. In device mode, you can switch between functions including HDC (debugging), ACM (serial port), and ECM (Ethernet port). +### Basic Concepts + +- USB service + + An abstraction of underlying hardware-based USB devices. Your application can access the USB devices via the USB service. With the APIs provided by the USB service, you can obtain the list of connected USB devices, manage device access permissions, and perform data transfer or control transfer between the host and connected devices. + +- USB API + + A collection of JS APIs provided for the upper layer through NAPI. Your application can use USB APIs to implement various basic functions, for example, query of the USB device list, USB device plug notification, USB host and device mode switching, bulk transfer, control transfer, right management, and function switching in device mode. + +- USB Service layer + + A layer implemented by using the C++ programming language. It consists of four modules: Host, Device, Port, and Right. HDI-based APIs provided by USB Service are mainly used to implement management of USB device list, USB functions, USB ports, and USB device access permissions. The USB Service layer interacts with the HAL layer to receive, parse, and distribute data, manages foreground and background policies, and performs USB device management and right control. + +- USB HAL layer + + A layer implemented by using the C programming language. Based on the Host Driver Development Kit (SDK) and Device DDK, USB HAL encapsulates basic USB device operations, provides C++ APIs for the upper layer, and receives information from the kernel through the Hardware Driver Foundation (HDF) framework. + +### Working Principles + +The USB subsystem logically consists of three parts: USB API, USB Service, and USB HAL. The following figure shows how the USB service is implemented. + **Figure 1** USB service architecture ![USB service architecture](figure/en-us_image_0000001267088285.png) -The USB service architecture consists of three layers: -- USB API: a layer that provides JS APIs for the upper layer through NAPI. +- USB API: provides USB APIs that implement various basic functions, for example, query of the USB device list, bulk data transfer, control transfer, and right management. + +- USB Service: receives, parses, and distributes Hardware Abstraction Layer (HAL) data, manages and controls foreground and background policies, and manages devices. -- USB service: a layer implemented by using the C++ programming language and logically divided into the Host, Device, and Port modules. HDI-based APIs provided by USB Service are mainly used to implement management of USB device list, USB functions, USB ports, and USB device access permissions. +- USB HAL: provides driver capability APIs that can be directly called in user mode. -- USB HAL: a layer implemented by using the C programming language. Based on the Host Driver Development Kit (SDK) and Device DDK, USB HAL encapsulates basic USB device operations, provides C++ APIs for the upper layer, and receives information from the kernel through the Hardware Driver Foundation (HDF) framework. +## Usage Guidelines +### When to Use -## Available APIs +In Host mode, you can obtain the list of connected devices, enable or disable the devices, manage device access permissions, and perform data transfer or control transfer. -- Host +### APIs + + **Table 1** Host-specific APIs | API | Description | | ------------------------------------------------------------ | ------------------------------------------------------------ | @@ -46,7 +73,7 @@ The USB service architecture consists of three layers: | int32_t BulkWrite(USBDevicePipe &pip, const USBEndpoint &endpoint, sptr &ashmem); | Writes data asynchronously during bulk transfer. | | int32_t BulkCancel(USBDevicePipe &pip, const USBEndpoint &endpoint); | Cancels bulk transfer. The asynchronous read and write operations on the current USB interface will be cancelled. | -- Device + **Table 2** Device-specific APIs | API | Description | | -------------------------------------------------- | ------------------------------------------------------ | @@ -55,10 +82,257 @@ The USB service architecture consists of three layers: | int32_t UsbFunctionsFromString(std::string funcs); | Converts the string descriptor of a given USB function list to a numeric mask combination.| | std::string UsbFunctionsToString(int32_t funcs); | Converts the numeric mask combination of a given USB function list to a string descriptor.| -- Port + **Table 3** Port-specific APIs | API | Description | | ------------------------------------------------------------ | -------------------------------------------------------- | | int32_t GetSupportedModes(int32_t portId, int32_t &supportedModes); | Obtains the mask combination for the supported mode list of a given port. | | int32_t SetPortRole(int32_t portId, int32_t powerRole, int32_t dataRole); | Sets the role types supported by a specified port, which can be **powerRole** (for charging) and **dataRole** (for data transfer).| | int32_t GetPorts(std::vector &usbPorts); | Obtains the USB port descriptor list. | + +### How to Use + +The following uses bulk transfer as an example to illustrate the development procedure. + +1. Obtain a USB service instance. + + ```cpp + static OHOS::USB::UsbSrvClient &g_usbClient = OHOS::USB::UsbSrvClient::GetInstance(); + ``` + +2. Obtain the USB device list. + + ```cpp + std::vector deviceList; + int32_t ret = g_usbClient.GetDevices(deviceList); + ``` + +3. Apply for device access permissions. + + ```cpp + int32_t ret = g_usbClient.RequestRight(device.GetName()); + ``` + +4. Open a camera device. + + ```cpp + USBDevicePipe pip; + int32_t et = g_usbClient.OpenDevice(device, pip); + ``` + +5. Configure the USB interface. + + ```cpp + // interface indicates an interface of the USB device in deviceList. + ret = g_usbClient.ClaimInterface(pip, interface, true); + ``` + +6. Perform data transfer. + + ```cpp + // pipe indicates the pipe for data transfer after the USB device is opened. endpoint indicates the endpoint for data transfer on the USB device. vdata indicates the binary data block to be transferred or read. timeout indicates the timeout duration of data transfer. + srvClient.BulkTransfer(pipe, endpoint, vdata, timeout); + ``` + +7. Closes a device. + + ```cpp + ret = g_usbClient.Close(pip); + ``` + +### Sample Code + +```cpp +#include +#include +#include +#include +#include +#include +#include "if_system_ability_manager.h" +#include "ipc_skeleton.h" +#include "iremote_object.h" +#include "iservice_registry.h" +#include "iusb_srv.h" +#include "string_ex.h" +#include "system_ability_definition.h" +#include "usb_common.h" +#include "usb_device.h" +#include "usb_errors.h" +#include "usb_request.h" +#include "usb_server_proxy.h" +#include "usb_srv_client.h" + +const int32_t REQUESTYPE = ((1 << 7) | (0 << 5) | (0 & 0x1f)); +const int32_t REQUESTCMD = 6; +const int32_t VALUE = (2 << 8) + 0; +const int32_t TIMEOUT = 5000; +const int32_t ITFCLASS = 10; +const int32_t PRAMATYPE = 2; +const int32_t BUFFERLENGTH = 21; + +void GetType(OHOS::USB::USBEndpoint &tep, OHOS::USB::USBEndpoint &outEp, bool &outEpFlg) +{ + if ((tep.GetType() == PRAMATYPE)) { + if (tep.GetDirection() == 0) { + outEp = tep; + outEpFlg = true; + } + } +} + +bool SelectEndpoint(OHOS::USB::USBConfig config, + std::vector interfaces, + OHOS::USB::UsbInterface &interface, + OHOS::USB::USBEndpoint &outEp, + bool &outEpFlg) +{ + for (int32_t i = 0; i < config.GetInterfaceCount(); ++i) { + OHOS::USB::UsbInterface tif = interfaces[i]; + std::vector mEndpoints = tif.GetEndpoints(); + for (int32_t j = 0; j < tif.GetEndpointCount(); ++j) { + OHOS::USB::USBEndpoint tep = mEndpoints[j]; + if ((tif.GetClass() == ITFCLASS) && (tif.GetSubClass() == 0) && (tif.GetProtocol() == PRAMATYPE)) { + GetType(tep, outEp, outEpFlg); + } + } + if (outEpFlg) { + interface = interfaces[i]; + return true; + } + std::cout << std::endl; + } + return false; +} + +int OpenDeviceTest(OHOS::USB::UsbSrvClient &Instran, OHOS::USB::UsbDevice device, OHOS::USB::USBDevicePipe &pip) +{ + int ret = Instran.RequestRight(device.GetName()); + std::cout << "device RequestRight ret = " << ret << std::endl; + if (0 != ret) { + std::cout << "device RequestRight failed = " << ret << std::endl; + } + ret = Instran.OpenDevice(device, pip); + return ret; +} + +int CtrTransferTest(OHOS::USB::UsbSrvClient &Instran, OHOS::USB::USBDevicePipe &pip) +{ + std::cout << "usb_device_test : << Control Transfer >> " << std::endl; + std::vector vData; + const OHOS::USB::UsbCtrlTransfer tctrl = {REQUESTYPE, REQUESTCMD, VALUE, 0, TIMEOUT}; + int ret = Instran.ControlTransfer(pip, tctrl, vData); + if (ret != 0) { + std::cout << "control message read failed width ret = " << ret << std::endl; + } else { + } + std::cout << "control message read success" << std::endl; + + return ret; +} + +int ClaimTest(OHOS::USB::UsbSrvClient &Instran, + OHOS::USB::USBDevicePipe &pip, + OHOS::USB::UsbInterface &interface, + bool interfaceFlg) +{ + if (interfaceFlg) { + std::cout << "ClaimInterface InterfaceInfo:" << interface.ToString() << std::endl; + int ret = Instran.ClaimInterface(pip, interface, true); + if (ret != 0) { + std::cout << "ClaimInterface failed width ret = " << ret << std::endl; + } else { + std::cout << "ClaimInterface success" << std::endl; + } + } + return 0; +} + +int BulkTransferTest(OHOS::USB::UsbSrvClient &Instran, + OHOS::USB::USBDevicePipe &pip, + OHOS::USB::USBEndpoint &outEp, + bool interfaceFlg, + bool outEpFlg) +{ + if (interfaceFlg) { + std::cout << "usb_device_test : << Bulk transfer start >> " << std::endl; + if (outEpFlg) { + uint8_t buffer[50] = "hello world 123456789"; + std::vector vData(buffer, buffer + BUFFERLENGTH); + int ret = Instran.BulkTransfer(pip, outEp, vData, TIMEOUT); + if (ret != 0) { + std::cout << "Bulk transfer write failed width ret = " << ret << std::endl; + } else { + std::cout << "Bulk transfer write success" << std::endl; + } + return ret; + } + } + return 0; +} + +int main(int argc, char **argv) +{ + std::cout << "usb_device_test " << std::endl; + static OHOS::USB::UsbSrvClient &Instran = OHOS::USB::UsbSrvClient::GetInstance(); + // GetDevices + std::vector deviceList; + int32_t ret = Instran.GetDevices(deviceList); + if (ret != 0) { + return OHOS::USB::UEC_SERVICE_INVALID_VALUE; + } + if (deviceList.empty()) { + return OHOS::USB::UEC_SERVICE_INVALID_VALUE; + } + + OHOS::USB::UsbDevice device = deviceList[0]; + std::vector configs = device.GetConfigs(); + OHOS::USB::USBConfig config = configs[0]; + std::vector interfaces = config.GetInterfaces(); + OHOS::USB::UsbInterface interface; + OHOS::USB::USBEndpoint outEp; + bool interfaceFlg = false; + bool outEpFlg = false; + interfaceFlg = SelectEndpoint(config, interfaces, interface, outEp, outEpFlg); + + // OpenDevice + std::cout << "usb_device_test : << OpenDevice >> test begin -> " << std::endl; + OHOS::USB::USBDevicePipe pip; + ret = OpenDeviceTest(Instran, device, pip); + if (ret != 0) { + return OHOS::USB::UEC_SERVICE_INVALID_VALUE; + } + + // ControlTransfer + CtrTransferTest(Instran, pip); + + // ClaimInterface + ClaimTest(Instran, pip, interface, interfaceFlg); + + // BulkTransferWrite + BulkTransferTest(Instran, pip, outEp, interfaceFlg, outEpFlg); + + // CloseDevice + std::cout << "usb_device_test : << Close Device >> " << std::endl; + ret = Instran.Close(pip); + if (ret == 0) { + std::cout << "Close device failed width ret = " << ret << std::endl; + return OHOS::USB::UEC_SERVICE_INVALID_VALUE; + } else { + std::cout << "Close Device success" << std::endl; + } + return 0; +} +``` + +### Repositories Involved + +[Driver subsystem](https://gitee.com/openharmony/docs/blob/master/en/readme/driver.md) + +[drivers\_peripheral](https://gitee.com/openharmony/drivers_peripheral/blob/master/README_zh.md) + +[drivers\_framework](https://gitee.com/openharmony/drivers_framework/blob/master/README_zh.md) + +[drivers\_adapter](https://gitee.com/openharmony/drivers_adapter/blob/master/README_zh.md) + +[drivers\_adapter\_khdf\_linux](https://gitee.com/openharmony/drivers_adapter_khdf_linux/blob/master/README_zh.md) diff --git a/en/device-dev/subsystems/subsys-xts-guide.md b/en/device-dev/subsystems/subsys-xts-guide.md deleted file mode 100644 index 8f8996cbbf7aa5604632bfd7f5a3bf09fd0da677..0000000000000000000000000000000000000000 --- a/en/device-dev/subsystems/subsys-xts-guide.md +++ /dev/null @@ -1,513 +0,0 @@ -# XTS Test Case Development - -## Introduction - -The X test suite (XTS) subsystem contains a set of OpenHarmony compatibility test suites, including the currently supported application compatibility test suite (ACTS) and the device compatibility test suite (DCTS) that will be supported in the future. - -This subsystem contains the ACTS and **tools** software package. - -- The **acts** directory stores the source code and configuration files of ACTS test cases. The ACTS helps device vendors detect the software incompatibility as early as possible and ensures that the software is compatible to OpenHarmony during the entire development process. -- The **tools** software package stores the test case development framework related to **acts**. - -## System Types - -OpenHarmony supports the following systems: - -- Mini system - - A mini system runs on a device that comes with memory greater than or equal to 128 KiB and MCU such as ARM Cortex-M and 32-bit RISC-V. It provides multiple lightweight network protocols and graphics frameworks, and a wide range of read/write components for the IoT bus. Typical products include connection modules, sensors, and wearables for smart home. - -- Small system - - A small system runs on a device that comes with memory greater than or equal to 1 MiB and application processors such as ARM Cortex-A. It provides higher security capabilities, standard graphics frameworks, and video encoding and decoding capabilities. Typical products include smart home IP cameras, electronic cat eyes, and routers, and event data recorders (EDRs) for smart travel. - -- Standard system - - A standard system runs on a device that comes with memory greater than or equal to 128 MiB and application processors such as ARM Cortex-A. It provides a complete application framework supporting the enhanced interaction, 3D GPU, hardware composer, diverse components, and rich animations. This system applies to high-end refrigerator displays. - - -## Directory Structure - -``` -/test/xts -├── acts # Test code -│ └── subsystem # Source code of subsystem test cases for the standard system -│ └── subsystem_lite # Source code of subsystems test cases for mini and small systems -│ └── BUILD.gn # Build configuration of test cases for the standard system -│ └── build_lite # Build configuration of test cases for the mini and small systems. -│ └── BUILD.gn # Build configuration of test cases for mini and small systems -└── tools # Test tool code -``` - -## Constraints - -Test cases for the mini system must be developed in C, and those for the small system must be developed in C++. - -## Usage Guidelines - -**Table 1** Test case levels - -| Level | Definition | Scope | -| ----- | ----------- | ------- | -| Level0 | Smoke | Verifies basic functionalities of key features and basic DFX attributes with the most common input. The pass result indicates that the features are runnable. | -| Level1 | Basic | Verifies basic functionalities of key features and basic DFX attributes with common input. The pass result indicates that the features are testable. | -| Level2 | Major | Verifies basic functionalities of key features and basic DFX attributes with common input and errors. The pass result indicates that the features are functional and ready for beta testing. | -| Level3 | Regular | Verifies functionalities of all key features, and all DFX attributes with common and uncommon input combinations or normal and abnormal preset conditions. | -| Level4 | Rare | Verifies functionalities of key features under extremely abnormal presets and uncommon input combinations. | - - -**Table 2** Test case granularities - -| Test Scale | Test Objects | Test Environment | -| ----- | ----------- | ------- | -| LargeTest | Service functionalities, all-scenario features, and mechanical power environment (MPE) and scenario-level DFX | Devices close to real devices. | -| MediumTest | Modules, subsystem functionalities after module integration, and DFX | Single device that is actually used. You can perform message simulation, but do not mock functions. | -| SmallTest | Modules, classes, and functions | Local PC. Use a large number of mocks to replace dependencies with other modules. | - -**Table 3** Test types - -| Type | Definition | -| ----------- | ------- | -| Function | Tests the correctness of both service and platform functionalities provided by the tested object for end users or developers. | -| Performance | Tests the processing capability of the tested object under specific preset conditions and load models. The processing capability is measured by the service volume that can be processed in a unit time, for example, call per second, frame per second, or event processing volume per second. | -| Power | Tests the power consumption of the tested object in a certain period of time under specific preset conditions and load models. | -| Reliability | Tests the service performance of the tested object under common and uncommon input conditions, or specified service volume pressure and long-term continuous running pressure. The test covers stability, pressure handling, fault injection, and Monkey test times. | -| Security | Tests the capability of defending against security threats, including but not limited to unauthorized access, use, disclosure, damage, modification, and destruction, to ensure information confidentiality, integrity, and availability.
Tests the privacy protection capability to ensure that the collection, use, retention, disclosure, and disposal of users' private data comply with laws and regulations.
Tests the compliance with various security specifications, such as security design, security requirements, and security certification of the Ministry of Industry and Information Technology (MIIT). | -| Global | Tests the internationalized data and localization capabilities of the tested object, including multi-language display, various input/output habits, time formats, and regional features, such as currency, time, and culture taboos. | -| Compatibility | Tests backward compatibility of an application with its own data, the forward and backward compatibility with the system, and the compatibility with different user data, such as audio file content of the player and smart SMS messages.
Tests system backward compatibility with its own data and the compatibility of common applications in the ecosystem.
Tests software compatibility with related hardware. | -| User | Tests user experience of the object in real user scenarios. All conclusions and comments should come from the users, which are all subjective evaluation in this case. | -| Standard | Tests the compliance with industry and company-specific standards, protocols, and specifications. The standards here do not include any security standards that should be classified into the security test. | -| Safety | Tests the safety property of the tested object to avoid possible hazards to personal safety, health, and the object itself. | -| Resilience | Tests the resilience property of the tested object to ensure that it can withstand and maintain the defined running status (including downgrading) when being attacked, and recover from and adapt defense to the attacks to approach mission assurance. | - -## Test Case Development Guidelines - -The test framework and programming language vary with the system type. - -**Table 4** Test frameworks and test case languages for different systems - -| System | Test Framework | Language | -| ----- | ----------- | ------- | -| Mini | HCTest | C | -| Small | HCPPTest | C++ | -| Standard | HJSUnit and HCPPTest | JavaScript and C++ | - -### Developing Test Cases in C (for the Mini System) - -**Developing Test Cases for the Mini System** - -HCTest and the C language are used to develop test cases. HCTest is enhanced and adapted based on the open-source test framework Unity. - -1. Define the test case directory. The test cases are stored to **test/xts/acts**. - - ``` - ├── acts - │ └──subsystem_lite - │ │ └── module_hal - │ │ │ └── BUILD.gn - │ │ │ └── src - │ └──build_lite - │ │ └── BUILD.gn - ``` - -2. Write the test case in the **src** directory. - - (1) Include the test framework header file. - - ``` - #include "hctest.h" - ``` - - (2) Use the **LITE_TEST_SUIT** macro to define names of the subsystem, module, and test suite. - - ``` - /** - * @brief register a test suite named "IntTestSuite" - * @param test subsystem name - * @param example module name - * @param IntTestSuite test suite name - */ - LITE_TEST_SUIT(test, example, IntTestSuite); - ``` - (3) Define Setup and TearDown. - - ​ Format: Test suite name+Setup, Test suite name+TearDown. - - ​ The Setup and TearDown functions must exist, but function bodies can be empty. - - (4) Use the **LITE_TEST_CASE** macro to write the test case. - - ​ Three parameters are involved: test suite name, test case name, and test case properties (including type, granularity, and level). - ``` - LITE_TEST_CASE(IntTestSuite, TestCase001, Function | MediumTest | Level1) - { - // Do something. - }; - ``` - (5) Use the **RUN_TEST_SUITE** macro to register the test suite. - ``` - RUN_TEST_SUITE(IntTestSuite); - ``` -3. Create the configuration file (**BUILD.gn**) of the test module. - - Create a **BUILD.gn** (example) file in each test module directory, and specify the name of the built static library and its dependent header files and libraries. - - The format is as follows: - - ``` - import("//test/xts/tools/lite/build/suite_lite.gni") - hctest_suite("ActsDemoTest") { - suite_name = "acts" - sources = [ - "src/test_demo.c", - ] - include_dirs = [ ] - cflags = [ "-Wno-error" ] - } - ``` - -4. Add build options to the **BUILD.gn** file in the **acts** directory. - - You need to add the test module to the **test/xts/acts/build\_lite/BUILD.gn** script in the **acts** directory. - - ``` - lite_component("acts") { - ... - if(board_name == "liteos_m") { - features += [ - ... - "//xts/acts/subsystem_lite/module_hal:ActsDemoTest" - ] - } - } - ``` - -5. Run build commands. - - Test suites are built along with the OS version. The ACTS is built together with the debug version. - - >![](../public_sys-resources/icon-note.gif) **NOTE**
The ACTS build middleware is a static library, which will be linked to the image. - - -### Executing Test Cases in C (for the Mini System) - -**Executing Test Cases for the Mini System** - -Burn the image into the development board. - -**Executing the Test** - -1. Use a serial port tool to log in to the development board and save information about the serial port. -2. Restart the device and view serial port logs. - -**Analyzing the Test Result** - -View the serial port logs in the following format: - -The log for each test suite starts with "Start to run test suite:" and ends with "xx Tests xx Failures xx Ignored". - -### Developing Test Cases in C++ (for Standard and Small Systems) - -**Developing Test Cases for Small-System Devices** (for the standard system, see the **global/i18n_standard directory**.) - -The HCPPTest framework, an enhanced version based on the open-source framework Googletest, is used. - -1. Define the test case directory. The test cases are stored to **test/xts/acts**. - - ``` - ├── acts - │ └──subsystem_lite - │ │ └── module_posix - │ │ │ └── BUILD.gn - │ │ │ └── src - │ └──build_lite - │ │ └── BUILD.gn - ``` - -2. Write the test case in the **src** directory. - - (1) Include the test framework. - - Include **gtest.h**. - ``` - #include "gtest/gtest.h" - ``` - - - (2) Define Setup and TearDown. - - ``` - using namespace std; - using namespace testing::ext; - class TestSuite: public testing::Test { - protected: - // Preset action of the test suite, which is executed before the first test case - static void SetUpTestCase(void){ - } - // Test suite cleanup action, which is executed after the last test case - static void TearDownTestCase(void){ - } - // Preset action of the test case - virtual void SetUp() - { - } - // Cleanup action of the test case - virtual void TearDown() - { - } - }; - ``` - - - (3) Use the **HWTEST** or **HWTEST_F** macro to write the test case. - - **HWTEST**: definition of common test cases, including the test suite name, test case name, and case annotation. - - **HWTEST_F**: definition of SetUp and TearDown test cases, including the test suite name, test case name, and case annotation. - - Three parameters are involved: test suite name, test case name, and test case properties (including type, granularity, and level). - - ``` - HWTEST_F(TestSuite, TestCase_0001, Function | MediumTest | Level1) { - // Do something - ``` - -3. Create a configuration file (**BUILD.gn**) of the test module. - - Create a **BUILD.gn** file in each test module directory, and specify the name of the built static library and its dependent header files and libraries. Each test module is independently built into a **.bin** executable file, which can be directly pushed to the development board for testing. - - Example: - - ``` - import("//test/xts/tools/lite/build/suite_lite.gni") - hcpptest_suite("ActsDemoTest") { - suite_name = "acts" - sources = [ - "src/TestDemo.cpp" - ] - - include_dirs = [ - "src", - ... - ] - deps = [ - ... - ] - cflags = [ "-Wno-error" ] - } - ``` - -4. Add build options to the **BUILD.gn** file in the **acts** directory. - - Add the test module to the **test/xts/acts/build_lite/BUILD.gn** script in the **acts** directory. - - ``` - lite_component("acts") { - ... - else if(board_name == "liteos_a") { - features += [ - ... - "//xts/acts/subsystem_lite/module_posix:ActsDemoTest" - ] - } - } - ``` - - -5. Run build commands. - - Test suites are built along with the OS version. The ACTS is built together with the debug version. - - >![](../public_sys-resources/icon-note.gif) **NOTE** - > - >The ACTS for the small system is independently built to an executable file (.bin) and archived in the **suites\acts** directory of the build result. - - -### Executing Test Cases in C++ (for Standard and Small Systems) - -**Executing Test Cases for the Small System** - -Currently, test cases are shared by the NFS and mounted to the development board for execution. - -**Setting Up the Environment** - -1. Use a network cable or wireless network to connect the development board to your PC. - -2. Configure the IP address, subnet mask, and gateway for the development board. Ensure that the development board and the PC are in the same network segment. - -3. Install and register the NFS server on the PC and start the NFS service. - -4. Run the **mount** command for the development board to ensure that the development board can access NFS shared files on the PC. - - Format: **mount** _NFS server IP address_**:/**_NFS shared directory_ **/**_development board directory_ **nfs** - - Example: - - ``` - mount 192.168.1.10:/nfs /nfs nfs - ``` - - - -**Executing Test Cases** - -Execute **ActsDemoTest.bin** to trigger test case execution, and analyze serial port logs generated after the execution is complete. - -### Developing Test Cases in JavaScript (for the Standard System) - -The HJSUnit framework is used to support automated test of OpenHarmony apps that are developed using the JavaScript language based on the JS application framework. - -**Basic Syntax of Test Cases** - -The test cases are developed with the JavaScript language and must meet the programming specifications of the language. - -**Table 5** Basic syntax of test cases - -| Syntax | Description | Mandatory | -| ------- | ------------- | ------------ | -| beforeAll | Presets a test-suite-level action executed only once before all test cases are executed. You can pass the action function as the only parameter. | No | -| afterAll | Presets a test-suite-level clear action executed only once after all test cases are executed. You can pass the clear function as the only parameter. | No | -| beforeEach | Presets a test-case-level action executed before each test case is executed. The number of execution times is the same as the number of test cases defined by it. You can pass the action function as the only parameter. | No | -| afterEach | Presets a test-case-level clear action executed after each test case is executed. The number of execution times is the same as the number of test cases defined by it. You can pass the clear function as the only parameter. | No | -| describe | Defines a test suite. You can pass two parameters: test suite name and test suite function. The describe statement supports nesting. You can use beforeall, beforeEach, afterEach, and afterAll in each describe statement. | Yes | -| it | Defines a test case. You can pass three parameters: test case name, filter parameter, and test case function.
**Filter parameter:**
The value is a 32-bit integer. Setting different bits to 1 means different configurations.
- Setting bit 0 to **1** means bypassing the filter.
- Setting bits 0-10 to **1** specifies the test case type, which can be FUNCTION (function test), PERFORMANCE (performance test), POWER (power consumption test), RELIABILITY (reliability test), SECURITY (security compliance test), GLOBAL (integrity test), COMPATIBILITY (compatibility test), USER (user test), STANDARD (standard test), SAFETY (security feature test), and RESILIENCE (resilience test), respectively.
- Setting bits 16-18 to **1** specifies the test case scale, which can be SMALL (small-scale test), MEDIUM (medium-scale test), and LARGE (large-scale test), respectively.
- Setting bits 24-28 to **1** specifies the test level, which can be LEVEL0 (level-0 test), LEVEL1 (level-1 test), LEVEL2 (level-2 test), LEVEL3 (level-3 test), and LEVEL4 (level-4 test), respectively.
| Yes | - -Use the standard syntax of Jasmine to write test cases. The ES6 specification is supported. - -1. Define the test case directory. The test cases are stored in the **entry/src/main/js/test** directory. - - ``` - ├── BUILD.gn - │ └──entry - │ │ └──src - │ │ │ └──main - │ │ │ │ └──js - │ │ │ │ │ └──default - │ │ │ │ │ │ └──pages - │ │ │ │ │ │ │ └──index - │ │ │ │ │ │ │ │ └──index.js # Entry file - │ │ │ │ │ └──test # Test code directory - │ │ │ └── resources # HAP resources - │ │ │ └── config.json # HAP configuration file - ``` - - -2. Start the JS test framework and load test cases. - - The following is an example for **index.js**. - - ``` - // Start the JS test framework and load test cases. - import {Core, ExpectExtend} from 'deccjsunit/index' - - export default { - data: { - title: "" - }, - onInit() { - this.title = this.$t('strings.world'); - }, - onShow() { - console.info('onShow finish') - const core = Core.getInstance() - const expectExtend = new ExpectExtend({ - 'id': 'extend' - }) - core.addService('expect', expectExtend) - core.init() - const configService = core.getDefaultService('config') - configService.setConfig(this) - require('../../../test/List.test') - core.execute() - }, - onReady() { - }, - } - ``` - - - -3. Write a unit test case. - - The following is an example: - - ``` - // Example 1: Use HJSUnit to perform a unit test. - describe('appInfoTest', function () { - it('app_info_test_001', 0, function () { - var info = app.getInfo() - expect(info.versionName).assertEqual('1.0') - expect(info.versionCode).assertEqual('3') - }) - }) - ``` - - - - -### Packaging Test Cases in JavaScript (for the Standard System) - -For details about how to build a HAP, see the JS application development guide of the standard system [Building and Creating HAPs](https://developer.harmonyos.com/en/docs/documentation/doc-guides/build_overview-0000001055075201). - -## Performing a Full Build (for the Standard System) - -Run the following command: - -``` -./build.sh suite=acts system_size=standard -``` - - - - -Test case directory: **out/release/suites/acts/testcases** - -Test framework and test case directory: **out/release/suites/acts** \(the test suite execution framework is compiled during the build process) - - -## Executing Test Cases in a Full Build (for Small and Standard Systems) - -**Setting Up a Test Environment** - -Install Python 3.7 or a later version on a Windows environment and ensure that the Windows environment is properly connected to the test device. - -**Test execution directory** \(corresponding to the **out/release/suites/acts** directory generated in the build) - -``` -├── testcase # Directory for storing test suite files -│ └──xxx.hap # HAP file executed by the test suite -│ └──xxx.json # Execution configuration file of the test suite -├── tools # Test framework tool directory -├── run.bat # File for starting the test suite on the Windows platform -├── report # Directory for storing the test reports -``` - -**Executing Test Cases** - -1. On the Windows environment, locate the directory in which the test cases are stored \(**out/release/suites/acts**, copied from the Linux server), go to the directory in the Windows command window, and run **acts\\run.bat**. - -2. Enter the command for executing the test case. - - - Execute all test cases. - - ``` - run acts - ``` - - ![](figure/en-us_image_0000001119924146.gif) - - - Execute the test cases of a module \(view specific module information in **\acts\testcases\**). - - ``` - run –l ActsSamgrTest - ``` - - ![](figure/en-us_image_0000001166643927.jpg) - - You can view specific module information in **\acts\testcases\**. - - Wait until the test cases are complete. - -3. View the test report. - - Go to **acts\reports**, obtain the current execution record, and open **summary_report.html** to view the test report. diff --git a/en/device-dev/website.md b/en/device-dev/website.md index 836b707712c87d3637a00d9faa83594beb754d1e..cc82446ca3e2442fc1ffe731568c6bd1695eab5e 100644 --- a/en/device-dev/website.md +++ b/en/device-dev/website.md @@ -93,40 +93,63 @@ - [Porting Verification](porting/porting-minichip-verification.md) - [FAQs](porting/porting-chip-faqs.md) -- Small System SoC Porting Guide - - - Porting Preparation - - - [Before You Start](porting/porting-smallchip-prepare-needs.md) - - - [Compilation and Building](porting/porting-smallchip-prepare-building.md) - - - Kernel Porting - - - [LiteOS Cortex-A](porting/porting-smallchip-kernel-a.md) - - - [Linux Kernel](porting/porting-smallchip-kernel-linux.md) - - - Driver Porting - - - [Porting Overview](porting/porting-smallchip-driver-overview.md) - - - [Platform Driver Porting](porting/porting-smallchip-driver-plat.md) - - - [Device Driver Porting](porting/porting-smallchip-driver-oom.md) + + - Small System SoC Porting Guide + + - Porting Preparation + + - [Before You Start](porting/porting-smallchip-prepare-needs.md) + + - [Compilation and Building](porting/porting-smallchip-prepare-building.md) + + - Kernel Porting + + - [LiteOS Cortex-A](porting/porting-smallchip-kernel-a.md) + + - [Linux Kernel](porting/porting-smallchip-kernel-linux.md) + + - Driver Porting + + - [Porting Overview](porting/porting-smallchip-driver-overview.md) + + - [Platform Driver Porting](porting/porting-smallchip-driver-plat.md) + + - [Device Driver Porting](porting/porting-smallchip-driver-oom.md) + - Standard System SoC Porting Guide - [Standard System Porting Guide](porting/standard-system-porting-guide.md) - [A Method for Rapidly Porting the OpenHarmony Linux Kernel](porting/porting-linux-kernel.md) -- Third-Party Library Porting Guide for Mini and Small Systems - - - [Overview](porting/porting-thirdparty-overview.md) - - [Porting a Library Built Using CMake](porting/porting-thirdparty-cmake.md) - - [Porting a Library Built Using Makefile](porting/porting-thirdparty-makefile.md) -- Mini System SoC Porting Cases - - [Mini-System Devices with Screens — Bestechnic SoC Porting Case](porting/porting-bes2600w-on-minisystem-display-demo.md) - - [Combo Solution – ASR Chip Porting Case](porting/porting-asr582x-combo-demo.md) + + - Third-Party Library Porting Guide for Mini and Small Systems + + - [Overview](porting/porting-thirdparty-overview.md) + + - [Porting a Library Built Using CMake](porting/porting-thirdparty-cmake.md) + + - [Porting a Library Built Using Makefile](porting/porting-thirdparty-makefile.md) + + - Mini System SoC Porting Cases + + - [Mini-System Devices with Screens — Bestechnic SoC Porting Case](porting/porting-bes2600w-on-minisystem-display-demo.md) + + - [Combo Solution – ASR Chip Porting Case](porting/porting-asr582x-combo-demo.md) + + - [IoT Solution - Chipsea CST85 Chip Porting Case](porting/porting-cst85f01-combo-demo.md) + + - [Mini System STM32F407 SoC Porting Case](porting/porting-stm32f407-on-minisystem-eth.md) + + - [Combo Solution – W800 Chip Porting Case](porting/porting-w800-combo-demo.md) + + - Small System SoC Porting Cases + + - [Mini-System Devices – STM32MP1 SoC Porting Case](porting/porting-stm32mp15xx-on-smallsystem.md) + + - Standard System SoC Porting Cases + + - [Standard System Solution – Rockchip RK3568 Porting Case](porting/porting-dayu200-on_standard-demo.md) + - Subsystem Development - Kernel @@ -142,7 +165,7 @@ - [Mutex](kernel/kernel-mini-basic-ipc-mutex.md) - [Queue](kernel/kernel-mini-basic-ipc-queue.md) - [Semaphore](kernel/kernel-mini-basic-ipc-sem.md) - - [Time Management](kernel/kernel-basic-mini-time.md) + - [Time Management](kernel/kernel-mini-basic-time.md) - [Software Timer](kernel/kernel-mini-basic-soft.md) - [Doubly Linked List](kernel/kernel-mini-basic-list.md) - Extended Components @@ -295,6 +318,7 @@ - HDF - [HDF Overview](driver/driver-hdf-overview.md) - [Driver Development](driver/driver-hdf-development.md) + - [Driver Loading](driver/driver-hdf-load.md) - [Driver Service Management](driver/driver-hdf-servicemanage.md) - [Driver Message Mechanism Management](driver/driver-hdf-message-management.md) - [Driver Configuration Management](driver/driver-hdf-manage.md) @@ -357,6 +381,7 @@ - Related Operations - [Building a Subsystem](subsystems/subsys-build-subsystem.md) - [Building a Product](subsystems/subsys-build-product.md) + - [Building a Subsystem](subsystems/subsys-build-subsystem.md) - [Building a Component](subsystems/subsys-build-component.md) - [Building a Module](subsystems/subsys-build-module.md) - [Building a Chipset Solution](subsystems/subsys-build-chip_solution.md) @@ -493,6 +518,8 @@ - Debugging + - [Development Self-Test Framework User Guide](device-test/developer_test.md) + - [xDevice User Guide](device-test/xdevice.md) - R&D Tools - [bytrace](subsystems/subsys-toolchain-bytrace-guide.md) - [hdc\_std](subsystems/subsys-toolchain-hdc-guide.md) @@ -503,8 +530,7 @@ - [Docker Environment](get-code/gettools-acquire.md) - [IDE](get-code/gettools-ide.md) - Hands-On Tutorials - - - [Codelabs](https://gitee.com/openharmony/codelabs/blob/master/README.md) + - [Codelabs](https://gitee.com/openharmony/codelabs/blob/master/README.md) - References - FAQs - [FAQs Overview](faqs/faqs-overview.md) @@ -515,5 +541,4 @@ - [Porting](faqs/faqs-porting.md) - [Startup](faqs/faqs-startup.md) - [System Applications](faqs/faqs-system-applications.md) -