diff --git a/.github/workflows/examples-openiotsdk.yaml b/.github/workflows/examples-openiotsdk.yaml index e34b079f2b81d7..d2f2e904c70827 100644 --- a/.github/workflows/examples-openiotsdk.yaml +++ b/.github/workflows/examples-openiotsdk.yaml @@ -32,7 +32,7 @@ env: jobs: openiotsdk: name: Open IoT SDK examples building - timeout-minutes: 140 + timeout-minutes: 90 env: TEST_NETWORK_NAME: OIStest @@ -107,25 +107,22 @@ jobs: run: | scripts/examples/openiotsdk_example.sh unit-tests - - name: Test shell example - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_shell.outcome == 'success' + - name: "Test: shell example" + if: steps.build_shell.outcome == 'success' timeout-minutes: 5 run: | scripts/examples/openiotsdk_example.sh -C test shell - - name: Test lock-app example - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_lock_app.outcome == 'success' + - name: "Test: lock-app example" + if: steps.build_lock_app.outcome == 'success' timeout-minutes: 5 run: | scripts/setup/openiotsdk/network_setup.sh -n $TEST_NETWORK_NAME up scripts/run_in_ns.sh ${TEST_NETWORK_NAME}ns scripts/examples/openiotsdk_example.sh -C test -n ${TEST_NETWORK_NAME}tap lock-app scripts/setup/openiotsdk/network_setup.sh -n $TEST_NETWORK_NAME down - - name: Run unit tests - # Temporarily disable test due to performance issue with FVP - if: false #steps.build_unit_tests.outcome == 'success' && github.event_name == 'pull_request' - timeout-minutes: 90 + - name: "Test: unit-tests" + if: steps.build_unit_tests.outcome == 'success' + timeout-minutes: 40 run: | - scripts/examples/openiotsdk_example.sh -C run unit-tests + scripts/examples/openiotsdk_example.sh -C test unit-tests diff --git a/.vscode/launch.json b/.vscode/launch.json index e68b78cbd4e57a..83ce3d5fe494bd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -455,7 +455,13 @@ "armToolchainPath": "${env:PW_ENVIRONMENT_ROOT}/cipd/packages/arm/bin/", "servertype": "external", "gdbTarget": ":31627", //GDBserver port on FVP - "overrideLaunchCommands": ["-enable-pretty-printing"], + "overrideLaunchCommands": [ + "-enable-pretty-printing", + "add-symbol-file ./build/bl2.elf 0x10000000", + "add-symbol-file ./build/tfm_s.elf 0x38000400", + "add-symbol-file ./build/${input:openiotsdkUnittest}_ns.elf 0x28060400", + "break main_ns.cpp:main" + ], "runToEntryPoint": "main", "preLaunchTask": "Debug Open IoT SDK unit-tests", "showDevDebugOutput": "parsed" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e495a9536fd2c..d0e5fbfae284c4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -250,6 +250,7 @@ "args": [ "-Cbuild", "-d${input:openiotsdkDebugMode}", + "-l${input:openiotsdkLwipDebug}", "${input:openiotsdkExample}" ], "group": "build", @@ -266,7 +267,12 @@ "label": "Build Open IoT SDK unit-tests", "type": "shell", "command": "scripts/examples/openiotsdk_example.sh", - "args": ["-Cbuild", "-d${input:openiotsdkDebugMode}", "unit-tests"], + "args": [ + "-Cbuild", + "-d${input:openiotsdkDebugMode}", + "-l${input:openiotsdkLwipDebug}", + "unit-tests" + ], "group": "build", "problemMatcher": { "pattern": { @@ -334,6 +340,21 @@ } } }, + { + "label": "Test Open IoT SDK unit-tests", + "type": "shell", + "command": "scripts/examples/openiotsdk_example.sh", + "args": ["-Ctest", "unit-tests", "${input:openiotsdkUnitTest}"], + "group": "test", + "problemMatcher": { + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "message": 5 + } + } + }, { "label": "Debug Open IoT SDK example", "type": "shell", @@ -439,6 +460,13 @@ "options": ["false", "true"], "default": "false" }, + { + "type": "pickString", + "id": "openiotsdkLwipDebug", + "description": "Do you want to use LwIP debug logs?", + "options": ["false", "true"], + "default": "false" + }, { "type": "pickString", "id": "openiotsdkExample", @@ -451,7 +479,6 @@ "id": "openiotsdkUnitTest", "description": "What unit test do you want to use?", "options": [ - "all", "accesstest", "AppTests", "ASN1Tests", @@ -478,7 +505,7 @@ "TransportLayerTests", "UserDirectedCommissioningTests" ], - "default": "all" + "default": "accesstest" }, { "type": "promptString", diff --git a/config/openiotsdk/cmake/linker.cmake b/config/openiotsdk/cmake/linker.cmake index 032b95f54afd60..fc3429ffd78656 100644 --- a/config/openiotsdk/cmake/linker.cmake +++ b/config/openiotsdk/cmake/linker.cmake @@ -27,12 +27,12 @@ function(set_target_link target) if (NOT LINKER_SCRIPT) set(LINKER_SCRIPT ${OPEN_IOT_SDK_CONFIG}/ld/cs300_gcc.ld) endif() - target_link_options(${APP_TARGET} PRIVATE -T ${LINKER_SCRIPT}) - set_target_properties(${APP_TARGET} PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) + target_link_options(${target} PRIVATE -T ${LINKER_SCRIPT}) + set_target_properties(${target} PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) target_link_options(${target} PRIVATE - "-Wl,-Map=${APP_TARGET}.map" + "-Wl,-Map=${target}.map" ) endfunction() diff --git a/config/openiotsdk/cmake/sdk.cmake b/config/openiotsdk/cmake/sdk.cmake index dbe54d4d122c37..7fbc7efa9561bc 100644 --- a/config/openiotsdk/cmake/sdk.cmake +++ b/config/openiotsdk/cmake/sdk.cmake @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +30,12 @@ list(APPEND CONFIG_CHIP_EXTERNAL_TARGETS) # Additional Open IoT SDK build configuration set(TFM_SUPPORT NO CACHE BOOL "Add Trusted Firmware-M (TF-M) support to application") set(TFM_NS_APP_VERSION "0.0.0" CACHE STRING "TF-M non-secure application version (in the x.x.x format)") +set(CONFIG_CHIP_OPEN_IOT_SDK_LWIP_DEBUG NO CACHE BOOL "Enable LwIP debug logs") + +# Default LwIP options directory (should contain user_lwipopts.h file) +if (NOT LWIP_PROJECT_OPTS_DIR) + set(LWIP_PROJECT_OPTS_DIR ${OPEN_IOT_SDK_CONFIG}/lwip) +endif() # Overwrite versions of Open IoT SDK components @@ -76,11 +82,11 @@ if(TFM_SUPPORT) set(TFM_PLATFORM ${OPEN_IOT_SDK_EXAMPLE_COMMON}/tf-m/targets/an552) set(TFM_PSA_FIRMWARE_UPDATE ON) set(MCUBOOT_IMAGE_VERSION_NS ${TFM_NS_APP_VERSION}) - set(TFM_CMAKE_ARGS "-DCONFIG_TFM_ENABLE_FP=ON;-DTFM_PROFILE=profile_medium") + set(TFM_CMAKE_ARGS "-DCONFIG_TFM_ENABLE_FP=ON;-DTFM_PROFILE=profile_medium;-DTFM_EXCEPTION_INFO_DUMP=ON;-DCONFIG_TFM_HALT_ON_CORE_PANIC=ON;-DTFM_ISOLATION_LEVEL=1") if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=INFO;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_INFO;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_INFO") + set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=INFO;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_DEBUG;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_INFO") else() - set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=ERROR;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_ERROR;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_ERROR") + set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DMCUBOOT_LOG_LEVEL=ERROR;-DTFM_SPM_LOG_LEVEL=TFM_SPM_LOG_LEVEL_DEBUG;-DTFM_PARTITION_LOG_LEVEL=TFM_PARTITION_LOG_LEVEL_ERROR") endif() if(TFM_PROJECT_CONFIG_HEADER_FILE) set(TFM_CMAKE_ARGS "${TFM_CMAKE_ARGS};-DPROJECT_CONFIG_HEADER_FILE=${TFM_PROJECT_CONFIG_HEADER_FILE}") @@ -135,7 +141,6 @@ if(TARGET cmsis-rtos-api) target_link_libraries(cmsis-rtos-api PUBLIC freertos-cmsis-rtos - freertos-kernel-heap-3 ) target_compile_definitions(cmsis-rtos-api @@ -162,12 +167,17 @@ if(TARGET lwip-cmsis-port) target_compile_definitions(lwipopts INTERFACE DEBUG_PRINT=printf + $<$:LWIP_DEBUG> + $<$:CHIP_LIB_TESTS> ) - if(TARGET lwip-cmsis-port) - # Link the emac factory to LwIP port - target_link_libraries(lwip-cmsis-port PUBLIC iotsdk-emac-factory) - endif() + target_include_directories(lwipopts + INTERFACE + ${LWIP_PROJECT_OPTS_DIR} + ) + + # Link the emac factory to LwIP port + target_link_libraries(lwip-cmsis-port PUBLIC iotsdk-emac-factory) endif() # MDH configuration @@ -183,6 +193,15 @@ if(TARGET mcu-driver-hal) INTERFACE DOMAIN_NS=$,1,0> ) + + # Fixing the optimization issue for mcu-driver-hal target in the no-debug build. + # The default -Og optimization causes performance issues for the application. + # We need to replace it with -O2 which is suitable for performance. + # This fix can be removed in the future when the issue will be fixed in SDK directly. + if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + target_compile_options(mcu-driver-hal INTERFACE $<$:-O2>) + target_compile_options(mcu-driver-hal INTERFACE $<$:-O2>) + endif() endif() # Mbedtls config @@ -279,66 +298,84 @@ list(APPEND CONFIG_CHIP_EXTERNAL_TARGETS ) function(sdk_post_build target) - string(REPLACE "_ns" "" APP_NAME ${APP_TARGET}) + string(REPLACE "_ns" "" APP_NAME ${target}) if(TFM_SUPPORT) include(ConvertElfToBin) include(SignTfmImage) - target_elf_to_bin(${APP_TARGET}) - iotsdk_tf_m_sign_image(${APP_TARGET}) - iotsdk_tf_m_merge_images(${APP_TARGET} 0x10000000 0x38000000 0x28060000) ExternalProject_Get_Property(trusted-firmware-m-build BINARY_DIR) + target_elf_to_bin(${target}) + add_custom_command( + TARGET + ${target} + POST_BUILD + DEPENDS + $/${target}.bin + COMMAND + # Sign the non-secure (application) image for TF-M bootloader (BL2)" + python3 ${BINARY_DIR}/install/image_signing/scripts/wrapper/wrapper.py + --layout ${BINARY_DIR}/install/image_signing/layout_files/signing_layout_ns.o + -v ${MCUBOOT_IMAGE_VERSION_NS} + -k ${BINARY_DIR}/install/image_signing/keys/root-RSA-3072_1.pem + --public-key-format full + --align 1 --pad --pad-header -H 0x400 -s auto -d "(0, 0.0.0+0)" + $/${target}.bin + --overwrite-only + --measured-boot-record + $/${target}_signed.bin + VERBATIM + ) + iotsdk_tf_m_merge_images(${target} 0x10000000 0x38000000 0x28060000) # Cleanup add_custom_command( TARGET - ${APP_TARGET} + ${target} POST_BUILD DEPENDS - $/tfm_s_signed.bin - $/${APP_TARGET}.bin - $/${APP_TARGET}_signed.bin - $/${APP_TARGET}_merged.hex - $/${APP_TARGET}_merged.elf + $/${target}.bin + $/${target}_signed.bin + $/${target}_merged.hex + $/${target}_merged.elf COMMAND - # Copy the TF-M secure elf image + # Copy the bootloader and TF-M secure image for debugging purposes ${CMAKE_COMMAND} -E copy + ${BINARY_DIR}/install/outputs/bl2.elf ${BINARY_DIR}/install/outputs/tfm_s.elf - $/ + $/ COMMAND # Rename output file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}_merged.elf - $/${APP_NAME}.elf + $/${target}_merged.elf + $/${APP_NAME}.elf COMMAND rm ARGS -Rf - $/tfm_s_signed.bin - $/${APP_TARGET}.bin - $/${APP_TARGET}_signed.bin - $/${APP_TARGET}_merged.hex - $/${APP_TARGET}_merged.elf + $/${target}.bin + $/${target}_signed.bin + $/${target}_merged.hex + $/${target}_merged.elf VERBATIM ) else() add_custom_command( TARGET - ${APP_TARGET} + ${target} POST_BUILD DEPENDS - $/${APP_TARGET}.elf - $/${APP_TARGET}.map + $/${target}.elf + $/${target}.map COMMAND # Rename output elf file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}.elf - $/${APP_NAME}.elf + $/${target}.elf + $/${APP_NAME}.elf COMMAND # Rename output map file ${CMAKE_COMMAND} -E copy - $/${APP_TARGET}.map - $/${APP_NAME}.map + $/${target}.map + $/${APP_NAME}.map COMMAND rm ARGS -Rf - $/${APP_TARGET}.elf - $/${APP_TARGET}.map + $/${target}.elf + $/${target}.map VERBATIM ) endif() #TFM_SUPPORT diff --git a/config/openiotsdk/ld/cs300_gcc_tfm.ld b/config/openiotsdk/ld/cs300_gcc_tfm.ld index 523c96e8efb77c..fd1d951258d917 100644 --- a/config/openiotsdk/ld/cs300_gcc_tfm.ld +++ b/config/openiotsdk/ld/cs300_gcc_tfm.ld @@ -18,7 +18,7 @@ MEMORY { FLASH (rx) : ORIGIN = ((0x28000000) + (0x60000) + (0x400)), LENGTH = ((0x200000) - (0x400) - (0x800)) - RAM (rwx) : ORIGIN = 0x21000000, LENGTH = 0x100000 + RAM (rwx) : ORIGIN = 0x21000000, LENGTH = 0x200000 } __stack_size__ = 0x1000; diff --git a/config/openiotsdk/lwip/user_lwipopts.h b/config/openiotsdk/lwip/user_lwipopts.h index 8bf38b1129fc56..48d8d4aee81b21 100644 --- a/config/openiotsdk/lwip/user_lwipopts.h +++ b/config/openiotsdk/lwip/user_lwipopts.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2022 Project CHIP Authors + * Copyright (c) 2022-2023 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,12 +19,35 @@ #ifndef USER_LWIPOPTS_H #define USER_LWIPOPTS_H +/** + * MEM_LIBC_MALLOC: use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. + */ +#define MEM_LIBC_MALLOC (1) + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + */ +#define MEMP_NUM_NETBUF 16 + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + * + * Some unit tests required more pbuf buffers. + */ +#ifdef CHIP_LIB_TESTS +#define PBUF_POOL_SIZE (1001) +#endif // CHIP_LIB_TESTS + +/** + * LWIP_STATS : Turn off statistics gathering + */ #define LWIP_STATS (0) -#define LWIP_IGMP (1) -#define LWIP_RAW (1) -#define MEM_LIBC_MALLOC (1) -#define MEM_USE_POOLS (0) +/** + * LWIP_RAW: Enable application layer to hook into the IP layer itself. + */ +#define LWIP_RAW (1) #ifdef LWIP_DEBUG diff --git a/examples/lock-app/openiotsdk/CMakeLists.txt b/examples/lock-app/openiotsdk/CMakeLists.txt index fca486fed81d09..289ecbc893fc54 100644 --- a/examples/lock-app/openiotsdk/CMakeLists.txt +++ b/examples/lock-app/openiotsdk/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,22 +38,6 @@ project(${APP_TARGET} LANGUAGES C CXX ASM) include(sdk) -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - ${OPEN_IOT_SDK_CONFIG}/lwip - ) - - if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") - target_compile_definitions(lwipopts - INTERFACE - LWIP_DEBUG - ) - endif() -endif() - add_executable(${APP_TARGET}) # Application CHIP build configuration @@ -80,6 +64,7 @@ target_sources(${APP_TARGET} ) target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) diff --git a/examples/lock-app/openiotsdk/main/main_ns.cpp b/examples/lock-app/openiotsdk/main/main_ns.cpp index 2b6484b05180c2..aea75e3c58ac7a 100644 --- a/examples/lock-app/openiotsdk/main/main_ns.cpp +++ b/examples/lock-app/openiotsdk/main/main_ns.cpp @@ -22,38 +22,8 @@ #include "openiotsdk_platform.h" -static void app_thread(void * argument) -{ - if (openiotsdk_network_init(true)) - { - ChipLogError(NotSpecified, "Network initialization failed"); - goto exit; - } - - if (openiotsdk_chip_run()) - { - ChipLogError(NotSpecified, "CHIP stack run failed"); - goto exit; - } - - ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application run"); - - while (true) - { - // Add forever delay to ensure proper workload for this thread - osDelay(osWaitForever); - } - - openiotsdk_chip_shutdown(); - -exit: - osThreadTerminate(osThreadGetId()); -} - int main() { - ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application start"); - if (openiotsdk_platform_init()) { ChipLogError(NotSpecified, "Open IoT SDK platform initialization failed"); @@ -66,22 +36,29 @@ int main() return EXIT_FAILURE; } - static const osThreadAttr_t thread_attr = { - .stack_size = 16 * 1024 // Allocate enough stack for app thread - }; + ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application start"); - osThreadId_t appThread = osThreadNew(app_thread, NULL, &thread_attr); - if (appThread == NULL) + if (openiotsdk_network_init(true)) { - ChipLogError(NotSpecified, "Failed to create app thread"); + ChipLogError(NotSpecified, "Network initialization failed"); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) + if (openiotsdk_chip_run()) { - ChipLogError(NotSpecified, "Open IoT SDK platform run failed"); + ChipLogError(NotSpecified, "CHIP stack run failed"); return EXIT_FAILURE; } + ChipLogProgress(NotSpecified, "Open IoT SDK lock-app example application run"); + + while (true) + { + // Add forever delay to ensure proper workload for this thread + osDelay(osWaitForever); + } + + openiotsdk_chip_shutdown(); + return EXIT_SUCCESS; } diff --git a/examples/platform/openiotsdk/app/CMakeLists.txt b/examples/platform/openiotsdk/app/CMakeLists.txt index 4d566fa2e37cd6..c9939ea6a74fcd 100644 --- a/examples/platform/openiotsdk/app/CMakeLists.txt +++ b/examples/platform/openiotsdk/app/CMakeLists.txt @@ -16,6 +16,38 @@ cmake_minimum_required(VERSION 3.21) +# Declare Open IoT SDK startup target +add_library(openiotsdk-startup + OBJECT + openiotsdk_startup_gcc.cpp +) + +target_link_libraries(openiotsdk-startup + PUBLIC + # iotsdk-serial-retarget contains the UART but we don't need the retarget part + $,EXCLUDE,.*gcc_retarget.*> + cmsis-rtos-api + mcu-driver-hal +) + +target_link_options(openiotsdk-startup + PUBLIC + "-Wl,--wrap,__malloc_lock" + "-Wl,--wrap,__malloc_unlock" +) + +if(TFM_SUPPORT) + target_compile_definitions(openiotsdk-startup + PUBLIC + TFM_SUPPORT + ) + + target_link_libraries(openiotsdk-startup + PUBLIC + tfm-ns-interface + ) +endif() + # Declare Open IoT SDK app interface target add_library(openiotsdk-app openiotsdk_platform.cpp @@ -31,7 +63,6 @@ target_include_directories(openiotsdk-app target_link_libraries(openiotsdk-app PUBLIC chip - $ ) if(TFM_SUPPORT) diff --git a/examples/platform/openiotsdk/app/openiotsdk_platform.cpp b/examples/platform/openiotsdk/app/openiotsdk_platform.cpp index 829980d31f2392..28b6da91f5c176 100644 --- a/examples/platform/openiotsdk/app/openiotsdk_platform.cpp +++ b/examples/platform/openiotsdk/app/openiotsdk_platform.cpp @@ -1,5 +1,4 @@ /* - * * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * @@ -31,6 +30,7 @@ #include #include #include +#include #include #include @@ -53,6 +53,7 @@ using namespace ::chip; using namespace ::chip::Platform; using namespace ::chip::DeviceLayer; +using namespace ::chip::Logging::Platform; constexpr EndpointId kNetworkCommissioningEndpointSecondary = 0xFFFE; @@ -168,7 +169,8 @@ static int get_psa_images_details() int openiotsdk_platform_init(void) { int ret; - osKernelState_t state; + + ois_logging_init(); ret = mbedtls_platform_setup(NULL); if (ret) @@ -178,13 +180,6 @@ int openiotsdk_platform_init(void) } #ifdef TFM_SUPPORT - ret = tfm_ns_interface_init(); - if (ret != 0) - { - ChipLogError(NotSpecified, "TF-M initialization failed: %d", ret); - return EXIT_FAILURE; - } - ret = get_psa_images_details(); if (ret != 0) { @@ -193,20 +188,6 @@ int openiotsdk_platform_init(void) } #endif // TFM_SUPPORT - ret = osKernelInitialize(); - if (ret != osOK) - { - ChipLogError(NotSpecified, "osKernelInitialize failed: %d", ret); - return EXIT_FAILURE; - } - - state = osKernelGetState(); - if (state != osKernelReady) - { - ChipLogError(NotSpecified, "Kernel not ready: %d", state); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; } @@ -214,10 +195,6 @@ int openiotsdk_chip_init(void) { CHIP_ERROR err; -#if NDEBUG - chip::Logging::SetLogFilter(chip::Logging::LogCategory::kLogCategory_Progress); -#endif - err = MemoryInit(); if (err != CHIP_NO_ERROR) { @@ -244,18 +221,6 @@ int openiotsdk_chip_init(void) return EXIT_SUCCESS; } -int openiotsdk_platform_run(void) -{ - int ret = osKernelStart(); - if (ret != osOK) - { - ChipLogError(NotSpecified, "Failed to start kernel: %d", ret); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - int openiotsdk_network_init(bool wait) { int ret; @@ -289,12 +254,10 @@ int openiotsdk_network_init(bool wait) int openiotsdk_chip_run(void) { - CHIP_ERROR err; - #ifdef USE_CHIP_DATA_MODEL // Init ZCL Data Model and start server static chip::CommonCaseDeviceServerInitParams initParams; - err = initParams.InitializeStaticResourcesBeforeServerInit(); + CHIP_ERROR err = initParams.InitializeStaticResourcesBeforeServerInit(); if (err != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Initialize static resources before server init failed: %s", err.AsString()); diff --git a/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp b/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp new file mode 100644 index 00000000000000..bf143db5d32877 --- /dev/null +++ b/examples/platform/openiotsdk/app/openiotsdk_startup_gcc.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2006-2022 ARM Limited + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootstrap/mbed_critical.h" +#include "cmsis_os2.h" +extern "C" { +#include "hal/serial_api.h" +} + +#ifdef TFM_SUPPORT +extern "C" uint32_t tfm_ns_interface_init(void); +#endif // TFM_SUPPORT + +#define CALLER_ADDR() __builtin_extract_return_addr(__builtin_return_address(0)) + +// Consider reducing the baudrate if the serial is used as input and characters are lost +extern "C" mdh_serial_t * get_example_serial(); +#ifndef IOT_SDK_APP_SERIAL_BAUDRATE +#define IOT_SDK_APP_SERIAL_BAUDRATE 921600 +#endif + +// main thread declaration +// The thread object and associated stack are statically allocated +#ifndef IOT_SDK_APP_MAIN_STACK_SIZE +#define IOT_SDK_APP_MAIN_STACK_SIZE 16 * 1024 +#endif +static void main_thread(void * argument); +alignas(8) static char main_thread_stack[IOT_SDK_APP_MAIN_STACK_SIZE]; +alignas(8) static uint8_t main_thread_storage[100] __attribute__((section(".bss.os.thread.cb"))); + +// malloc mutex declaration +static osMutexId_t malloc_mutex; +alignas(8) static uint8_t malloc_mutex_obj[80]; + +// C runtime import: constructor initialization and main +extern "C" void __libc_init_array(void); +extern "C" int main(void); + +// IOT SDK serial declarations +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +static mdh_serial_t * serial; + +// stdout tx mutex declaration +static osMutexId_t tx_mutex; +alignas(8) static uint8_t tx_mutex_obj[80]; + +static int serial_out(const char * str, size_t len) +{ + if (str == NULL) + { + return -1; + } + + if (len == 0) + { + return 0; + } + + size_t written = 0; + while (written++ < len) + { + mdh_serial_put_data(serial, *str++); + } + + return len; +} + +// prints while printf is not usable yet +static void bare_metal_print(const char * str) +{ + serial_out(str, strlen(str)); +} + +static void serial_irq_cb(void * p_instance, mdh_serial_irq_type_t event); + +struct CriticalSection +{ + CriticalSection() { core_util_critical_section_enter(); } + CriticalSection(const CriticalSection &) = delete; + CriticalSection & operator=(const CriticalSection &) = delete; + ~CriticalSection() { core_util_critical_section_exit(); } +}; + +// RX buffer +template +struct RingBuffer +{ + static_assert(Size > 0); + RingBuffer() : head_(0), tail_(0), full_(false) {} + void put(uint8_t data); + bool get(uint8_t * data); + size_t get(uint8_t * data, size_t size); + bool empty() const; + bool full() const; + size_t size() const; + +private: + uint8_t buffer_[Size]; + size_t head_ = 0; + size_t tail_ = 0; + bool full_ = false; + + void increment(size_t & val); + void increment(size_t & val, size_t incr); + bool empty_() const; + size_t size_() const; +}; + +// Buffer that receive data from serial +static RingBuffer<128> rx_buffer; + +/* + * This function override startup sequence. Instead of releasing control to the C runtime + * the following operations are performed: + * + * - initialize the serial (low level) + * - initialize RTOS + * - Start the RTOS with the main thread + */ +extern "C" void mbed_sdk_init(void) +{ + serial = get_example_serial(); + mdh_serial_set_baud(serial, IOT_SDK_APP_SERIAL_BAUDRATE); + + int ret = osKernelInitialize(); + if (ret != osOK) + { + bare_metal_print("osKernelInitialize failed\r\n"); + abort(); + } + + // Create main thread used to run the application + { + osThreadAttr_t main_thread_attr = { + .name = "main", + .cb_mem = &main_thread_storage, + .cb_size = sizeof(main_thread_storage), + .stack_mem = main_thread_stack, + .stack_size = sizeof(main_thread_stack), + .priority = osPriorityNormal, + }; + + osThreadId_t main_thread_id = osThreadNew(main_thread, NULL, &main_thread_attr); + if (main_thread_id == NULL) + { + bare_metal_print("Main thread creation failed\r\n"); + abort(); + } + } + + ret = osKernelStart(); + // Note osKernelStart should never return + bare_metal_print("Kernel failed to start\r\n"); + abort(); +} + +/** + * Main thread + * - Initialize TF-M + * - Initialize the toolchain: + * - Setup mutexes for malloc and environment + * - Construct global objects + * - Run the main + */ +static void main_thread(void * argument) +{ + // Create Malloc mutex + { + osMutexAttr_t malloc_mutex_attr = { .name = "malloc_mutex", + .attr_bits = osMutexRecursive | osMutexPrioInherit, + .cb_mem = &malloc_mutex_obj, + .cb_size = sizeof(malloc_mutex_obj) }; + + malloc_mutex = osMutexNew(&malloc_mutex_attr); + if (malloc_mutex == NULL) + { + bare_metal_print("Failed to initialize malloc mutex\r\n"); + abort(); + } + } + + // Create stdout TX mutex + { + osMutexAttr_t tx_mutex_attr = { + .name = "tx_mutex", .attr_bits = osMutexPrioInherit, .cb_mem = &tx_mutex_obj, .cb_size = sizeof(tx_mutex_obj) + }; + + tx_mutex = osMutexNew(&tx_mutex_attr); + if (tx_mutex == NULL) + { + bare_metal_print("Failed to initialize tx mutex\r\n"); + abort(); + } + } + + // Disable buffering to let write and fwrite call straight into _write + setvbuf(stdout, /* buffer */ NULL, _IONBF, /* size */ 0); + setvbuf(stderr, /* buffer */ NULL, _IONBF, /* size */ 0); + setvbuf(stdin, /* buffer */ NULL, _IONBF, /* size */ 0); + + // It is safe to use printf from this point + +#ifdef TFM_SUPPORT + { + int ret = tfm_ns_interface_init(); + if (ret != 0) + { + bare_metal_print("TF-M initialization failed\r\n"); + abort(); + } + } +#endif + + /* Run the C++ global object constructors */ + __libc_init_array(); + + // Note: Reception on the serial port is buffered to mitigate bytes lost + mdh_serial_set_irq_callback(serial, serial_irq_cb, serial); + mdh_serial_set_irq_availability(serial, MDH_SERIAL_IRQ_TYPE_RX, true); + + // It is safe to receive data on serial from this point + + int return_code = main(); + + exit(return_code); +} + +/* + * Override of lock/unlock functions for malloc. + */ +extern "C" void __wrap___malloc_lock(struct _reent * reent) +{ + osMutexAcquire(malloc_mutex, osWaitForever); +} + +extern "C" void __wrap___malloc_unlock(struct _reent * reent) +{ + osMutexRelease(malloc_mutex); +} + +/* + * Override of new/delete operators. + * The override add a trace when a non-throwing new fails. + */ + +void * operator new(std::size_t count) +{ + void * buffer = malloc(count); + if (!buffer) + { + printf("operator new failure from %p\r\n", CALLER_ADDR()); + abort(); + } + return buffer; +} + +void * operator new[](std::size_t count) +{ + void * buffer = malloc(count); + if (!buffer) + { + printf("operator new[] failure from %p\r\n", CALLER_ADDR()); + abort(); + } + return buffer; +} + +void * operator new(std::size_t count, const std::nothrow_t & tag) +{ + return malloc(count); +} + +void * operator new[](std::size_t count, const std::nothrow_t & tag) +{ + return malloc(count); +} + +void operator delete(void * ptr) +{ + free(ptr); +} + +void operator delete(void * ptr, std::size_t) +{ + free(ptr); +} + +void operator delete[](void * ptr) +{ + free(ptr); +} + +void operator delete[](void * ptr, std::size_t) +{ + free(ptr); +} + +/* + * Override of _sbrk + * It prints an error when the system runs out of memory in the heap segment. + */ + +#undef errno +extern "C" int errno; + +extern "C" char __end__; +extern "C" char __HeapLimit; + +extern "C" void * _sbrk(int incr) +{ + static uint32_t heap = (uint32_t) &__end__; + uint32_t prev_heap = heap; + uint32_t new_heap = heap + incr; + + /* __HeapLimit is end of heap section */ + if (new_heap > (uint32_t) &__HeapLimit) + { + printf("_sbrk failure, incr = %d, new_heap = 0x%08lX\r\n", incr, new_heap); + errno = ENOMEM; + return (void *) -1; + } + + heap = new_heap; + return (void *) prev_heap; +} + +// Override exit +extern "C" void _exit(int return_code) +{ + // display exit reason + if (return_code) + { + printf("Application exited with %d\r\n", return_code); + } + + // flush stdio + fflush(stdout); + fflush(stderr); + + // lock the kernel and go to sleep forever + osKernelLock(); + while (1) + { + __WFE(); + } +} + +// Calling a FreeRTOS API is illegal while scheduler is suspended. +// Therefore we provide this custom implementation which relies on underlying +// safety of malloc. +extern "C" void * pvPortMalloc(size_t size) +{ + return malloc(size); +} + +extern "C" void vPortFree(void * ptr) +{ + free(ptr); +} + +// Retarget of low level read and write + +extern "C" int _write(int fd, const char * str, size_t len) +{ + if (fd != STDOUT_FILENO && fd != STDERR_FILENO) + { + return -1; + } + + osMutexAcquire(tx_mutex, osWaitForever); + int len_written = serial_out(str, len); + osMutexRelease(tx_mutex); + + return len_written; +} + +extern "C" int _read(int fd, char * str, size_t len) +{ + if (fd != STDIN_FILENO) + { + return -1; + } + + return rx_buffer.get((uint8_t *) str, len); +} + +static void serial_irq_cb(void * p_instance, mdh_serial_irq_type_t event) +{ + if ((event != MDH_SERIAL_IRQ_TYPE_RX) || !p_instance) + { + return; + } + + mdh_serial_t * serial = reinterpret_cast(p_instance); + + if (event == MDH_SERIAL_IRQ_TYPE_RX) + { + while (mdh_serial_is_readable(serial)) + { + // Gather as much as possible data bytes + rx_buffer.put(mdh_serial_get_data(serial)); + } + } +} + +// Ring buffer implementation +template +void RingBuffer::put(uint8_t data) +{ + CriticalSection _; + buffer_[head_] = data; + increment(head_); + + if (full_) + { + tail_ = head_; + } + else if (head_ == tail_) + { + full_ = true; + } +} + +template +bool RingBuffer::get(uint8_t * data) +{ + CriticalSection _; + if (empty_()) + { + return false; + } + + *data = buffer_[tail_]; + increment(tail_); + full_ = false; + + return true; +} + +template +size_t RingBuffer::get(uint8_t * data, size_t size) +{ + if (size == 0) + { + return 0; + } + + if (size == 1) + { + return get(data) ? 1 : 0; + } + + CriticalSection _; + if (empty_()) + { + return 0; + } + + // resize dest to fit with available data + size = std::min(size_(), size); + + if (tail_ + size > Size) + { + auto it = std::copy(buffer_ + tail_, buffer_ + Size, data); + // set to the future tail + tail_ = size - (Size - tail_); + std::copy(buffer_, buffer_ + tail_, it); + } + else + { + std::copy(buffer_ + tail_, buffer_ + size, data); + increment(tail_, size); + } + + full_ = false; + + return size; +} + +template +bool RingBuffer::empty() const +{ + CriticalSection _; + return empty_(); +} + +template +bool RingBuffer::full() const +{ + CriticalSection _; + return bool(full_); +} + +template +size_t RingBuffer::size() const +{ + CriticalSection _; + return size_(); +} + +template +void RingBuffer::increment(size_t & val) +{ + ++val; + assert(val <= Size); + + if (val == Size) + { + val = 0; + } +} + +template +void RingBuffer::increment(size_t & val, size_t incr) +{ + val += incr; + + if (val >= Size) + { + val = val - Size; + } +} + +template +bool RingBuffer::empty_() const +{ + return head_ == tail_ && !bool(full_); +} + +template +size_t RingBuffer::size_() const +{ + if (full_) + { + return Size; + } + else if (head_ < tail_) + { + return Size - (tail_ - head_); + } + else + { + return head_ - tail_; + }; +} diff --git a/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h b/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h index d124a00450785e..682f0f50747373 100755 --- a/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h +++ b/examples/platform/openiotsdk/tf-m/targets/an552/partition/region_defs.h @@ -101,9 +101,9 @@ #define NS_CODE_SIZE (IMAGE_NS_CODE_SIZE) #define NS_CODE_LIMIT (NS_CODE_START + NS_CODE_SIZE - 1) -/* Non-Secure Data stored in ISRAM0 */ +/* Non-Secure Data stored in ISRAM0 + ISRAM1 */ #define NS_DATA_START (ISRAM0_BASE_NS) -#define NS_DATA_SIZE (ISRAM0_SIZE) +#define NS_DATA_SIZE (ISRAM0_SIZE + ISRAM1_SIZE) #define NS_DATA_LIMIT (NS_DATA_START + NS_DATA_SIZE - 1) /* NS partition information is used for MPC and SAU configuration */ diff --git a/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c b/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c index 9b0338b38c8532..0358426779cafc 100755 --- a/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c +++ b/examples/platform/openiotsdk/tf-m/targets/an552/target_cfg.c @@ -171,6 +171,13 @@ enum tfm_plat_err_t nvic_interrupt_enable(void) return TFM_PLAT_ERR_SYSTEM_ERR; } + ret = Driver_ISRAM1_MPC.EnableInterrupt(); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Enable MPC interrupt for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + ret = Driver_SRAM_MPC.EnableInterrupt(); if (ret != ARM_DRIVER_OK) { @@ -302,6 +309,21 @@ enum tfm_plat_err_t mpc_init_cfg(void) return TFM_PLAT_ERR_SYSTEM_ERR; } + /* ISRAM1 is allocated for NS data, so whole range is set to non-secure + * accesible. */ + ret = Driver_ISRAM1_MPC.Initialize(); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Initialize MPC for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + ret = Driver_ISRAM1_MPC.ConfigRegion(MPC_ISRAM1_RANGE_BASE_NS, MPC_ISRAM1_RANGE_LIMIT_NS, ARM_MPC_ATTR_NONSECURE); + if (ret != ARM_DRIVER_OK) + { + ERROR_MSG("Failed to Configure MPC for ISRAM1!"); + return TFM_PLAT_ERR_SYSTEM_ERR; + } + /* Configuring additional flash partition. */ ret = Driver_SRAM_MPC.Initialize(); if (ret != ARM_DRIVER_OK) @@ -331,10 +353,7 @@ enum tfm_plat_err_t mpc_init_cfg(void) return TFM_PLAT_ERR_SYSTEM_ERR; } - /* Lock down not used MPC's */ - Driver_ISRAM1_MPC.LockDown(); - - /* SRAM and ISRAM0 MPCs left unlocked as they are not reset if NVIC system + /* SRAM, ISRAM0 and ISRAM1 MPCs left unlocked as they are not reset if NVIC system * reset asserted. */ @@ -351,6 +370,8 @@ void mpc_revert_non_secure_to_secure_cfg(void) { Driver_ISRAM0_MPC.ConfigRegion(MPC_ISRAM0_RANGE_BASE_S, MPC_ISRAM0_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); + Driver_ISRAM1_MPC.ConfigRegion(MPC_ISRAM1_RANGE_BASE_S, MPC_ISRAM1_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); + Driver_SRAM_MPC.ConfigRegion(MPC_SRAM_RANGE_BASE_S, MPC_SRAM_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); Driver_QSPI_MPC.ConfigRegion(MPC_QSPI_RANGE_BASE_S, MPC_QSPI_RANGE_LIMIT_S, ARM_MPC_ATTR_SECURE); @@ -365,6 +386,7 @@ void mpc_revert_non_secure_to_secure_cfg(void) void mpc_clear_irq(void) { Driver_ISRAM0_MPC.ClearInterrupt(); + Driver_ISRAM1_MPC.ClearInterrupt(); Driver_SRAM_MPC.ClearInterrupt(); Driver_QSPI_MPC.ClearInterrupt(); } diff --git a/examples/shell/openiotsdk/CMakeLists.txt b/examples/shell/openiotsdk/CMakeLists.txt index d17908bf6021e7..13ea384b261ff6 100644 --- a/examples/shell/openiotsdk/CMakeLists.txt +++ b/examples/shell/openiotsdk/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,22 +35,6 @@ project(${APP_TARGET} LANGUAGES C CXX ASM) include(sdk) -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - ${OPEN_IOT_SDK_CONFIG}/lwip - ) - - if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") - target_compile_definitions(lwipopts - INTERFACE - LWIP_DEBUG - ) - endif() -endif() - add_executable(${APP_TARGET}) # Application CHIP build configuration @@ -59,6 +43,16 @@ include(chip) add_subdirectory(${OPEN_IOT_SDK_EXAMPLE_COMMON}/app ./app_build) +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_SERIAL_BAUDRATE=9600 +) + +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_MAIN_STACK_SIZE=8192 +) + target_include_directories(${APP_TARGET} PRIVATE main/include @@ -73,6 +67,7 @@ target_sources(${APP_TARGET} ) target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) diff --git a/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h b/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h index 7089ae47a8038f..1d5b781c19e7ef 100644 --- a/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h +++ b/examples/shell/openiotsdk/main/include/CHIPProjectConfig.h @@ -28,3 +28,5 @@ #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 #define CHIP_DISPATCH_EVENT_LONG_DISPATCH_TIME_WARNING_THRESHOLD_MS 500 + +#define CHIP_SHELL_PROMPT "Enter command:\r\n" diff --git a/examples/shell/openiotsdk/main/main_ns.cpp b/examples/shell/openiotsdk/main/main_ns.cpp index c832b2df4e49cf..056ab2c66f62ff 100644 --- a/examples/shell/openiotsdk/main/main_ns.cpp +++ b/examples/shell/openiotsdk/main/main_ns.cpp @@ -29,41 +29,19 @@ using namespace ::chip; using namespace ::chip::Shell; -static void app_thread(void * argument) +int main() { - int ret; - - if (openiotsdk_network_init(true)) - { - ChipLogError(Shell, "Network initialization failed"); - goto exit; - } - - // Initialize the default streamer that was linked. - ret = Engine::Root().Init(); - if (ret) + if (openiotsdk_platform_init()) { - ChipLogError(Shell, "Streamer initialization failed [%d]", ret); - goto exit; + ChipLogError(Shell, "Open IoT SDK platform initialization failed"); + return EXIT_FAILURE; } - cmd_misc_init(); - - ChipLogProgress(Shell, "Open IoT SDK shell example application run"); - - Engine::Root().RunMainLoop(); - -exit: - osThreadTerminate(osThreadGetId()); -} - -int main() -{ ChipLogProgress(Shell, "Open IoT SDK shell example application start"); - if (openiotsdk_platform_init()) + if (openiotsdk_network_init(true)) { - ChipLogError(Shell, "Open IoT SDK platform initialization failed"); + ChipLogError(Shell, "Network initialization failed"); return EXIT_FAILURE; } @@ -73,22 +51,19 @@ int main() return EXIT_FAILURE; } - static const osThreadAttr_t thread_attr = { - .stack_size = 8 * 1024 // Allocate enough stack for app thread - }; - - osThreadId_t appThread = osThreadNew(app_thread, NULL, &thread_attr); - if (appThread == NULL) + // Initialize the default streamer that was linked. + int ret = Engine::Root().Init(); + if (ret) { - ChipLogError(Shell, "Failed to create app thread"); + ChipLogError(Shell, "Streamer initialization failed [%d]", ret); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) - { - ChipLogError(Shell, "Open IoT SDK platform run failed"); - return EXIT_FAILURE; - } + cmd_misc_init(); + + ChipLogProgress(Shell, "Open IoT SDK shell example application run"); + + Engine::Root().RunMainLoop(); return EXIT_SUCCESS; } diff --git a/scripts/build/builders/openiotsdk.py b/scripts/build/builders/openiotsdk.py index 7de737e3ea60a4..7f3cd682d86ba3 100644 --- a/scripts/build/builders/openiotsdk.py +++ b/scripts/build/builders/openiotsdk.py @@ -64,6 +64,7 @@ def generate(self): shlex.quote(self.toolchain_path)), '-DCMAKE_SYSTEM_PROCESSOR={}'.format( self.system_processor), + '-DCMAKE_BUILD_TYPE=Release', ], title='Generating ' + self.identifier) def _build(self): diff --git a/scripts/build/testdata/dry_run_openiotsdk-lock.txt b/scripts/build/testdata/dry_run_openiotsdk-lock.txt index bbe17977600ec6..a0c36ee27f53ad 100644 --- a/scripts/build/testdata/dry_run_openiotsdk-lock.txt +++ b/scripts/build/testdata/dry_run_openiotsdk-lock.txt @@ -2,7 +2,7 @@ cd "{root}" # Generating openiotsdk-lock -cmake -GNinja -S {root}/examples/lock-app/openiotsdk -B {out}/openiotsdk-lock --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 +cmake -GNinja -S {root}/examples/lock-app/openiotsdk -B {out}/openiotsdk-lock --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 -DCMAKE_BUILD_TYPE=Release # Building openiotsdk-lock cmake --build {out}/openiotsdk-lock diff --git a/scripts/build/testdata/dry_run_openiotsdk-shell.txt b/scripts/build/testdata/dry_run_openiotsdk-shell.txt index ef1a848a14ac88..6c7c63befb4e7a 100644 --- a/scripts/build/testdata/dry_run_openiotsdk-shell.txt +++ b/scripts/build/testdata/dry_run_openiotsdk-shell.txt @@ -2,7 +2,7 @@ cd "{root}" # Generating openiotsdk-shell -cmake -GNinja -S {root}/examples/shell/openiotsdk -B {out}/openiotsdk-shell --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 +cmake -GNinja -S {root}/examples/shell/openiotsdk -B {out}/openiotsdk-shell --toolchain=toolchains/toolchain-arm-none-eabi-gcc.cmake -DCMAKE_SYSTEM_PROCESSOR=cortex-m55 -DCMAKE_BUILD_TYPE=Release # Building openiotsdk-shell cmake --build {out}/openiotsdk-shell diff --git a/scripts/examples/openiotsdk_example.sh b/scripts/examples/openiotsdk_example.sh index fbba7a343f0c7c..11aeffbe08601f 100755 --- a/scripts/examples/openiotsdk_example.sh +++ b/scripts/examples/openiotsdk_example.sh @@ -1,7 +1,7 @@ #!/bin/bash # -# Copyright (c) 2020 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ # Build and/or run Open IoT SDK examples. -IS_TEST=0 NAME="$(basename "$0")" HERE="$(dirname "$0")" CHIP_ROOT="$(realpath "$HERE"/../..)" @@ -30,6 +29,7 @@ EXAMPLE_PATH="" BUILD_PATH="" TOOLCHAIN=arm-none-eabi-gcc DEBUG=false +LWIP_DEBUG=false EXAMPLE="" FVP_BIN=FVP_Corstone_SSE-300_Ethos-U55 GDB_PLUGIN="$FAST_MODEL_PLUGINS_PATH/GDBRemoteConnection.so" @@ -37,11 +37,18 @@ OIS_CONFIG="$CHIP_ROOT/config/openiotsdk" FVP_CONFIG_FILE="$OIS_CONFIG/fvp/cs300.conf" EXAMPLE_TEST_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/integration-tests" TELNET_TERMINAL_PORT=5000 +TELNET_CONNECTION_PORT="" FAILED_TESTS=0 +IS_UNIT_TEST=0 FVP_NETWORK="user" readarray -t TEST_NAMES <"$CHIP_ROOT"/src/test_driver/openiotsdk/unit-tests/testnames.txt +declare -a SUPPORTED_APP_NAMES +SUPPORTED_APP_NAMES+=("shell") +SUPPORTED_APP_NAMES+=("lock-app") +SUPPORTED_APP_NAMES+=("unit-tests") + function show_usage() { cat < Action to execute - -d,--debug Build in debug mode - -p,--path Build path - -n,--network FVP network interface name + -h,--help Show this help + -c,--clean Clean target build + -s,--scratch Remove build directory at all before building + -C,--command Action to execute + -d,--debug Build in debug mode + -l,--lwipdebug Build with LwIP debug logs support + -p,--path Build path + -n,--network FVP network interface name Examples: - shell - lock-app - unit-tests +EOF + + for app in "${SUPPORTED_APP_NAMES[@]}"; do + echo " $app" + done + + cat < parameter as [test_name] - -The "test" command can be used for all supported examples expect the unit-tests. +Use "test" command without a specific test name, runs all supported unit tests. EOF } @@ -104,8 +113,9 @@ function build_with_cmake() { BUILD_OPTIONS+=(-DCMAKE_BUILD_TYPE=Debug) fi - # Activate Matter environment - source "$CHIP_ROOT"/scripts/activate.sh + if "$LWIP_DEBUG"; then + BUILD_OPTIONS+=(-DCONFIG_CHIP_OPEN_IOT_SDK_LWIP_DEBUG=YES) + fi cmake -G Ninja -S "$EXAMPLE_PATH" -B "$BUILD_PATH" --toolchain="$TOOLCHAIN_PATH" "${BUILD_OPTIONS[@]}" cmake --build "$BUILD_PATH" @@ -121,18 +131,18 @@ function run_fvp() { exit 1 fi - if [[ $IS_TEST -eq 0 ]]; then - EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" - else - EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" - fi - # Check if executable file exists if ! [ -f "$EXAMPLE_EXE_PATH" ]; then echo "Error: $EXAMPLE_EXE_PATH does not exist." >&2 exit 1 fi + # Check if FVP GDB plugin file exists + if "$DEBUG" && ! [ -f "$GDB_PLUGIN" ]; then + echo "Error: $GDB_PLUGIN does not exist. Ensure Fast Model extensions are mounted." >&2 + exit 1 + fi + RUN_OPTIONS=(-C mps3_board.telnetterminal0.start_port="$TELNET_TERMINAL_PORT") RUN_OPTIONS+=(--quantum=25) @@ -148,39 +158,46 @@ function run_fvp() { echo "Running $EXAMPLE_EXE_PATH with options: ${RUN_OPTIONS[@]}" - "$FVP_BIN" "${RUN_OPTIONS[@]}" -f "$FVP_CONFIG_FILE" --application "$EXAMPLE_EXE_PATH" >/dev/null 2>&1 & + # Run the FVP + "$FVP_BIN" "${RUN_OPTIONS[@]}" -f "$FVP_CONFIG_FILE" --application "$EXAMPLE_EXE_PATH" 2>&1 >/tmp/FVP_run_$$ & FVP_PID=$! - sleep 1 - - if [[ $IS_TEST -eq 1 ]]; then - set +e - expect <"$EXAMPLE_PATH"/test_report.json + + # Wait for FVP to start and exist the output file + timeout=0 + while [ ! -e /tmp/FVP_run_$$ ]; do + timeout=$((timeout + 1)) + if [ "$timeout" -ge 5 ]; then + echo "Error: FVP start failed" >&2 + break + fi + sleep 1 + done + + while IFS= read -t 5 -r line; do + if [[ $line == *"Listening for serial connection on port"* ]]; then + TELNET_CONNECTION_PORT="${line##* }" + break + fi + done &2 fi - # stop the fvp - kill -9 "$FVP_PID" || true - set -e - sleep 1 + # Stop the FVP + kill -SIGTERM "$FVP_PID" + # Wait for the FVP stop + while kill -0 "$FVP_PID"; do + sleep 1 + done + rm -rf /tmp/FVP_run_$$ } function run_test() { - EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" # Check if executable file exists if ! [ -f "$EXAMPLE_EXE_PATH" ]; then echo "Error: $EXAMPLE_EXE_PATH does not exist." >&2 @@ -193,9 +210,6 @@ function run_test() { exit 1 fi - # Activate Matter environment with pytest - source "$CHIP_ROOT"/scripts/activate.sh - # Check if pytest exists if ! [ -x "$(command -v pytest)" ]; then echo "Error: pytest not installed." >&2 @@ -208,25 +222,25 @@ function run_test() { TEST_OPTIONS+=(--networkInterface="$FVP_NETWORK") fi - if [[ -f $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json ]]; then - rm -rf "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json + if [[ -f $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json ]]; then + rm -rf "$EXAMPLE_TEST_PATH/test_report_$EXAMPLE".json fi set +e - pytest --json-report --json-report-summary --json-report-file="$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json --binaryPath="$EXAMPLE_EXE_PATH" --fvp="$FVP_BIN" --fvpConfig="$FVP_CONFIG_FILE" "${TEST_OPTIONS[@]}" "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_app.py + pytest --json-report --json-report-summary --json-report-file="$EXAMPLE_TEST_PATH"/test_report_"$EXAMPLE".json --binaryPath="$EXAMPLE_EXE_PATH" --fvp="$FVP_BIN" --fvpConfig="$FVP_CONFIG_FILE" "${TEST_OPTIONS[@]}" "$EXAMPLE_TEST_PATH"/test_app.py set -e - if [[ ! -f $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json ]]; then + if [[ ! -f $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json ]]; then exit 1 else - if [[ $(jq '.summary | has("failed")' $EXAMPLE_TEST_PATH/$EXAMPLE/test_report.json) == true ]]; then - FAILED_TESTS=$(jq '.summary.failed' "$EXAMPLE_TEST_PATH/$EXAMPLE"/test_report.json) + if [[ $(jq '.summary | has("failed")' $EXAMPLE_TEST_PATH/test_report_$EXAMPLE.json) == true ]]; then + FAILED_TESTS=$((FAILED_TESTS + $(jq '.summary.failed' "$EXAMPLE_TEST_PATH"/test_report_"$EXAMPLE".json))) fi fi } -SHORT=C:,p:,d:,n:,c,s,h -LONG=command:,path:,debug:,network:,clean,scratch,help +SHORT=C:,p:,d:,l:,n:,c,s,h +LONG=command:,path:,debug:,lwipdebug:,network:,clean,scratch,help OPTS=$(getopt -n build --options "$SHORT" --longoptions "$LONG" -- "$@") eval set -- "$OPTS" @@ -253,6 +267,10 @@ while :; do DEBUG=$2 shift 2 ;; + -l | --lwipdebug) + LWIP_DEBUG=$2 + shift 2 + ;; -p | --path) BUILD_PATH=$CHIP_ROOT/$2 shift 2 @@ -278,12 +296,18 @@ if [[ $# -lt 1 ]]; then exit 1 fi -case "$1" in - shell | unit-tests | lock-app) - EXAMPLE=$1 - ;; +EXAMPLE=$1 + +if [[ ! " ${SUPPORTED_APP_NAMES[@]} " =~ " ${EXAMPLE} " ]]; then + echo "Wrong example name" + show_usage + exit 2 +fi + +case "$COMMAND" in + build | run | test | build-run) ;; *) - echo "Wrong example name" + echo "Wrong command definition" show_usage exit 2 ;; @@ -292,13 +316,8 @@ esac if [[ "$EXAMPLE" == "unit-tests" ]]; then if [ ! -z "$2" ]; then if [[ " ${TEST_NAMES[*]} " =~ " $2 " ]]; then - if [[ "$COMMAND" != *"run"* ]]; then - echo "Test suites can only accept --command run" - show_usage - exit 2 - fi EXAMPLE=$2 - echo "Run specific unit test $EXAMPLE" + echo "Use specific unit test $EXAMPLE" elif [[ "$2" == "all" ]]; then echo "Use all unit tests" else @@ -309,54 +328,38 @@ if [[ "$EXAMPLE" == "unit-tests" ]]; then else echo "Use all unit tests" fi - IS_TEST=1 + EXAMPLE_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/unit-tests" + IS_UNIT_TEST=1 +else + EXAMPLE_PATH="$CHIP_ROOT/examples/$EXAMPLE/openiotsdk" fi -case "$COMMAND" in - build | run | test | build-run) ;; - *) - echo "Wrong command definition" - show_usage - exit 2 - ;; -esac - TOOLCHAIN_PATH="toolchains/toolchain-$TOOLCHAIN.cmake" -if [[ $IS_TEST -eq 0 ]]; then - EXAMPLE_PATH="$CHIP_ROOT/examples/$EXAMPLE/openiotsdk" -else - EXAMPLE_PATH="$CHIP_ROOT/src/test_driver/openiotsdk/unit-tests" - if [[ -f $EXAMPLE_PATH/test_report.json ]]; then - rm -rf "$EXAMPLE_PATH"/test_report.json - fi - echo "{}" >"$EXAMPLE_PATH"/test_report.json -fi - if [ -z "$BUILD_PATH" ]; then BUILD_PATH="$EXAMPLE_PATH/build" fi +# Activate Matter environment +source "$CHIP_ROOT"/scripts/activate.sh + +if [[ $IS_UNIT_TEST -eq 0 ]]; then + EXAMPLE_EXE_PATH="$BUILD_PATH/chip-openiotsdk-$EXAMPLE-example.elf" + EXAMPLE_TEST_PATH+="/$EXAMPLE" +else + EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" + EXAMPLE_TEST_PATH+="/unit-tests" +fi + if [[ "$COMMAND" == *"build"* ]]; then build_with_cmake fi if [[ "$COMMAND" == *"run"* ]]; then - # If user wants to run unit-tests we need to loop through all test names if [[ "$EXAMPLE" == "unit-tests" ]]; then - if "$DEBUG"; then - echo "You have to specify the test suites to run in debug mode" - show_usage - exit 2 - else - for NAME in "${TEST_NAMES[@]}"; do - EXAMPLE=$NAME - echo "$EXAMPLE_PATH" - echo "Run specific unit test $EXAMPLE" - run_fvp - done - echo "Failed tests total: $FAILED_TESTS" - fi + echo "You have to specify the test suites to run" + show_usage + exit 2 else run_fvp fi @@ -364,15 +367,16 @@ fi if [[ "$COMMAND" == *"test"* ]]; then if [[ "$EXAMPLE" == "unit-tests" ]]; then - echo "The test command can not be applied to the unit-tests example" - show_usage - exit 2 + for NAME in "${TEST_NAMES[@]}"; do + EXAMPLE=$NAME + EXAMPLE_EXE_PATH="$BUILD_PATH/$EXAMPLE.elf" + echo "Test specific unit test $EXAMPLE" + run_test + done + else - IS_TEST=1 run_test fi -fi - -if [[ $IS_TEST -eq 1 ]]; then + echo "Failed tests total: $FAILED_TESTS" exit "$FAILED_TESTS" fi diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index b4da7f9f6af015..49657c9d3798c8 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -150,11 +150,12 @@ chip_test_suite("tests") { test_sources += [ "TestClusterStateCache.cpp" ] } - # On NRF and fake platforms we do not have a realtime clock available, so + # On NRF, Open IoT SDK and fake platforms we do not have a realtime clock available, so # TestEventLogging.cpp would be testing the same thing as # TestEventLoggingNoUTCTime, but it's not set up to deal with the timestamps # being that low. - if (chip_device_platform != "nrfconnect" && chip_device_platform != "fake") { + if (chip_device_platform != "nrfconnect" && + chip_device_platform != "openiotsdk" && chip_device_platform != "fake") { test_sources += [ "TestEventLogging.cpp" ] } diff --git a/src/app/tests/TestFailSafeContext.cpp b/src/app/tests/TestFailSafeContext.cpp index f0631cd7a92386..9e5d11dc320b73 100644 --- a/src/app/tests/TestFailSafeContext.cpp +++ b/src/app/tests/TestFailSafeContext.cpp @@ -50,10 +50,8 @@ constexpr FabricIndex kTestAccessingFabricIndex2 = 2; static void TestPlatformMgr_Init(nlTestSuite * inSuite, void * inContext) { -#if !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) CHIP_ERROR err = PlatformMgr().InitChipStack(); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); -#endif // !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) } static void TestFailSafeContext_ArmFailSafe(nlTestSuite * inSuite, void * inContext) @@ -123,9 +121,7 @@ int TestFailSafeContext_Setup(void * inContext) */ int TestFailSafeContext_Teardown(void * inContext) { -#if !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) PlatformMgr().Shutdown(); -#endif // !defined(CHIP_DEVICE_LAYER_TARGET_OPEN_IOT_SDK) chip::Platform::MemoryShutdown(); return SUCCESS; } diff --git a/src/inet/tests/TestInetAddress.cpp b/src/inet/tests/TestInetAddress.cpp index 4df87b1e752e59..a31bbb878efee9 100644 --- a/src/inet/tests/TestInetAddress.cpp +++ b/src/inet/tests/TestInetAddress.cpp @@ -1009,7 +1009,7 @@ void CheckToIPv6(nlTestSuite * inSuite, void * inContext) SetupIPAddress(test_addr, lCurrent); #if CHIP_SYSTEM_CONFIG_USE_LWIP - ip6_addr_t ip_addr_1, ip_addr_2; + ip6_addr_t ip_addr_1 = { 0 }, ip_addr_2 = { 0 }; memcpy(ip_addr_1.addr, addr, sizeof(addr)); #if LWIP_IPV6_SCOPES ip_addr_1.zone = 0; diff --git a/src/platform/openiotsdk/BUILD.gn b/src/platform/openiotsdk/BUILD.gn index 726b2face0b36e..984b4ff28b157f 100644 --- a/src/platform/openiotsdk/BUILD.gn +++ b/src/platform/openiotsdk/BUILD.gn @@ -68,6 +68,7 @@ static_library("openiotsdk") { "KeyValueStoreManagerImpl.cpp", "KeyValueStoreManagerImpl.h", "Logging.cpp", + "Logging.h", "NetworkCommissioningDriver.h", "NetworkCommissioningEthernetDriver.cpp", "OpenIoTSDKArchUtils.c", diff --git a/src/platform/openiotsdk/KVBlockDeviceStore.cpp b/src/platform/openiotsdk/KVBlockDeviceStore.cpp index f42d76300e7efb..3a235a0d99c61a 100644 --- a/src/platform/openiotsdk/KVBlockDeviceStore.cpp +++ b/src/platform/openiotsdk/KVBlockDeviceStore.cpp @@ -300,12 +300,12 @@ CHIP_ERROR KVBlockDeviceStore::WriteConfigValue(Key key, uint64_t val) CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str) { - return WriteConfigValueBin(key, reinterpret_cast(str), strlen(str)); + return WriteConfigValueBin(key, reinterpret_cast(str), (str != nullptr) ? strlen(str) : 0); } CHIP_ERROR KVBlockDeviceStore::WriteConfigValueStr(Key key, const char * str, size_t strLen) { - return WriteConfigValueBin(key, reinterpret_cast(str), strLen); + return WriteConfigValueBin(key, reinterpret_cast(str), (strLen > 0) ? strLen : 1); } CHIP_ERROR KVBlockDeviceStore::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen) diff --git a/src/platform/openiotsdk/KVPsaPsStore.cpp b/src/platform/openiotsdk/KVPsaPsStore.cpp index 0e46cec64d5919..523cb37b536c2a 100644 --- a/src/platform/openiotsdk/KVPsaPsStore.cpp +++ b/src/platform/openiotsdk/KVPsaPsStore.cpp @@ -303,7 +303,7 @@ CHIP_ERROR KVPsaPsStore::WriteConfigValue(Key key, uint64_t val) CHIP_ERROR KVPsaPsStore::WriteConfigValueStr(Key key, const char * str) { - return WriteConfigValueBin(key, reinterpret_cast(str), (str != NULL) ? strlen(str) : 0); + return WriteConfigValueBin(key, reinterpret_cast(str), (str != nullptr) ? strlen(str) : 0); } CHIP_ERROR KVPsaPsStore::WriteConfigValueStr(Key key, const char * str, size_t strLen) diff --git a/src/platform/openiotsdk/Logging.cpp b/src/platform/openiotsdk/Logging.cpp index 2a3ecfa7e64e7b..bc51e4e44d6570 100644 --- a/src/platform/openiotsdk/Logging.cpp +++ b/src/platform/openiotsdk/Logging.cpp @@ -47,19 +47,12 @@ void __attribute__((weak)) OnLogOutput() {} namespace Logging { namespace Platform { -/** - * Logging static buffer - */ -namespace { -char logMsgBuffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; -} - /** * CHIP log output functions. */ void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v) { - vsnprintf(logMsgBuffer, sizeof(logMsgBuffer), msg, v); + char logMsgBuffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE]; const char * category_prefix; switch (category) @@ -79,12 +72,22 @@ void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char break; } - printf("%s [%s] %s\r\n", category_prefix, module, logMsgBuffer); + int header_len = snprintf(logMsgBuffer, sizeof(logMsgBuffer), "%s [%s] ", category_prefix, module); + int content_len = vsnprintf(logMsgBuffer + header_len, sizeof(logMsgBuffer) - header_len, msg, v); + int trailer_len = snprintf(logMsgBuffer + header_len + content_len, sizeof(logMsgBuffer) - header_len - content_len, "\r\n"); + fwrite(logMsgBuffer, header_len + content_len + trailer_len, 1, stdout); // Let the application know that a log message has been emitted. DeviceLayer::OnLogOutput(); } +void ois_logging_init() +{ +#if defined(NDEBUG) && CHIP_CONFIG_TEST == 0 + SetLogFilter(LogCategory::kLogCategory_Progress); +#endif +} + } // namespace Platform } // namespace Logging } // namespace chip diff --git a/src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h b/src/platform/openiotsdk/Logging.h similarity index 67% rename from src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h rename to src/platform/openiotsdk/Logging.h index 59ed1720c7e36c..cd34e5206cd58a 100644 --- a/src/test_driver/openiotsdk/unit-tests/lwip-config/user_lwipopts.h +++ b/src/platform/openiotsdk/Logging.h @@ -16,10 +16,22 @@ * limitations under the License. */ -#ifndef USER_LWIPOPTS_H -#define USER_LWIPOPTS_H +/** + * @file + * Provides an implementation of logging support + * for Open IOT SDK platform. + */ + +#pragma once + +#include + +namespace chip { +namespace Logging { +namespace Platform { -#define LWIP_STATS (0) -#define PBUF_POOL_SIZE (1001) +void ois_logging_init(void); -#endif /* USER_LWIPOPTS_H */ +} // namespace Platform +} // namespace Logging +} // namespace chip diff --git a/src/platform/openiotsdk/PlatformManagerImpl.cpp b/src/platform/openiotsdk/PlatformManagerImpl.cpp index 47b8a5293dafdf..579eb5e7f10429 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.cpp +++ b/src/platform/openiotsdk/PlatformManagerImpl.cpp @@ -40,6 +40,18 @@ namespace DeviceLayer { CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { + if (mInitialized) + { + return CHIP_NO_ERROR; + } + + // Call up to the base class _InitChipStack() to perform the bulk of the initialization. + CHIP_ERROR err = GenericPlatformManagerImpl::_InitChipStack(); + if (err != CHIP_NO_ERROR) + { + return err; + } + // Members are initialized by the stack osMutexAttr_t mut_att = { .attr_bits = osMutexRecursive }; @@ -62,15 +74,12 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) return CHIP_ERROR_INTERNAL; } + mInitialized = true; + SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance()); SetDiagnosticDataProvider(&DiagnosticDataProviderImpl::GetDefaultInstance()); - // Call up to the base class _InitChipStack() to perform the bulk of the initialization. - CHIP_ERROR err = GenericPlatformManagerImpl::_InitChipStack(); - SuccessOrExit(err); - -exit: - return err; + return CHIP_NO_ERROR; } void PlatformManagerImpl::_LockChipStack() @@ -97,6 +106,9 @@ void PlatformManagerImpl::_UnlockChipStack() CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * eventPtr) { + // The post event requires event queue from stack initialization + ReturnLogErrorOnFailure(_InitChipStack()); + osStatus_t status = osMessageQueuePut(mQueue, eventPtr, 0, 0); CHIP_ERROR ret = (status == osOK) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; osEventFlagsSet(mPlatformFlags, kPostEventFlag); @@ -107,12 +119,18 @@ void PlatformManagerImpl::HandlePostEvent() { /* handle an event */ ChipDeviceEvent event; - osStatus_t status = osMessageQueueGet(mQueue, &event, nullptr, 0); - if (status == osOK) + uint32_t count = osMessageQueueGetCount(mQueue); + while (count) { + if (osMessageQueueGet(mQueue, &event, nullptr, 0) != osOK) + { + break; + } + LockChipStack(); DispatchEvent(&event); UnlockChipStack(); + count--; } } @@ -154,12 +172,8 @@ void PlatformManagerImpl::RunEventLoopInternal() if (flags & kPostEventFlag) { + osEventFlagsClear(mPlatformFlags, kPostEventFlag); HandlePostEvent(); - - if (!osMessageQueueGetCount(mQueue)) - { - osEventFlagsClear(mPlatformFlags, kPostEventFlag); - } } if ((flags & kTaskRunningEventFlag) == 0) @@ -171,6 +185,12 @@ void PlatformManagerImpl::RunEventLoopInternal() void PlatformManagerImpl::_RunEventLoop() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_RunEventLoop: stack not initialized"); + return; + } + osEventFlagsSet(mPlatformFlags, kTaskRunningEventFlag); RunEventLoopInternal(); @@ -185,6 +205,12 @@ void PlatformManagerImpl::EventLoopTask(void * arg) CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_StartEventLoopTask: stack not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + // this mutex only needed to guard against multiple launches { osMutexAcquire(mEventTaskMutex, osWaitForever); @@ -221,6 +247,12 @@ CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_StopEventLoopTask: stack not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + // this mutex only needed to guard against multiple calls to stop { osMutexAcquire(mEventTaskMutex, osWaitForever); @@ -258,6 +290,9 @@ void PlatformManagerImpl::TimerCallback(void * arg) CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) { + // The timer requires event queue from stack initialization + ReturnLogErrorOnFailure(_InitChipStack()); + if (duration.count() == 0) { TimerCallback(0); @@ -276,6 +311,12 @@ CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) void PlatformManagerImpl::_Shutdown() { + if (!mInitialized) + { + ChipLogError(DeviceLayer, "_Shutdown: stack not initialized"); + return; + } + // // Call up to the base class _Shutdown() to perform the actual stack de-initialization // and clean-up @@ -304,6 +345,7 @@ void PlatformManagerImpl::_Shutdown() mEventTaskMutex = nullptr; mQueue = nullptr; mTimer = nullptr; + mInitialized = false; GenericPlatformManagerImpl::_Shutdown(); } diff --git a/src/platform/openiotsdk/PlatformManagerImpl.h b/src/platform/openiotsdk/PlatformManagerImpl.h index 70664944002143..2a5e86e8341e5d 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.h +++ b/src/platform/openiotsdk/PlatformManagerImpl.h @@ -100,6 +100,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener osMutexId_t mEventTaskMutex = nullptr; osMessageQueueId_t mQueue = nullptr; osTimerId_t mTimer = nullptr; + bool mInitialized = false; }; /** diff --git a/src/platform/tests/TestPlatformMgr.cpp b/src/platform/tests/TestPlatformMgr.cpp index 239817d9417465..09acb8a9d046f8 100644 --- a/src/platform/tests/TestPlatformMgr.cpp +++ b/src/platform/tests/TestPlatformMgr.cpp @@ -100,6 +100,12 @@ static void TestPlatformMgr_BasicEventLoopTask(nlTestSuite * inSuite, void * inC err = PlatformMgr().StopEventLoopTask(); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // Sleep for a short time to allow the event loop to stop. + // Note, in some platform implementations the event loop thread + // is self-terminating. We need time to process the stopping event + // inside event loop. + chip::test_utils::SleepMillis(10); } NL_TEST_ASSERT(inSuite, counterRun == (3 * 2)); diff --git a/src/system/tests/BUILD.gn b/src/system/tests/BUILD.gn index 3c90b372df69f3..1fb0a8f708b7a9 100644 --- a/src/system/tests/BUILD.gn +++ b/src/system/tests/BUILD.gn @@ -35,10 +35,11 @@ chip_test_suite("tests") { test_sources += [ "TestSystemScheduleWork.cpp" ] } - # SystemPacketBuffer on nrfconnect uses LwIP buffers, which ignore the + # SystemPacketBuffer on nrfconnect and openiotsdk uses LwIP buffers, which ignore the # requested allocation size and always allocate at max-size. So our test, # which tries to size-limit the buffers, does not work correctly there. - if (chip_device_platform != "nrfconnect") { + if (chip_device_platform != "nrfconnect" && + chip_device_platform != "openiotsdk") { test_sources += [ "TestTLVPacketBufferBackingStore.cpp" ] } diff --git a/src/test_driver/openiotsdk/integration-tests/.gitignore b/src/test_driver/openiotsdk/integration-tests/.gitignore index 9e76edca71065a..4b72c4651b729f 100644 --- a/src/test_driver/openiotsdk/integration-tests/.gitignore +++ b/src/test_driver/openiotsdk/integration-tests/.gitignore @@ -1 +1 @@ -/**/test_report.json +/**/test_report*.json diff --git a/src/test_driver/openiotsdk/integration-tests/common/device.py b/src/test_driver/openiotsdk/integration-tests/common/device.py index 51e939e135f3b3..c88564ee699676 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/device.py +++ b/src/test_driver/openiotsdk/integration-tests/common/device.py @@ -30,6 +30,7 @@ def __init__(self, name: Optional[str] = None): Base Device runner class containing device handling functions and logging :param name: Logging name for the client """ + self.verbose = True self.iq = queue.Queue() self.oq = queue.Queue() if name is None: @@ -72,7 +73,7 @@ def flush(self, timeout: float = 0) -> [str]: except queue.Empty: return lines - def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool = True) -> [str]: + def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool = True, verbose: bool = False) -> [str]: """ Wait for expected output response :param search: Expected response string @@ -108,7 +109,7 @@ def wait_for_output(self, search: str, timeout: float = 10, assert_timeout: bool else: log.warning(timeout_error_msg) return [] - if now - last > 1: + if verbose and (now - last > 1): log.info('{}: Waiting for "{}" string... Timeout in {:.0f} s'.format(self.name, search, abs(now - start - timeout))) @@ -117,3 +118,6 @@ def _write(self, data): def _read_line(self, timeout): return self.iq.get(timeout=timeout) + + def set_verbose(self, state: bool): + self.verbose = state diff --git a/src/test_driver/openiotsdk/integration-tests/common/fixtures.py b/src/test_driver/openiotsdk/integration-tests/common/fixtures.py index 04d1927ae77f2b..1bbb75d9e9eab6 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/fixtures.py +++ b/src/test_driver/openiotsdk/integration-tests/common/fixtures.py @@ -75,34 +75,20 @@ def device(fvp, fvpConfig, binaryPath, telnetPort, networkInterface): @pytest.fixture(scope="session") -def vendor_id(): - return 0xFFF1 - - -@pytest.fixture(scope="session") -def fabric_id(): - return 1 - - -@pytest.fixture(scope="session") -def node_id(): - return 1 - - -@pytest.fixture(scope="function") -def controller(vendor_id, fabric_id, node_id): +def controller(controllerConfig): try: chip.native.Init() chipStack = chip.ChipStack.ChipStack( - persistentStoragePath='/tmp/openiotsdk-test-storage.json', enableServerInteractions=False) + persistentStoragePath=controllerConfig['persistentStoragePath'], enableServerInteractions=False) certificateAuthorityManager = chip.CertificateAuthority.CertificateAuthorityManager( chipStack, chipStack.GetStorageManager()) certificateAuthorityManager.LoadAuthoritiesFromStorage() if (len(certificateAuthorityManager.activeCaList) == 0): ca = certificateAuthorityManager.NewCertificateAuthority() - ca.NewFabricAdmin(vendorId=vendor_id, fabricId=fabric_id) + ca.NewFabricAdmin(vendorId=controllerConfig['vendorId'], fabricId=controllerConfig['fabricId']) elif (len(certificateAuthorityManager.activeCaList[0].adminList) == 0): - certificateAuthorityManager.activeCaList[0].NewFabricAdmin(vendorId=vendor_id, fabricId=fabric_id) + certificateAuthorityManager.activeCaList[0].NewFabricAdmin( + vendorId=controllerConfig['vendorId'], fabricId=controllerConfig['fabricId']) caList = certificateAuthorityManager.activeCaList @@ -116,3 +102,8 @@ def controller(vendor_id, fabric_id, node_id): return None yield devCtrl + + devCtrl.Shutdown() + certificateAuthorityManager.Shutdown() + chipStack.Shutdown() + os.remove(controllerConfig['persistentStoragePath']) diff --git a/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py b/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py index 9fd1233be2e818..d84d9d00dc1931 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py +++ b/src/test_driver/openiotsdk/integration-tests/common/fvp_device.py @@ -16,9 +16,10 @@ # import logging +import re import subprocess import threading -from time import sleep +import time from .device import Device @@ -60,9 +61,30 @@ def start(self): """ log.info('Starting "{}" runner...'.format(self.name)) - self.proc = subprocess.Popen(self.fvp_cmd) - sleep(3) + self.proc = subprocess.Popen(self.fvp_cmd, stdout=subprocess.PIPE) + timeout = time.time() + 10 # 10s timeout + # Check if FVP process run properly and wait for the connection port log + while True: + if time.time() >= timeout: + raise Exception("FVP start failed") + else: + # Readline from process output + output = self.proc.stdout.readline() + + # Check if process still running + if output == '' and self.proc.poll() is not None: + raise Exception("FVP process has stopped") + else: + line = output.decode().strip() + if re.match(".*Listening for serial connection on port .*", line): + connection_port = int(line.split("port", 1)[1]) + break + time.sleep(0.5) + + if self.connection_channel.get_port() != connection_port: + self.connection_channel.set_port(connection_port) self.connection_channel.open() + self.run = True self.it.start() self.ot.start() @@ -86,7 +108,8 @@ def _input_thread(self): while self.run: line = self.connection_channel.readline() if line: - log.info('<--|{}| {}'.format(self.name, line.strip())) + if self.verbose: + log.info('<--|{}| {}'.format(self.name, line.strip())) self.iq.put(line) else: pass @@ -95,7 +118,8 @@ def _output_thread(self): while self.run: line = self.oq.get() if line: - log.info('-->|{}| {}'.format(self.name, line.strip())) + if self.verbose: + log.info('-->|{}| {}'.format(self.name, line.strip())) self.connection_channel.write(line) else: log.debug('Nothing sent') diff --git a/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py b/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py index 66a509e2627e6b..cc1c42c3d5c1d9 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py +++ b/src/test_driver/openiotsdk/integration-tests/common/telnet_connection.py @@ -33,6 +33,7 @@ def __init__(self, host=None, port=0): self.host = host self.port = port self.is_open = False + self.output_line = bytearray() def open(self): """ @@ -85,11 +86,15 @@ def readline(self): if not self.is_open: return None try: - output = self.telnet.read_until(b"\n", 1) - return self.__formatline(output) + self.output_line.extend(self.telnet.read_until(b"\n", 1)) + if b"\n" in self.output_line: + output = self.__formatline(self.output_line) + self.output_line.clear() + return output except Exception as e: log.error('Telnet read failed {}'.format(e)) return None + return None def write(self, data): """ @@ -114,6 +119,12 @@ def close(self): self.telnet.close() self.is_open = False + def set_port(self, port): + """ + set port number of telnet connection + """ + self.port = port + def get_port(self): """ Get port number of telnet connection diff --git a/src/test_driver/openiotsdk/integration-tests/common/utils.py b/src/test_driver/openiotsdk/integration-tests/common/utils.py index 905876f36057da..52b52839a6b21b 100644 --- a/src/test_driver/openiotsdk/integration-tests/common/utils.py +++ b/src/test_driver/openiotsdk/integration-tests/common/utils.py @@ -19,10 +19,11 @@ import logging import random import re -import shlex from chip import discovery, exceptions +from chip.clusters import Attribute as ClusterAttribute from chip.clusters import Objects as GeneratedObjects +from chip.interaction_model import delegate as IM from chip.setup_payload import SetupPayload log = logging.getLogger(__name__) @@ -36,7 +37,7 @@ def get_setup_payload(device): :param device: serial device instance :return: setup payload or None """ - ret = device.wait_for_output("SetupQRCode") + ret = device.wait_for_output("SetupQRCode", timeout=30) if ret is None or len(ret) < 2: return None @@ -74,9 +75,10 @@ def discover_device(devCtrl, setupPayload): return res[0] -def connect_device(setupPayload, commissionableDevice, nodeId=None): +def connect_device(devCtrl, setupPayload, commissionableDevice, nodeId=None): """ Connect to Matter discovered device on network + :param devCtrl: device controller instance :param setupPayload: device setup payload :param commissionableDevice: CommissionableNode object with discovered device :param nodeId: device node ID @@ -87,10 +89,14 @@ def connect_device(setupPayload, commissionableDevice, nodeId=None): pincode = int(setupPayload.attributes['SetUpPINCode']) try: - commissionableDevice.Commission(nodeId, pincode) + res = devCtrl.CommissionOnNetwork( + nodeId, pincode, filterType=discovery.FilterType.INSTANCE_NAME, filter=commissionableDevice.instanceName) except exceptions.ChipStackError as ex: log.error("Commission discovered device failed {}".format(str(ex))) return None + if not res: + log.info("Commission discovered device failed") + return None return nodeId @@ -109,89 +115,28 @@ def disconnect_device(devCtrl, nodeId): return True -class ParsingError(exceptions.ChipStackException): - def __init__(self, msg=None): - self.msg = "Parsing Error: " + msg - - def __str__(self): - return self.msg - - -def ParseEncodedString(value): - if value.find(":") < 0: - raise ParsingError( - "Value should be encoded in encoding:encodedvalue format") - enc, encValue = value.split(":", 1) - if enc == "str": - return encValue.encode("utf-8") + b'\x00' - elif enc == "hex": - return bytes.fromhex(encValue) - raise ParsingError("Only str and hex encoding is supported") - - -def ParseValueWithType(value, type): - if type == 'int': - return int(value) - elif type == 'str': - return value - elif type == 'bytes': - return ParseEncodedString(value) - elif type == 'bool': - return (value.upper() not in ['F', 'FALSE', '0']) - else: - raise ParsingError('Cannot recognize type: {}'.format(type)) - - -def ParseValueWithStruct(value, cluster): - return eval(f"GeneratedObjects.{cluster}.Structs.{value}") - - -def ParseValue(value, valueType, cluster): - if valueType: - return ParseValueWithType(value, valueType) - elif value.find(":") > 0 and value.split(":", 1)[0] == "struct": - return ParseValueWithStruct(value.split(":", 1)[1], cluster) - else: - raise ParsingError('Cannot parse value: {}'.format(value)) - - -def FormatZCLArguments(cluster, args, cmdArgsWithType): - cmdArgsDict = {} - for kvPair in args: - if kvPair.find("=") < 0: - raise ParsingError("Argument should in key=value format") - key, value = kvPair.split("=", 1) - valueType = cmdArgsWithType.get(key, None) - cmdArgsDict[key] = ParseValue(value, valueType, cluster) - return cmdArgsDict - - -def send_zcl_command(devCtrl, line, requestTimeoutMs: int = None): +def send_zcl_command(devCtrl, cluster: str, command: str, nodeId: int, endpoint: int, args, requestTimeoutMs: int = None): """ - Format and send ZCL message to device. + Send ZCL command to device. :param devCtrl: device controller instance - :param line: command line + :param cluster: cluster name + :param command: command name + :param nodeId: device node ID + :param endpoint: device endpoint + :parma args: command argument in dictionary format :param requestTimeoutMs: command request timeout in ms :return: error code and command response """ res = None err = 0 try: - args = shlex.split(line) - if len(args) < 4: - raise exceptions.InvalidArgumentCount(4, len(args)) - - cluster, command, nodeId, endpoint = args[0:4] - cmdArgsLine = args[4:] allCommands = devCtrl.ZCLCommandList() if cluster not in allCommands: raise exceptions.UnknownCluster(cluster) - cmdArgsWithType = allCommands.get(cluster).get(command, None) - # When command takes no arguments, (not command) is True - if command is None: + cmd = allCommands.get(cluster).get(command) + if cmd is None: raise exceptions.UnknownCommand(cluster, command) - args = FormatZCLArguments(cluster, cmdArgsLine, cmdArgsWithType) clusterObj = getattr(GeneratedObjects, cluster) commandObj = getattr(clusterObj.Commands, command) req = commandObj(**args) @@ -208,32 +153,76 @@ def send_zcl_command(devCtrl, line, requestTimeoutMs: int = None): return (err, res) -def read_zcl_attribute(devCtrl, line): +def write_zcl_attribute(devCtrl, cluster: str, attribute: str, nodeId: int, endpoint: int, value): """ - Read ZCL attribute from device: - + Write ZCL attribute to device. :param devCtrl: device controller instance - :param line: command line + :param cluster: cluster name + :param attribute: attribute name + :param nodeId: device node ID + :param endpoint: device endpoint + :parma value: attribute value to write :return: error code and attribute response """ res = None err = 0 try: - args = shlex.split(line) - if len(args) < 4: - raise exceptions.InvalidArgumentCount(4, len(args)) + allAttrs = devCtrl.ZCLAttributeList() + if cluster not in allAttrs: + raise exceptions.UnknownCluster(cluster) + + attrDetails = allAttrs.get(cluster).get(attribute) + if attrDetails is None: + raise exceptions.UnknownAttribute(cluster, attribute) - cluster, attribute, nodeId, endpoint = args[0:4] + clusterObj = getattr(GeneratedObjects, cluster) + attributeObj = getattr(clusterObj.Attributes, attribute) + req = attributeObj(value) + + res = asyncio.run(devCtrl.WriteAttribute(nodeId, [(endpoint, req)])) + + except exceptions.ChipStackException as ex: + log.error("An exception occurred during processing ZCL attribute: {}".format(str(ex))) + err = -1 + except Exception as ex: + log.error("An exception occurred during processing input: {}".format(str(ex))) + err = -1 + + return (err, res) + + +def read_zcl_attribute(devCtrl, cluster: str, attribute: str, nodeId: int, endpoint: int): + """ + Read ZCL attribute from device. + :param devCtrl: device controller instance + :param cluster: cluster name + :param attribute: attribute name + :param nodeId: device node ID + :param endpoint: device endpoint + :return: error code and attribute response + """ + res = None + err = 0 + try: allAttrs = devCtrl.ZCLAttributeList() if cluster not in allAttrs: raise exceptions.UnknownCluster(cluster) - attrDetails = allAttrs.get(cluster).get(attribute, None) + attrDetails = allAttrs.get(cluster).get(attribute) if attrDetails is None: raise exceptions.UnknownAttribute(cluster, attribute) - res = devCtrl.ZCLReadAttribute(cluster, attribute, int( - nodeId), int(endpoint), 0) + clusterObj = getattr(GeneratedObjects, cluster) + attributeObj = getattr(clusterObj.Attributes, attribute) + + result = asyncio.run(devCtrl.ReadAttribute(nodeId, [(endpoint, attributeObj)])) + + path = ClusterAttribute.AttributePath( + EndpointId=endpoint, Attribute=attributeObj) + + res = IM.AttributeReadResult(path=IM.AttributePath(nodeId=nodeId, endpointId=path.EndpointId, clusterId=path.ClusterId, + attributeId=path.AttributeId), status=0, value=result[endpoint][clusterObj][attributeObj]) + except exceptions.ChipStackException as ex: log.error("An exception occurred during processing ZCL attribute: {}".format(str(ex))) err = -1 diff --git a/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py b/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py index 72ff8fdd78d8c1..8969dcb6ef2165 100644 --- a/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py +++ b/src/test_driver/openiotsdk/integration-tests/lock-app/test_app.py @@ -20,6 +20,7 @@ import pytest from chip.clusters.Objects import DoorLock +from chip.clusters.Types import NullValue from common.utils import connect_device, disconnect_device, discover_device, get_setup_payload, read_zcl_attribute, send_zcl_command log = logging.getLogger(__name__) @@ -33,11 +34,21 @@ def binaryPath(request, rootDir): return os.path.join(rootDir, 'examples/lock-app/openiotsdk/build/chip-openiotsdk-lock-app-example.elf') +@pytest.fixture(scope="session") +def controllerConfig(request): + config = { + 'vendorId': 0xFFF1, + 'fabricId': 1, + 'persistentStoragePath': '/tmp/openiotsdk-test-storage.json' + } + return config + + @pytest.mark.smoketest def test_smoke_test(device): ret = device.wait_for_output("Open IoT SDK lock-app example application start") assert ret is not None and len(ret) > 0 - ret = device.wait_for_output("Open IoT SDK lock-app example application run") + ret = device.wait_for_output("Open IoT SDK lock-app example application run", timeout=30) assert ret is not None and len(ret) > 0 @@ -46,6 +57,9 @@ def test_commissioning(device, controller): assert controller is not None devCtrl = controller + ret = device.wait_for_output("Open IoT SDK lock-app example application start") + assert ret is not None and len(ret) > 0 + setupPayload = get_setup_payload(device) assert setupPayload is not None @@ -56,7 +70,7 @@ def test_commissioning(device, controller): assert commissionable_device.productId == int(setupPayload.attributes['ProductID']) assert commissionable_device.addresses[0] is not None - nodeId = connect_device(setupPayload, commissionable_device) + nodeId = connect_device(devCtrl, setupPayload, commissionable_device) assert nodeId is not None log.info("Device {} connected".format(commissionable_device.addresses[0])) @@ -66,7 +80,7 @@ def test_commissioning(device, controller): assert disconnect_device(devCtrl, nodeId) -LOCK_CTRL_TEST_PIN_CODE = 12345 +LOCK_CTRL_TEST_PIN_CODE = b"123456" LOCK_CTRL_TEST_USER_INDEX = 1 LOCK_CTRL_TEST_ENDPOINT_ID = 1 LOCK_CTRL_TEST_USER_NAME = 'testUser' @@ -78,28 +92,30 @@ def test_lock_ctrl(device, controller): assert controller is not None devCtrl = controller + ret = device.wait_for_output("Open IoT SDK lock-app example application start") + assert ret is not None and len(ret) > 0 + setupPayload = get_setup_payload(device) assert setupPayload is not None commissionable_device = discover_device(devCtrl, setupPayload) assert commissionable_device is not None - nodeId = connect_device(setupPayload, commissionable_device) + nodeId = connect_device(devCtrl, setupPayload, commissionable_device) assert nodeId is not None ret = device.wait_for_output("Commissioning completed successfully", timeout=30) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock SetUser {} {} operationType={} userIndex={} userName={} userUniqueId={} " - "userStatus={} userType={} credentialRule={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlDataOperationType.kAdd, - LOCK_CTRL_TEST_USER_INDEX, - LOCK_CTRL_TEST_USER_NAME, - LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlUserStatus.kOccupiedEnabled, - DoorLock.Enums.DlUserType.kUnrestrictedUser, - DoorLock.Enums.DlCredentialRule.kSingle), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "SetUser", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(operationType=DoorLock.Enums.DataOperationTypeEnum.kAdd, + userIndex=LOCK_CTRL_TEST_USER_INDEX, + userName=LOCK_CTRL_TEST_USER_NAME, + userUniqueID=LOCK_CTRL_TEST_USER_INDEX, + userStatus=DoorLock.Enums.UserStatusEnum.kOccupiedEnabled, + userType=DoorLock.Enums.UserTypeEnum.kUnrestrictedUser, + credentialRule=DoorLock.Enums.CredentialRuleEnum.kSingle), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("Successfully set the user [mEndpointId={},index={},adjustedIndex=0]".format( @@ -107,28 +123,26 @@ def test_lock_ctrl(device, controller): LOCK_CTRL_TEST_USER_INDEX)) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock GetUser {} {} userIndex={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_USER_INDEX)) + err, res = send_zcl_command(devCtrl, "DoorLock", "GetUser", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(userIndex=LOCK_CTRL_TEST_USER_INDEX), + requestTimeoutMs=1000) assert err == 0 assert res.userIndex == LOCK_CTRL_TEST_USER_INDEX assert res.userName == LOCK_CTRL_TEST_USER_NAME assert res.userUniqueID == LOCK_CTRL_TEST_USER_INDEX - assert res.userStatus == DoorLock.Enums.DlUserStatus.kOccupiedEnabled - assert res.userType == DoorLock.Enums.DlUserType.kUnrestrictedUser - assert res.credentialRule == DoorLock.Enums.DlCredentialRule.kSingle - - err, res = send_zcl_command( - devCtrl, "DoorLock SetCredential {} {} operationType={} " - "credential=struct:DlCredential(credentialType={},credentialIndex={}) credentialData=str:{} " - "userIndex={} userStatus={} userType={}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlDataOperationType.kAdd, - DoorLock.Enums.DlCredentialType.kPin, - LOCK_CTRL_TEST_CREDENTIAL_INDEX, - LOCK_CTRL_TEST_PIN_CODE, - LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlUserStatus.kOccupiedEnabled, - DoorLock.Enums.DlUserType.kUnrestrictedUser), requestTimeoutMs=1000) + assert res.userStatus == DoorLock.Enums.UserStatusEnum.kOccupiedEnabled + assert res.userType == DoorLock.Enums.UserTypeEnum.kUnrestrictedUser + assert res.credentialRule == DoorLock.Enums.CredentialRuleEnum.kSingle + + err, res = send_zcl_command(devCtrl, "DoorLock", "SetCredential", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(operationType=DoorLock.Enums.DataOperationTypeEnum.kAdd, + credential=DoorLock.Structs.CredentialStruct( + credentialType=DoorLock.Enums.CredentialTypeEnum.kPin, credentialIndex=LOCK_CTRL_TEST_CREDENTIAL_INDEX), + credentialData=LOCK_CTRL_TEST_PIN_CODE, + userIndex=LOCK_CTRL_TEST_USER_INDEX, + userStatus=NullValue, + userType=NullValue), + requestTimeoutMs=1000) assert err == 0 assert res.status == DoorLock.Enums.DlStatus.kSuccess @@ -136,42 +150,39 @@ def test_lock_ctrl(device, controller): "credentialType={},creator=1,modifier=1]".format( LOCK_CTRL_TEST_ENDPOINT_ID, LOCK_CTRL_TEST_USER_INDEX, - DoorLock.Enums.DlCredentialType.kPin + DoorLock.Enums.CredentialTypeEnum.kPin )) assert ret is not None and len(ret) > 0 - err, res = send_zcl_command( - devCtrl, "DoorLock GetCredentialStatus {} {} credential=struct:DlCredential(credentialType={}," - "credentialIndex={})".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - DoorLock.Enums.DlCredentialType.kPin, - LOCK_CTRL_TEST_CREDENTIAL_INDEX), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "GetCredentialStatus", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(credential=DoorLock.Structs.CredentialStruct( + credentialType=DoorLock.Enums.CredentialTypeEnum.kPin, credentialIndex=LOCK_CTRL_TEST_CREDENTIAL_INDEX)), + requestTimeoutMs=1000) assert err == 0 assert res.credentialExists assert res.userIndex == LOCK_CTRL_TEST_USER_INDEX - err, res = send_zcl_command( - devCtrl, "DoorLock LockDoor {} {} pinCode=str:{}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_PIN_CODE), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "LockDoor", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(PINCode=LOCK_CTRL_TEST_PIN_CODE), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("setting door lock state to \"Locked\"") assert ret is not None and len(ret) > 0 - err, res = read_zcl_attribute( - devCtrl, "DoorLock LockState {} {}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID)) + err, res = read_zcl_attribute(devCtrl, "DoorLock", "LockState", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID) assert err == 0 assert res.value == DoorLock.Enums.DlLockState.kLocked - err, res = send_zcl_command( - devCtrl, "DoorLock UnlockDoor {} {} pinCode=str:{}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, - LOCK_CTRL_TEST_PIN_CODE), requestTimeoutMs=1000) + err, res = send_zcl_command(devCtrl, "DoorLock", "UnlockDoor", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID, + dict(PINCode=LOCK_CTRL_TEST_PIN_CODE), + requestTimeoutMs=1000) assert err == 0 ret = device.wait_for_output("setting door lock state to \"Unlocked\"") assert ret is not None and len(ret) > 0 - err, res = read_zcl_attribute( - devCtrl, "DoorLock LockState {} {}".format(nodeId, LOCK_CTRL_TEST_ENDPOINT_ID)) + err, res = read_zcl_attribute(devCtrl, "DoorLock", "LockState", nodeId, LOCK_CTRL_TEST_ENDPOINT_ID) assert err == 0 assert res.value == DoorLock.Enums.DlLockState.kUnlocked diff --git a/src/test_driver/openiotsdk/integration-tests/shell/test_app.py b/src/test_driver/openiotsdk/integration-tests/shell/test_app.py index a10989b7f9379d..529436c7c79671 100644 --- a/src/test_driver/openiotsdk/integration-tests/shell/test_app.py +++ b/src/test_driver/openiotsdk/integration-tests/shell/test_app.py @@ -1,17 +1,19 @@ -# Copyright (c) 2009-2021 Arm Limited -# SPDX-License-Identifier: Apache-2.0 # -# 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 +# Copyright (c) 2022 Project CHIP Authors +# All rights reserved. # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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. # -# 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 logging import os @@ -91,6 +93,10 @@ def test_command_check(device): ret = device.wait_for_output("Open IoT SDK shell example application run") assert ret is not None and len(ret) > 0 + # Wait for printing prompt + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 + # Help ret = device.send(command="help", expected_output="Done") assert ret is not None and len(ret) > 1 @@ -98,32 +104,44 @@ def test_command_check(device): assert set(SHELL_COMMAND_NAME) == set(shell_commands) # Echo + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="echo Hello", expected_output="Done") assert ret is not None and len(ret) > 1 assert "Hello" in ret[-2] # Log + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="log Hello", expected_output="Done") assert ret is not None and len(ret) > 1 assert "[INF] [TOO] Hello" in ret[-2] # Rand + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="rand", expected_output="Done") assert ret is not None and len(ret) > 1 assert ret[-2].rstrip().isdigit() # Base64 + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 hex_string = "1234" ret = device.send(command="base64 encode {}".format( hex_string), expected_output="Done") assert ret is not None and len(ret) > 1 base64code = ret[-2] + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="base64 decode {}".format( base64code), expected_output="Done") assert ret is not None and len(ret) > 1 assert ret[-2].rstrip() == hex_string # Version + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="version", expected_output="Done") assert ret is not None and len(ret) > 1 assert "CHIP" in ret[-2].split()[0] @@ -131,12 +149,16 @@ def test_command_check(device): assert isinstance(version.parse(app_version), version.Version) # Config + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config", expected_output="Done") assert ret is not None and len(ret) > 2 config = parse_config_response(ret[1:-1]) for param_name, value in config.items(): + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config {}".format( param_name), expected_output="Done") assert ret is not None and len(ret) > 1 @@ -145,23 +167,31 @@ def test_command_check(device): else: assert int(ret[-2].split()[0]) == value + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 new_value = int(config['discriminator']) + 1 ret = device.send(command="config discriminator {}".format( new_value), expected_output="Done") assert ret is not None and len(ret) > 1 assert "Setup discriminator set to: {}".format(new_value) in ret[-2] + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="config discriminator", expected_output="Done") assert ret is not None and len(ret) > 1 assert int(ret[-2].split()[0], 16) == new_value # Onboardingcodes + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="onboardingcodes none", expected_output="Done") assert ret is not None and len(ret) > 2 boarding_codes = parse_boarding_codes_response(ret[1:-1]) for param, value in boarding_codes.items(): + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="onboardingcodes none {}".format( param), expected_output="Done") assert ret is not None and len(ret) > 1 @@ -184,5 +214,7 @@ def test_command_check(device): assert device_details is not None and len(device_details) != 0 # Exit - should be the last check + ret = device.wait_for_output("Enter command") + assert ret is not None and len(ret) > 0 ret = device.send(command="exit", expected_output="Goodbye") assert ret is not None and len(ret) > 0 diff --git a/src/test_driver/openiotsdk/integration-tests/unit-tests/__init__.py b/src/test_driver/openiotsdk/integration-tests/unit-tests/__init__.py new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py b/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py new file mode 100644 index 00000000000000..c232eddba54d93 --- /dev/null +++ b/src/test_driver/openiotsdk/integration-tests/unit-tests/test_app.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2022 Project CHIP Authors +# All rights reserved. +# +# 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 logging +import re + +import pytest + +log = logging.getLogger(__name__) + + +@pytest.fixture(scope="session") +def binaryPath(request, rootDir): + if request.config.getoption('binaryPath'): + return request.config.getoption('binaryPath') + else: + assert False + + +def test_unit_tests(device): + ret = device.wait_for_output("Open IoT SDK unit-tests start", timeout=30) + assert ret is not None and len(ret) > 0 + ret = device.wait_for_output("Open IoT SDK unit-tests run", timeout=30) + assert ret is not None and len(ret) > 0 + + ret = device.wait_for_output("Test status:", 1200) + # Get test status + test_status = ret[-1] + result = re.findall(r'\d+', test_status) + assert len(result) == 1 + assert int(result[0]) == 0 diff --git a/src/test_driver/openiotsdk/unit-tests/.gitignore b/src/test_driver/openiotsdk/unit-tests/.gitignore index bcd261e241a6c2..567609b1234a9b 100644 --- a/src/test_driver/openiotsdk/unit-tests/.gitignore +++ b/src/test_driver/openiotsdk/unit-tests/.gitignore @@ -1,2 +1 @@ build/ -test_report.json diff --git a/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt b/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt index e9c0f48953cfde..15bf03373d5b46 100644 --- a/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt +++ b/src/test_driver/openiotsdk/unit-tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Project CHIP Authors +# Copyright (c) 2022-2023 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ get_filename_component(OPEN_IOT_SDK_EXAMPLE_COMMON ${CHIP_ROOT}/examples/platfor list(APPEND CMAKE_MODULE_PATH ${OPEN_IOT_SDK_CONFIG}/cmake) +set(TFM_SUPPORT YES) + # Application CHIP build configuration set(CONFIG_CHIP_LIB_TESTS YES) set(CONFIG_CHIP_DETAIL_LOGGING NO) @@ -35,48 +37,44 @@ include(toolchain) project(chip-open-iot-sdk-unit-tests LANGUAGES C CXX ASM) include(sdk) -include(linker) - -# LwIP configuration -if(TARGET lwip-cmsis-port) - # lwip requires user_lwipopts.h, we use the custom settings - target_include_directories(lwipopts - INTERFACE - lwip-config - ) -endif() - include(chip) +include(linker) add_subdirectory(${OPEN_IOT_SDK_EXAMPLE_COMMON}/app ./app_build) file(STRINGS testnames.txt TEST_NAMES_FROM_FILE) STRING(REGEX REPLACE "\n" ";" TEST_NAMES_FROM_FILE "${TEST_NAMES_FROM_FILE}") +target_compile_definitions(openiotsdk-startup + PRIVATE + IOT_SDK_APP_MAIN_STACK_SIZE=20480 +) + foreach(TEST_NAME IN LISTS TEST_NAMES_FROM_FILE) - add_executable(${TEST_NAME}) - target_include_directories(${TEST_NAME} + set(APP_TARGET ${TEST_NAME}_ns) + add_executable(${APP_TARGET}) + target_include_directories(${APP_TARGET} PRIVATE main/include ${CHIP_ROOT}/third_party/nlunit-test/repo/src ) - target_sources(${TEST_NAME} + target_sources(${APP_TARGET} PRIVATE - main/main.cpp + main/main_ns.cpp ) - target_link_libraries(${TEST_NAME} + target_link_libraries(${APP_TARGET} + openiotsdk-startup openiotsdk-app ) # Link the *whole-archives* to keep the static test objects. - target_link_options(${TEST_NAME} + target_link_options(${APP_TARGET} PUBLIC -Wl,--whole-archive "${CMAKE_CURRENT_BINARY_DIR}/chip_build/lib/lib${TEST_NAME}.a" -Wl,--no-whole-archive) - # set_target_link requires APP_TARGET to be defined - set(APP_TARGET ${TEST_NAME}) - set_target_link(${TEST_NAME}) + set_target_link(${APP_TARGET}) + sdk_post_build(${APP_TARGET}) endforeach() diff --git a/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h b/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h index 6011c1e9d6bf79..0e306efb13d231 100644 --- a/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h +++ b/src/test_driver/openiotsdk/unit-tests/freertos-config/FreeRTOSConfig.h @@ -39,13 +39,13 @@ extern uint32_t SystemCoreClock; // Minimal stack size [words] <0-65535> // Stack for idle task and default task stack in words. -// Default: 128 +// Default: 4kB #define configMINIMAL_STACK_SIZE ((uint16_t)(4 * 1024)) // Total heap size [bytes] <0-0xFFFFFFFF> // Heap memory size in bytes. -// Default: 8192 -#define configTOTAL_HEAP_SIZE ((size_t) 8192) +// Default: 8kB +#define configTOTAL_HEAP_SIZE ((size_t)(8 * 1024)) // Kernel tick frequency [Hz] <0-0xFFFFFFFF> // Kernel tick rate in Hz. @@ -190,7 +190,7 @@ extern uint32_t SystemCoreClock; // Use TrustZone Secure Side Only // This settings prevents FreeRTOS contex switch to Non-Secure side. // Enable this setting when FreeRTOS runs on the Secure side only. -#define configRUN_FREERTOS_SECURE_ONLY 1 +#define configRUN_FREERTOS_SECURE_ONLY CONFIG_RUN_FREERTOS_SECURE_ONLY // Use TrustZone Security Extension // Using TrustZone affects context handling. diff --git a/src/test_driver/openiotsdk/unit-tests/main/main.cpp b/src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp similarity index 73% rename from src/test_driver/openiotsdk/unit-tests/main/main.cpp rename to src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp index 947e2b9f55137a..e9c8bbd208cf48 100644 --- a/src/test_driver/openiotsdk/unit-tests/main/main.cpp +++ b/src/test_driver/openiotsdk/unit-tests/main/main_ns.cpp @@ -42,13 +42,6 @@ static void test_thread(void * argument) goto exit; } - err = DeviceLayer::PlatformMgr().InitChipStack(); - if (err != CHIP_NO_ERROR) - { - ChipLogAutomation("Chip stack initialization failed: %s", err.AsString()); - goto exit; - } - ChipLogAutomation("Open IoT SDK unit-tests run..."); status = RunRegisteredUnitTests(); ChipLogAutomation("Test status: %d", status); @@ -59,8 +52,6 @@ static void test_thread(void * argument) int main() { - ChipLogAutomation("Open IoT SDK unit-tests start"); - if (openiotsdk_platform_init()) { ChipLogAutomation("ERROR: Open IoT SDK platform initialization failed"); @@ -69,22 +60,18 @@ int main() nlTestSetLogger(&NlTestLogger::nl_test_logger); - static const osThreadAttr_t thread_attr = { - .stack_size = 20 * 1024 // Allocate enough stack for app thread - }; + ChipLogAutomation("Open IoT SDK unit-tests start"); - osThreadId_t testThread = osThreadNew(test_thread, NULL, &thread_attr); - if (testThread == NULL) + if (openiotsdk_network_init(true)) { - ChipLogAutomation("ERROR: Failed to create app thread"); + ChipLogAutomation("ERROR: Network initialization failed"); return EXIT_FAILURE; } - if (openiotsdk_platform_run()) - { - ChipLogAutomation("ERROR: Open IoT SDK platform run failed"); - return EXIT_FAILURE; - } + ChipLogAutomation("Open IoT SDK unit-tests run..."); + int status = RunRegisteredUnitTests(); + ChipLogAutomation("Test status: %d", status); + ChipLogAutomation("Open IoT SDK unit-tests completed"); return EXIT_SUCCESS; } diff --git a/third_party/open-iot-sdk/sdk b/third_party/open-iot-sdk/sdk index f2ffb845311828..152061529ebeca 160000 --- a/third_party/open-iot-sdk/sdk +++ b/third_party/open-iot-sdk/sdk @@ -1 +1 @@ -Subproject commit f2ffb845311828af0c5a8ecdbacdc104cb03c703 +Subproject commit 152061529ebeca4488c81f2562a0db9c60b7b325