From 3d01ffa65c36afbba9c08f0b6602ec4cf797ec22 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 4 Dec 2023 18:28:05 +0100 Subject: [PATCH] [spinel] add vendor hook for the spinel host side (#9560) Allow out-of-tree compilation of vendor specific changes to the host side of spinel. By setting `OT_SPINEL_VENDOR_HOOK_SOURCE_DIR`, `OT_SPINEL_VENDOR_HOOK_SOURCE` and `OT_SPINEL_VENDOR_HOOK_HEADER`, the corresponding compile flags `OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK` and `OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER` get set which force the use of the `VendorRadioSpinel` class instead of the regular `RadioSpinel` in `src/Posix/platform/radio`. --- src/lib/spinel/CMakeLists.txt | 11 ++++ src/lib/spinel/example_vendor_hook.cpp | 67 +++++++++++++++++++++++ src/lib/spinel/example_vendor_hook.hpp | 59 ++++++++++++++++++++ src/lib/spinel/openthread-spinel-config.h | 20 +++++++ src/lib/spinel/radio_spinel.cpp | 6 ++ src/lib/spinel/radio_spinel.hpp | 20 +++++++ src/posix/platform/CMakeLists.txt | 1 + src/posix/platform/radio.cpp | 2 +- src/posix/platform/radio.hpp | 13 ++++- 9 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/lib/spinel/example_vendor_hook.cpp create mode 100644 src/lib/spinel/example_vendor_hook.hpp diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index e6cf16f58fd..8d552cf08cd 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -62,6 +62,17 @@ set(COMMON_SOURCES spinel_encoder.cpp ) +set(OT_SPINEL_VENDOR_HOOK_SOURCE "" CACHE STRING "set vendor hook source file for Spinel") +set(OT_SPINEL_VENDOR_HOOK_HEADER "" CACHE STRING "set vendor hook header file for Spinel") +if(OT_SPINEL_VENDOR_HOOK_SOURCE) + target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK=1") + target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK=1") + target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"") + target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"") + list(APPEND COMMON_SOURCES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}${OT_SPINEL_VENDOR_HOOK_SOURCE}) + list(APPEND COMMON_INCLUDES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}) +endif() + target_include_directories(openthread-radio-spinel PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) target_include_directories(openthread-spinel-ncp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) diff --git a/src/lib/spinel/example_vendor_hook.cpp b/src/lib/spinel/example_vendor_hook.cpp new file mode 100644 index 00000000000..3c0a73a159a --- /dev/null +++ b/src/lib/spinel/example_vendor_hook.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file shows how to implement the Radio Spinel vendor hook. + */ + +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + +#include OPENTRHEAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#include "common/log.hpp" +#include "lib/platform/exit_code.h" + +namespace ot { +namespace Spinel { + +otError RadioSpinel::VendorHandleValueIs(spinel_prop_key_t aPropKey) +{ + otError error = OT_ERROR_NONE; + + switch (aPropKey) + { + // TODO: Implement your property get handlers here. + // + // Get handler should retrieve the property value and then encode and write the + // value into the NCP buffer. If the "get" operation itself fails, handler should + // write a `LAST_STATUS` with the error status into the NCP buffer. `OT_ERROR_NO_BUFS` + // should be returned if NCP buffer is full and response cannot be written. + default: + error = OT_ERROR_NOT_FOUND; + break; + } +exit: + return error; +} + +} // namespace Spinel +} // namespace ot + +extern ot::Spinel::RadioSpinel &GetRadioSpinel(void); + +#endif // OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK diff --git a/src/lib/spinel/example_vendor_hook.hpp b/src/lib/spinel/example_vendor_hook.hpp new file mode 100644 index 00000000000..f9dedb6269f --- /dev/null +++ b/src/lib/spinel/example_vendor_hook.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file shows how to implement the Radio Spinel vendor hook. + */ + +#ifndef SPINEL_EXTENSION_H +#define SPINEL_EXTENSION_H + +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + +#include +#include "lib/spinel/radio_spinel.hpp" + +namespace ot { +namespace Spinel { + +class VendorRadioSpinel : public ot::Spinel::RadioSpinel +{ +public: + VendorRadioSpinel(void) + : ot::Spinel::RadioSpinel() + { + } + + // Add public/private methods or member variables +}; + +} // namespace Spinel +} // namespace ot + +#endif // OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#endif // SPINEL_EXTENSION_H diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index 6b3316ac19d..231a8683898 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -89,4 +89,24 @@ #define OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID SPINEL_HEADER_IID_3 #endif +/** + * @def OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + * + * Enables compilation of vendor specific code for Spinel + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#define OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK 0 +#endif + +/** + * @def OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER + * + * Header file defining class VendorRadioSpinel + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#define OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER "lib/spinel/example_vendor_hook.hpp" +#endif + #endif // OPENTHREAD_SPINEL_CONFIG_H_ diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 2cd1deaff8c..7da561ae2cc 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -747,6 +747,12 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, break; } } +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END) + { + error = VendorHandleValueIs(aKey); + } +#endif exit: UpdateParseErrorCount(error); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index da0b6521d10..bc0774d327e 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -1065,6 +1065,26 @@ class RadioSpinel */ void RestoreProperties(void); #endif +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + /** + * Defines a vendor "set property handler" hook to process vendor spinel properties. + * + * The vendor handler should return `OT_ERROR_NOT_FOUND` status if it does not support "set" operation for the + * given property key. Otherwise, the vendor handler should behave like other property set handlers, i.e., it + * should first decode the value from the input spinel frame and then perform the corresponding set operation. The + * handler should not prepare the spinel response and therefore should not write anything to the NCP buffer. The + * `otError` returned from handler (other than `OT_ERROR_NOT_FOUND`) indicates the error in either parsing of the + * input or the error of the set operation. In case of a successful "set", `NcpBase` set command handler will call + * the `VendorGetPropertyHandler()` for the same property key to prepare the response. + * + * @param[in] aPropKey The spinel property key. + * + * @returns OT_ERROR_NOT_FOUND if it does not support the given property key, otherwise the error in either parsing + * of the input or the "set" operation. + * + */ + otError VendorHandleValueIs(spinel_prop_key_t aPropKey); +#endif private: enum diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 6dd32881148..defbf24240f 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -195,6 +195,7 @@ target_include_directories(openthread-posix PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/core ${PROJECT_SOURCE_DIR}/third_party/mbedtls/repo/include + ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR} PUBLIC ${PROJECT_SOURCE_DIR}/src/posix/platform/include ) diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 491c6db1f73..44d011606e7 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -296,7 +296,7 @@ void Radio::GetIidListFromRadioUrl(spinel_iid_t (&aIidList)[Spinel::kSpinelHeade } // namespace Posix } // namespace ot -static ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); } +ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); } void platformRadioDeinit(void) { GetRadioSpinel().Deinit(); } diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp index 439483f00e1..00c958c2ff1 100644 --- a/src/posix/platform/radio.hpp +++ b/src/posix/platform/radio.hpp @@ -35,6 +35,11 @@ #include "posix/platform/radio_url.hpp" #include "posix/platform/spi_interface.hpp" #include "posix/platform/vendor_interface.hpp" +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#ifdef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#include OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#endif +#endif namespace ot { namespace Posix { @@ -104,8 +109,12 @@ class Radio #error "No Spinel interface is specified!" #endif - RadioUrl mRadioUrl; - Spinel::RadioSpinel mRadioSpinel; + RadioUrl mRadioUrl; +#if OPENTHREAD_ENABLE_SPINEL_VENDOR_HOOK + Spinel::VendorRadioSpinel mRadioSpinel; +#else + Spinel::RadioSpinel mRadioSpinel; +#endif Spinel::SpinelInterface *mSpinelInterface; OT_DEFINE_ALIGNED_VAR(mSpinelInterfaceRaw, kSpinelInterfaceRawSize, uint64_t);