diff --git a/config/telink/app/enable-gnu-std.cmake b/config/telink/app/enable-gnu-std.cmake index 38bacbea1ef791..e46c87199aad2d 100644 --- a/config/telink/app/enable-gnu-std.cmake +++ b/config/telink/app/enable-gnu-std.cmake @@ -15,5 +15,5 @@ # add_library(gnu17 INTERFACE) -target_compile_options(gnu17 INTERFACE -std=gnu++17 -D_SYS__PTHREADTYPES_H_) +target_compile_options(gnu17 INTERFACE $<$:-std=gnu++17> -D_SYS__PTHREADTYPES_H_) target_link_libraries(app PRIVATE gnu17) \ No newline at end of file diff --git a/config/telink/chip-module/CMakeLists.txt b/config/telink/chip-module/CMakeLists.txt index 6ef6ac2c882238..6a48424b7f4832 100644 --- a/config/telink/chip-module/CMakeLists.txt +++ b/config/telink/chip-module/CMakeLists.txt @@ -91,6 +91,7 @@ macro(chip_gn_arg ARG VALUE) string(APPEND CHIP_GN_ARGS "--arg\n${ARG}\n${VALUE}\n") endmacro() + # ============================================================================== # Prepare CHIP configuration based on the project Kconfig configuration # ============================================================================== @@ -128,10 +129,6 @@ if (CONFIG_CHIP_LIB_SHELL) list(APPEND CHIP_LIBRARIES -lCHIPShell) endif() -if (CONFIG_CHIP_PW_RPC) - list(APPEND CHIP_LIBRARIES -lPwRpc) -endif() - if (CONFIG_TELINK_BLE_LIB) list(APPEND CHIP_LIBRARIES -lB91_ble_lib) endif() @@ -226,6 +223,14 @@ if (CHIP_PROJECT_CONFIG) chip_gn_arg_string("chip_project_config_include" ${CHIP_PROJECT_CONFIG}) chip_gn_arg_string("chip_system_project_config_include" ${CHIP_PROJECT_CONFIG}) endif() + +if (CONFIG_CHIP_PW_RPC) + set(PIGWEED_DIR "//third_party/pigweed/repo") + chip_gn_arg_string("pw_assert_BACKEND" ${PIGWEED_DIR}/pw_assert_log:check_backend) + chip_gn_arg_string("pw_log_BACKEND" ${PIGWEED_DIR}/pw_log_basic) + chip_gn_arg("pw_build_LINK_DEPS" [\"${PIGWEED_DIR}/pw_assert:impl\",\ \"${PIGWEED_DIR}/pw_log:impl\"]) +endif() + if (CONFIG_CHIP_EXAMPLE_DEVICE_INFO_PROVIDER) chip_gn_arg_bool("chip_build_example_providers" "true") list(APPEND CHIP_LIBRARIES -lMatterDeviceInfoProviderExample) diff --git a/examples/common/pigweed/telink/PigweedLoggerMutex.cpp b/examples/common/pigweed/telink/PigweedLoggerMutex.cpp new file mode 100644 index 00000000000000..5061d53e768a6d --- /dev/null +++ b/examples/common/pigweed/telink/PigweedLoggerMutex.cpp @@ -0,0 +1,27 @@ +/* + * + * Copyright (c) 2021 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 "PigweedLoggerMutex.h" + +namespace chip { +namespace rpc { + +PigweedLoggerMutex logger_mutex; + +} // namespace rpc +} // namespace chip diff --git a/examples/common/pigweed/telink/PigweedLoggerMutex.h b/examples/common/pigweed/telink/PigweedLoggerMutex.h new file mode 100644 index 00000000000000..9a9b7e57d5dfea --- /dev/null +++ b/examples/common/pigweed/telink/PigweedLoggerMutex.h @@ -0,0 +1,52 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#pragma once + +#include "PigweedLogger.h" +#include "pigweed/RpcService.h" +#include + +namespace chip { +namespace rpc { +class PigweedLoggerMutex : public ::chip::rpc::Mutex +{ +public: + PigweedLoggerMutex() {} + void Lock() override + { + k_sem * sem = PigweedLogger::GetSemaphore(); + if (sem) + { + k_sem_take(sem, K_FOREVER); + } + } + void Unlock() override + { + k_sem * sem = PigweedLogger::GetSemaphore(); + if (sem) + { + k_sem_give(sem); + } + } +}; + +extern PigweedLoggerMutex logger_mutex; + +} // namespace rpc +} // namespace chip diff --git a/examples/lighting-app/telink/CMakeLists.txt b/examples/lighting-app/telink/CMakeLists.txt index c4af6cb598b8c9..1cf675a23bab7b 100644 --- a/examples/lighting-app/telink/CMakeLists.txt +++ b/examples/lighting-app/telink/CMakeLists.txt @@ -66,3 +66,162 @@ chip_configure_data_model(app if(CONFIG_CHIP_OTA_REQUESTOR) target_sources(app PRIVATE ${TELINK_COMMON}/util/src/OTAUtil.cpp) endif() + +# Fix for unused swap parameter in: zephyr/include/zephyr/arch/riscv/irq.h:70 +add_compile_options(-Wno-error=unused-parameter) + +if (CONFIG_CHIP_PW_RPC) + +# Make all targets created below depend on zephyr_interface to inherit MCU-related compilation flags +link_libraries($) + +set(PIGWEED_ROOT "${CHIP_ROOT}/third_party/pigweed/repo") +include(${PIGWEED_ROOT}/pw_build/pigweed.cmake) +include(${PIGWEED_ROOT}/pw_protobuf_compiler/proto.cmake) + +pw_set_module_config(pw_rpc_CONFIG pw_rpc.disable_global_mutex_config) +pw_set_backend(pw_log pw_log_basic) +pw_set_backend(pw_assert.check pw_assert_log.check_backend) +pw_set_backend(pw_assert.assert pw_assert.assert_compatibility_backend) +pw_set_backend(pw_sys_io pw_sys_io.telink) +pw_set_backend(pw_trace pw_trace_tokenized) +set(dir_pw_third_party_nanopb "${CHIP_ROOT}/third_party/nanopb/repo" CACHE STRING "" FORCE) + +add_subdirectory(third_party/connectedhomeip/third_party/pigweed/repo) +add_subdirectory(third_party/connectedhomeip/third_party/nanopb/repo) +add_subdirectory(third_party/connectedhomeip/examples/platform/telink/pw_sys_io) + +pw_proto_library(attributes_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/attributes_service.proto + INPUTS + ${CHIP_ROOT}/examples/common/pigweed/protos/attributes_service.options + PREFIX + attributes_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(button_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/button_service.proto + PREFIX + button_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(descriptor_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/descriptor_service.proto + PREFIX + descriptor_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(device_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/device_service.proto + INPUTS + ${CHIP_ROOT}/examples/common/pigweed/protos/device_service.options + PREFIX + device_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(lighting_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/lighting_service.proto + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + PREFIX + lighting_service + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(ot_cli_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/ot_cli_service.proto + INPUTS + ${CHIP_ROOT}/examples/common/pigweed/protos/ot_cli_service.options + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + PREFIX + ot_cli_service + DEPS + pw_protobuf.common_proto +) + +pw_proto_library(thread_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/thread_service.proto + INPUTS + ${CHIP_ROOT}/examples/common/pigweed/protos/thread_service.options + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + PREFIX + thread_service + DEPS + pw_protobuf.common_proto +) + +target_sources(app PRIVATE + ../../common/pigweed/RpcService.cpp + ../../common/pigweed/telink/PigweedLoggerMutex.cpp + ${TELINK_COMMON}/Rpc.cpp + ${TELINK_COMMON}/util/src/PigweedLogger.cpp +) + +target_include_directories(app PRIVATE + ${PIGWEED_ROOT}/pw_sys_io/public + ${CHIP_ROOT}/src/lib/support + ${CHIP_ROOT}/src/system + ${TELINK_COMMON} + ../../common + ../../common/pigweed + ../../common/pigweed/telink) + +target_compile_options(app PRIVATE + "-DPW_RPC_ATTRIBUTE_SERVICE=1" + "-DPW_RPC_BUTTON_SERVICE=1" + "-DPW_RPC_DESCRIPTOR_SERVICE=1" + "-DPW_RPC_DEVICE_SERVICE=1" + "-DPW_RPC_LIGHTING_SERVICE=1" + "-DPW_RPC_THREAD_SERVICE=1" + "-DPW_RPC_TRACING_SERVICE=1" + "-DPW_TRACE_BACKEND_SET=1") + +target_link_libraries(app PRIVATE + attributes_service.nanopb_rpc + button_service.nanopb_rpc + descriptor_service.nanopb_rpc + device_service.nanopb_rpc + lighting_service.nanopb_rpc + thread_service.nanopb_rpc + pw_checksum + pw_hdlc + pw_log + pw_rpc.server + pw_trace_tokenized + pw_trace_tokenized.trace_buffer + pw_trace_tokenized.rpc_service + pw_trace_tokenized.protos.nanopb_rpc +) + +target_link_options(app + PUBLIC + "-T${PIGWEED_ROOT}/pw_tokenizer/pw_tokenizer_linker_sections.ld" +) + +endif(CONFIG_CHIP_PW_RPC) diff --git a/examples/lighting-app/telink/Readme.md b/examples/lighting-app/telink/Readme.md index 018fdd386dc898..d6ace813c0df75 100644 --- a/examples/lighting-app/telink/Readme.md +++ b/examples/lighting-app/telink/Readme.md @@ -223,3 +223,15 @@ Usage of OTA: Once the transfer is complete, OTA requestor sends ApplyUpdateRequest command to OTA provider for applying the image. Device will restart on successful application of OTA image. + +### Building with Pigweed RPCs + +The RPCs in `lighting-common/lighting_service/lighting_service.proto` can be +used to control various functionalities of the lighting app from a USB-connected +host computer. To build the example with the RPC server, run the following +command with _build-target_ replaced with the build target name of the Nordic +Semiconductor's kit you own: + + ``` + $ west build -b tlsr9518adk80d -- -DOVERLAY_CONFIG=rpc.overlay + ``` diff --git a/examples/lighting-app/telink/include/AppTask.h b/examples/lighting-app/telink/include/AppTask.h index 0b1b588654aa13..09ba6ef240f26c 100644 --- a/examples/lighting-app/telink/include/AppTask.h +++ b/examples/lighting-app/telink/include/AppTask.h @@ -27,6 +27,10 @@ #include #endif +#ifdef CONFIG_CHIP_PW_RPC +#include "Rpc.h" +#endif + #include struct k_timer; @@ -40,7 +44,18 @@ class AppTask void PostEvent(AppEvent * event); void UpdateClusterState(); + enum ButtonId_t + { + kButtonId_LightingAction = 1, + kButtonId_FactoryReset, + kButtonId_StartThread, + kButtonId_StartBleAdv + } ButtonId; + private: +#ifdef CONFIG_CHIP_PW_RPC + friend class chip::rpc::TelinkButton; +#endif friend AppTask & GetAppTask(void); CHIP_ERROR Init(); @@ -62,6 +77,7 @@ class AppTask static void LightingActionEventHandler(AppEvent * aEvent); static void StartBleAdvHandler(AppEvent * aEvent); + static void ButtonEventHandler(ButtonId_t btnId, bool btnPressed); static void InitButtons(void); static void ThreadProvisioningHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg); diff --git a/examples/lighting-app/telink/rpc.overlay b/examples/lighting-app/telink/rpc.overlay new file mode 100644 index 00000000000000..a97621181efc75 --- /dev/null +++ b/examples/lighting-app/telink/rpc.overlay @@ -0,0 +1,47 @@ +# +# Copyright (c) 2020 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. +# 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. +# + +# This file should be used as a configuration overlay to build Pigweed RPCs to +# lighting-app. + +# Enable Pigweed RPC +CONFIG_CHIP_PW_RPC=y + +# Add support for C++17 to build Pigweed components +CONFIG_STD_CPP14=n +CONFIG_STD_CPP17=y + +# Add support for Zephyr console component to use it for Pigweed console purposes +CONFIG_CONSOLE_SUBSYS=y +CONFIG_CONSOLE_GETCHAR=y +CONFIG_CONSOLE_PUTCHAR_BUFSIZE=256 + +# Disable features which may interfere with Pigweed HDLC transport +CONFIG_SHELL=n +CONFIG_OPENTHREAD_SHELL=n +CONFIG_BOOT_BANNER=n + +# Configure Zephyr logger with defaults backends disabled as the app provides its own, +# based on Pigweed HDLC. +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=n +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_BACKEND_RTT=n +CONFIG_LOG_OUTPUT=y + +# Increase zephyr tty rx buffer +CONFIG_CONSOLE_GETCHAR_BUFSIZE=128 diff --git a/examples/lighting-app/telink/src/AppTask.cpp b/examples/lighting-app/telink/src/AppTask.cpp index 00db77cd01c89a..a310bdb1907560 100644 --- a/examples/lighting-app/telink/src/AppTask.cpp +++ b/examples/lighting-app/telink/src/AppTask.cpp @@ -457,6 +457,30 @@ void AppTask::UpdateClusterState() } } +void AppTask::ButtonEventHandler(ButtonId_t btnId, bool btnPressed) +{ + if (!btnPressed) + { + return; + } + + switch (btnId) + { + case kButtonId_LightingAction: + LightingActionButtonEventHandler(); + break; + case kButtonId_FactoryReset: + FactoryResetButtonEventHandler(); + break; + case kButtonId_StartThread: + StartThreadButtonEventHandler(); + break; + case kButtonId_StartBleAdv: + StartBleAdvButtonEventHandler(); + break; + } +} + void AppTask::InitButtons(void) { sFactoryResetButton.Configure(BUTTON_PORT, BUTTON_PIN_3, BUTTON_PIN_1, FactoryResetButtonEventHandler); diff --git a/examples/lighting-app/telink/src/main.cpp b/examples/lighting-app/telink/src/main.cpp index 4ea2a51acd6b2e..55e75804eec945 100644 --- a/examples/lighting-app/telink/src/main.cpp +++ b/examples/lighting-app/telink/src/main.cpp @@ -23,6 +23,10 @@ #include +#ifdef CONFIG_CHIP_PW_RPC +#include "Rpc.h" +#endif + LOG_MODULE_REGISTER(app); using namespace ::chip; @@ -33,6 +37,10 @@ int main(void) { CHIP_ERROR err = CHIP_NO_ERROR; +#ifdef CONFIG_CHIP_PW_RPC + rpc::Init(); +#endif + err = chip::Platform::MemoryInit(); if (err != CHIP_NO_ERROR) { diff --git a/examples/platform/telink/Rpc.cpp b/examples/platform/telink/Rpc.cpp new file mode 100644 index 00000000000000..93348fc5dbcbcb --- /dev/null +++ b/examples/platform/telink/Rpc.cpp @@ -0,0 +1,219 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE +#include "AppTask.h" +#endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE +#include "PigweedLoggerMutex.h" +#include "pigweed/RpcService.h" +#include "pw_sys_io_telink/init.h" +#include +#include + +#include + +LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); + +#if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#include "pigweed/rpc_services/Attributes.h" +#endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE + +#if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE +#include "pigweed/rpc_services/Button.h" +#endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE + +#if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE +#include "pigweed/rpc_services/Descriptor.h" +#endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE + +#if defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +#include "pigweed/rpc_services/Device.h" +#endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE + +#if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE +#include "pigweed/rpc_services/Lighting.h" +#endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE + +#if defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE +#include "pigweed/rpc_services/Locking.h" +#endif // defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE + +#if defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE +#include "pigweed/rpc_services/OtCli.h" +#endif // defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE + +#if defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE +#include "pigweed/rpc_services/Thread.h" +#endif // defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE + +#if defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE +#define PW_TRACE_BUFFER_SIZE_BYTES 1024 +#include "pw_trace/trace.h" +#include "pw_trace_tokenized/trace_rpc_service_nanopb.h" + +// Define trace time for pw_trace +PW_TRACE_TIME_TYPE pw_trace_GetTraceTime() +{ + return (PW_TRACE_TIME_TYPE) chip::System::SystemClock().GetMonotonicMicroseconds64().count(); +} +// Microsecond time source +size_t pw_trace_GetTraceTimeTicksPerSecond() +{ + return 1000000; +} + +#endif // defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE + +namespace chip { +namespace rpc { + +#if defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +namespace { + +void reboot_timer_handler(struct k_timer * dummy) +{ + sys_reboot(0); +} +K_TIMER_DEFINE(reboot_timer, reboot_timer_handler, NULL); + +} // namespace + +class TelinkDevice final : public Device +{ +public: + pw::Status Reboot(const pw_protobuf_Empty & request, pw_protobuf_Empty & response) override + { + k_timer_start(&reboot_timer, K_SECONDS(1), K_FOREVER); + return pw::OkStatus(); + } +}; +#endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE + +#if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE +class TelinkButton final : public Button +{ +public: + pw::Status Event(const chip_rpc_ButtonEvent & request, pw_protobuf_Empty & response) override + { + GetAppTask().ButtonEventHandler((AppTask::ButtonId_t) request.idx, request.pushed); + return pw::OkStatus(); + } +}; +#endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE + +namespace { + +constexpr size_t kRpcTaskSize = 5120; +constexpr int kRpcPriority = 5; + +K_THREAD_STACK_DEFINE(rpc_stack_area, kRpcTaskSize); +struct k_thread rpc_thread_data; + +#if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +Attributes attributes_service; +#endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE + +#if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE +TelinkButton button_service; +#endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE + +#if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE +Descriptor descriptor_service; +#endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE + +#if defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE +TelinkDevice device_service; +#endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE + +#if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE +Lighting lighting_service; +#endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE + +#if defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE +Locking locking; +#endif // defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE + +#if defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE +OtCli ot_cli_service; +#endif // defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE + +#if defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE +Thread thread; +#endif // defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE + +#if defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE +pw::trace::TraceService trace_service; +#endif // defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE + +void RegisterServices(pw::rpc::Server & server) +{ +#if defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE + server.RegisterService(attributes_service); +#endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE + +#if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE + server.RegisterService(button_service); +#endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE + +#if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE + server.RegisterService(descriptor_service); +#endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE + +#if defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE + server.RegisterService(device_service); +#endif // defined(PW_RPC_DEVICE_SERVICE) && PW_RPC_DEVICE_SERVICE + +#if defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE + server.RegisterService(lighting_service); +#endif // defined(PW_RPC_LIGHTING_SERVICE) && PW_RPC_LIGHTING_SERVICE + +#if defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE + server.RegisterService(locking); +#endif // defined(PW_RPC_LOCKING_SERVICE) && PW_RPC_LOCKING_SERVICE + +#if defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE + server.RegisterService(ot_cli_service); +#endif // defined(PW_RPC_OTCLI_SERVICE) && PW_RPC_OTCLI_SERVICE + +#if defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE + server.RegisterService(thread); +#endif // defined(PW_RPC_THREAD_SERVICE) && PW_RPC_THREAD_SERVICE + +#if defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE + server.RegisterService(trace_service); + PW_TRACE_SET_ENABLED(true); +#endif // defined(PW_RPC_TRACING_SERVICE) && PW_RPC_TRACING_SERVICE +} + +} // namespace + +void RunRpcService(void *, void *, void *) +{ + Start(RegisterServices, &logger_mutex); +} + +k_tid_t Init() +{ + pw_sys_io_Init(); + k_tid_t tid = k_thread_create(&rpc_thread_data, rpc_stack_area, K_THREAD_STACK_SIZEOF(rpc_stack_area), RunRpcService, NULL, + NULL, NULL, kRpcPriority, 0, K_NO_WAIT); + return tid; +} + +} // namespace rpc +} // namespace chip diff --git a/examples/platform/telink/Rpc.h b/examples/platform/telink/Rpc.h new file mode 100644 index 00000000000000..82409b727cab26 --- /dev/null +++ b/examples/platform/telink/Rpc.h @@ -0,0 +1,33 @@ +/* + * + * Copyright (c) 2020 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. + */ + +#pragma once + +#include + +namespace chip { +namespace rpc { + +class TelinkButton; + +void RunRpcService(void *, void *, void *); + +k_tid_t Init(); + +} // namespace rpc +} // namespace chip diff --git a/examples/platform/telink/pw_sys_io/BUILD.gn b/examples/platform/telink/pw_sys_io/BUILD.gn new file mode 100644 index 00000000000000..3bd1d4310d8f0e --- /dev/null +++ b/examples/platform/telink/pw_sys_io/BUILD.gn @@ -0,0 +1,32 @@ +# Copyright (c) 2020 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. +# 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("//build_overrides/pigweed.gni") + +import("$dir_pw_build/target_types.gni") + +config("default_config") { + include_dirs = [ "public" ] +} + +pw_source_set("pw_sys_io_telink") { + sources = [ "sys_io_telink.cc" ] + + deps = [ + "$dir_pw_sys_io:default_putget_bytes", + "$dir_pw_sys_io:facade", + ] + + public_configs = [ ":default_config" ] +} diff --git a/examples/platform/telink/pw_sys_io/CMakeLists.txt b/examples/platform/telink/pw_sys_io/CMakeLists.txt new file mode 100644 index 00000000000000..43263ad2fad2dc --- /dev/null +++ b/examples/platform/telink/pw_sys_io/CMakeLists.txt @@ -0,0 +1,18 @@ +include($ENV{PW_ROOT}/pw_build/pigweed.cmake) + +add_library(suppress_zephyr_warnings INTERFACE) +target_compile_options(suppress_zephyr_warnings INTERFACE + -Wno-redundant-decls + -Wno-missing-field-initializers + -Wno-cast-qual + -Wno-undef +) + +pw_add_module_library(pw_sys_io.telink + SOURCES + sys_io_telink.cc + PRIVATE_DEPS + pw_sys_io + suppress_zephyr_warnings + pw_sys_io.default_putget_bytes +) diff --git a/examples/platform/telink/pw_sys_io/public/pw_sys_io_telink/init.h b/examples/platform/telink/pw_sys_io/public/pw_sys_io_telink/init.h new file mode 100644 index 00000000000000..9f375ed5fc2312 --- /dev/null +++ b/examples/platform/telink/pw_sys_io/public/pw_sys_io_telink/init.h @@ -0,0 +1,27 @@ +/* + * + * Copyright (c) 2020 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. + * 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. + */ + +#pragma once + +#include "pw_preprocessor/util.h" + +PW_EXTERN_C_START + +// The actual implement of PreMainInit() in sys_io_BACKEND. +void pw_sys_io_Init(); + +PW_EXTERN_C_END diff --git a/examples/platform/telink/pw_sys_io/sys_io_telink.cc b/examples/platform/telink/pw_sys_io/sys_io_telink.cc new file mode 100644 index 00000000000000..37af653075fdc6 --- /dev/null +++ b/examples/platform/telink/pw_sys_io/sys_io_telink.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2020 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. + * 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 "pw_sys_io/sys_io.h" +#include "zephyr/console/console.h" +#include +#include + +#ifdef CONFIG_USB +#include +#endif + +extern "C" void pw_sys_io_Init() +{ + int err; + +#ifdef CONFIG_USB + err = usb_enable(nullptr); + assert(err == 0); +#endif + + err = console_init(); + assert(err == 0); +} + +namespace pw::sys_io { + +Status ReadByte(std::byte * dest) +{ + if (!dest) + return Status::InvalidArgument(); + + const int c = console_getchar(); + *dest = static_cast(c); + + return c < 0 ? Status::FailedPrecondition() : OkStatus(); +} + +Status WriteByte(std::byte b) +{ + return console_putchar(static_cast(b)) < 0 ? Status::FailedPrecondition() : OkStatus(); +} + +// Writes a string using pw::sys_io, and add newline characters at the end. +StatusWithSize WriteLine(const std::string_view & s) +{ + size_t chars_written = 0; + StatusWithSize result = WriteBytes(pw::as_bytes(pw::span(s))); + if (!result.ok()) + { + return result; + } + chars_written += result.size(); + + // Write trailing newline. + result = WriteBytes(pw::as_bytes(pw::span("\r\n", 2))); + chars_written += result.size(); + + return StatusWithSize(result.status(), chars_written); +} + +} // namespace pw::sys_io diff --git a/examples/platform/telink/util/include/PigweedLogger.h b/examples/platform/telink/util/include/PigweedLogger.h new file mode 100644 index 00000000000000..ac106231802e50 --- /dev/null +++ b/examples/platform/telink/util/include/PigweedLogger.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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. + */ + +#pragma once + +#include + +namespace PigweedLogger { + +k_sem * GetSemaphore(); + +} // namespace PigweedLogger diff --git a/examples/platform/telink/util/src/PigweedLogger.cpp b/examples/platform/telink/util/src/PigweedLogger.cpp new file mode 100644 index 00000000000000..82b2d9f11fd9e2 --- /dev/null +++ b/examples/platform/telink/util/src/PigweedLogger.cpp @@ -0,0 +1,149 @@ +/* + * + * Copyright (c) 2020 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. + */ + +/* + * @file PigweedLogger.cpp + * + * This file contains a backend of Zephyr logging system, based on Pigweed HDLC + * over UART transport. It allows to send log messages even if the application + * needs to use HDLC/UART for another purpose like the RPC server. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pw_span/span.h" +#include +#include +#include + +namespace PigweedLogger { +namespace { + +#if CONFIG_LOG + +#if !CONFIG_LOG_MODE_IMMEDIATE +#error "Backend of Zephyr logger based on Pigweed HDLC requires LOG_MODE_IMMEDIATE=y" +#endif + +constexpr uint8_t kLogHdlcAddress = 1; // Send log messages to HDLC address 1 (other than RPC communication) +constexpr size_t kWriteBufferSize = 128; // Buffer for constructing HDLC frames + +// Exclusive access to the backend is needed to make sure that log messages coming +// from different threads are not interwoven. +K_SEM_DEFINE(sLoggerLock, 1, 1); +pw::stream::SysIoWriter sWriter; +size_t sWriteBufferPos; +uint8_t sWriteBuffer[kWriteBufferSize]; +bool sIsPanicMode; + +void flush() +{ + pw::hdlc::WriteUIFrame(kLogHdlcAddress, pw::as_bytes(pw::span(sWriteBuffer, sWriteBufferPos)), sWriter); + sWriteBufferPos = 0; +} + +int putString(uint8_t * buffer, size_t size, void * /* ctx */) +{ + assert(sWriteBufferPos < kWriteBufferSize); + + for (size_t i = 0; i < size; ++i) + { + // Send each line excluding "\r\n" in a separate frame + + if (buffer[i] == '\r') + continue; + + if (buffer[i] == '\n') + { + flush(); + continue; + } + + sWriteBuffer[sWriteBufferPos++] = buffer[i]; + + if (sWriteBufferPos == kWriteBufferSize) + flush(); + } + + return size; +} + +LOG_OUTPUT_DEFINE(pigweedLogOutput, putString, nullptr, 0); + +void init(const log_backend *) +{ + pw_sys_io_Init(); +} + +void processMessage(const struct log_backend * const backend, union log_msg_generic * msg) +{ + int ret = k_sem_take(&sLoggerLock, K_FOREVER); + assert(ret == 0); + + if (!sIsPanicMode) + { + log_format_func_t outputFunc = log_format_func_t_get(LOG_OUTPUT_TEXT); + + outputFunc(&pigweedLogOutput, &msg->log, log_backend_std_get_flags()); + } + + k_sem_give(&sLoggerLock); +} + +void panic(const log_backend *) +{ + int ret = k_sem_take(&sLoggerLock, K_FOREVER); + assert(ret == 0); + + log_backend_std_panic(&pigweedLogOutput); + flush(); + sIsPanicMode = true; + + k_sem_give(&sLoggerLock); +} + +const log_backend_api pigweedLogApi = { + .process = processMessage, + .panic = panic, + .init = init, +}; + +LOG_BACKEND_DEFINE(pigweedLogBackend, pigweedLogApi, /* autostart */ true); + +#endif // CONFIG_LOG + +} // namespace + +k_sem * GetSemaphore() +{ +#if CONFIG_LOG + return &sLoggerLock; +#else + return nullptr; +#endif +} + +} // namespace PigweedLogger