[Telink] RPC implementation added (#23740)

Basic RPC functionaliy.

Signed-off-by: Maciej Bojczuk <maciej.bojczuk@telink-semi.com>

Signed-off-by: Maciej Bojczuk <maciej.bojczuk@telink-semi.com>
diff --git a/config/telink/app/enable-gnu-std.cmake b/config/telink/app/enable-gnu-std.cmake
index 38bacbe..e46c871 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 $<$<COMPILE_LANGUAGE:CXX>:-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 6ef6ac2..6a48424 100644
--- a/config/telink/chip-module/CMakeLists.txt
+++ b/config/telink/chip-module/CMakeLists.txt
@@ -91,6 +91,7 @@
     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 @@
     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 @@
     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 0000000..5061d53
--- /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 0000000..9a9b7e5
--- /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 <zephyr/zephyr.h>
+
+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 c4af6cb..1cf675a 100644
--- a/examples/lighting-app/telink/CMakeLists.txt
+++ b/examples/lighting-app/telink/CMakeLists.txt
@@ -66,3 +66,162 @@
 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($<BUILD_INTERFACE:zephyr_interface>)
+
+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 018fdd3..d6ace81 100644
--- a/examples/lighting-app/telink/Readme.md
+++ b/examples/lighting-app/telink/Readme.md
@@ -223,3 +223,15 @@
 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 0b1b588..09ba6ef 100644
--- a/examples/lighting-app/telink/include/AppTask.h
+++ b/examples/lighting-app/telink/include/AppTask.h
@@ -27,6 +27,10 @@
 #include <platform/telink/FactoryDataProvider.h>
 #endif
 
+#ifdef CONFIG_CHIP_PW_RPC
+#include "Rpc.h"
+#endif
+
 #include <cstdint>
 
 struct k_timer;
@@ -40,7 +44,18 @@
     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 @@
     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 0000000..a976211
--- /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 00db77c..a310bdb 100644
--- a/examples/lighting-app/telink/src/AppTask.cpp
+++ b/examples/lighting-app/telink/src/AppTask.cpp
@@ -457,6 +457,30 @@
     }
 }
 
+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 4ea2a51..55e7580 100644
--- a/examples/lighting-app/telink/src/main.cpp
+++ b/examples/lighting-app/telink/src/main.cpp
@@ -23,6 +23,10 @@
 
 #include <zephyr/kernel.h>
 
+#ifdef CONFIG_CHIP_PW_RPC
+#include "Rpc.h"
+#endif
+
 LOG_MODULE_REGISTER(app);
 
 using namespace ::chip;
@@ -33,6 +37,10 @@
 {
     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 0000000..93348fc
--- /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 <zephyr/logging/log.h>
+#include <zephyr/sys/reboot.h>
+
+#include <zephyr/kernel.h>
+
+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 0000000..82409b7
--- /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 <zephyr/kernel.h>
+
+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 0000000..3bd1d43
--- /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 0000000..43263ad
--- /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 0000000..9f375ed
--- /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 0000000..37af653
--- /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 <cinttypes>
+
+#include "pw_sys_io/sys_io.h"
+#include "zephyr/console/console.h"
+#include <cassert>
+#include <zephyr/zephyr.h>
+
+#ifdef CONFIG_USB
+#include <zephyr/usb/usb_device.h>
+#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<std::byte>(c);
+
+    return c < 0 ? Status::FailedPrecondition() : OkStatus();
+}
+
+Status WriteByte(std::byte b)
+{
+    return console_putchar(static_cast<char>(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 0000000..ac10623
--- /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 <zephyr/kernel.h>
+
+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 0000000..82b2d9f
--- /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 <zephyr/logging/log.h>
+#include <zephyr/logging/log_backend.h>
+#include <zephyr/logging/log_backend_std.h>
+#include <zephyr/logging/log_output.h>
+#include <zephyr/zephyr.h>
+
+#include <pw_hdlc/encoder.h>
+#include <pw_stream/sys_io_stream.h>
+#include <pw_sys_io_telink/init.h>
+
+#include "pw_span/span.h"
+#include <cassert>
+#include <cstdint>
+#include <string_view>
+
+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