modules: openthread: Add of spinel hdlc rcp host interface
Add a spinel support to an RCP design, the core of OpenThread lives on the host processor connected to an RCP radio controller over a HDLC interface. Signed-off-by: Jamel Arbi <jamel.arbi@nxp.com>
This commit is contained in:
parent
70add85f9f
commit
ec4e0192fb
6 changed files with 1187 additions and 0 deletions
|
|
@ -249,6 +249,17 @@ list(APPEND ot_libs openthread-mtd)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_HDLC_RCP_IF)
|
||||
list(APPEND ot_libs
|
||||
ot-config
|
||||
openthread-platform
|
||||
openthread-radio-spinel
|
||||
openthread-spinel-ncp
|
||||
openthread-url
|
||||
openthread-hdlc
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_OPENTHREAD_SETTINGS_RAM)
|
||||
target_compile_options(openthread-platform-utils PRIVATE
|
||||
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,18 @@ zephyr_library_sources(
|
|||
entropy.c
|
||||
misc.c
|
||||
platform.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifndef(CONFIG_HDLC_RCP_IF
|
||||
radio.c
|
||||
spi.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_HDLC_RCP_IF
|
||||
radio_spinel.cpp
|
||||
hdlc_interface.cpp
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_BLE_TCAT ble.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_DIAG diag.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_COPROCESSOR uart.c)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2024 NXP.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
|
@ -131,3 +132,10 @@ uint16_t otPlatTimeGetXtalAccuracy(void)
|
|||
{
|
||||
return otPlatRadioGetCslAccuracy(NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HDLC_RCP_IF
|
||||
uint64_t otPlatTimeGet(void)
|
||||
{
|
||||
return k_ticks_to_us_floor64(k_uptime_ticks());
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
363
modules/openthread/platform/hdlc_interface.cpp
Normal file
363
modules/openthread/platform/hdlc_interface.cpp
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* Copyright (c) 2021, The OpenThread Authors.
|
||||
* Copyright (c) 2022-2024, NXP.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <openthread/tasklet.h>
|
||||
#include <openthread/platform/alarm-milli.h>
|
||||
#include <common/logging.hpp>
|
||||
#include <common/code_utils.hpp>
|
||||
#include "hdlc_interface.hpp"
|
||||
|
||||
namespace ot
|
||||
{
|
||||
|
||||
namespace Hdlc
|
||||
{
|
||||
|
||||
HdlcInterface::HdlcInterface(const Url::Url &aRadioUrl)
|
||||
: mEncoderBuffer(), mHdlcEncoder(mEncoderBuffer), hdlc_rx_callback(nullptr),
|
||||
mReceiveFrameBuffer(nullptr), mReceiveFrameCallback(nullptr),
|
||||
mReceiveFrameContext(nullptr), mHdlcSpinelDecoder(), mIsInitialized(false),
|
||||
mSavedFrame(nullptr), mSavedFrameLen(0), mIsSpinelBufferFull(false), mRadioUrl(aRadioUrl)
|
||||
{
|
||||
bool is_device_ready;
|
||||
|
||||
hdlc_rx_callback = HdlcRxCallback;
|
||||
|
||||
radio_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_hdlc_rcp_if));
|
||||
is_device_ready = device_is_ready(radio_dev);
|
||||
__ASSERT(is_device_ready == true, "Radio device is not ready");
|
||||
|
||||
hdlc_api = (struct hdlc_api *)radio_dev->api;
|
||||
__ASSERT(hdlc_api != NULL, "Radio device initialization failed");
|
||||
}
|
||||
|
||||
HdlcInterface::~HdlcInterface(void)
|
||||
{
|
||||
}
|
||||
|
||||
otError HdlcInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext,
|
||||
RxFrameBuffer &aFrameBuffer)
|
||||
{
|
||||
int status;
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
if (!mIsInitialized) {
|
||||
/* Event initialization */
|
||||
k_event_init(&spinel_hdlc_event);
|
||||
|
||||
/* Read/Write semaphores initialization */
|
||||
k_mutex_init(&spinel_hdlc_wr_lock);
|
||||
k_mutex_init(&spinel_hdlc_rd_lock);
|
||||
|
||||
/* Message queue initialization */
|
||||
k_msgq_init(&spinel_hdlc_msgq, &spinel_hdlc_msgq_buffer, 1, 1);
|
||||
|
||||
mHdlcSpinelDecoder.Init(mRxSpinelFrameBuffer, HandleHdlcFrame, this);
|
||||
mReceiveFrameCallback = aCallback;
|
||||
mReceiveFrameContext = aCallbackContext;
|
||||
mReceiveFrameBuffer = &aFrameBuffer;
|
||||
|
||||
/* Initialize the HDLC interface */
|
||||
status = hdlc_api->register_rx_cb(hdlc_rx_callback, this);
|
||||
if (status == 0) {
|
||||
mIsInitialized = true;
|
||||
} else {
|
||||
otLogDebgPlat("Failed to initialize HDLC interface %d", status);
|
||||
error = OT_ERROR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void HdlcInterface::Deinit(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = hdlc_api->deinit();
|
||||
if (status == 0) {
|
||||
mIsInitialized = false;
|
||||
} else {
|
||||
otLogDebgPlat("Failed to terminate HDLC interface %d", status);
|
||||
}
|
||||
|
||||
mReceiveFrameCallback = nullptr;
|
||||
mReceiveFrameContext = nullptr;
|
||||
mReceiveFrameBuffer = nullptr;
|
||||
}
|
||||
|
||||
void HdlcInterface::Process(const void *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
TryReadAndDecode(false);
|
||||
}
|
||||
|
||||
otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
/* Protect concurrent Send operation to avoid any buffer corruption */
|
||||
if (k_mutex_lock(&spinel_hdlc_wr_lock, K_FOREVER) != 0) {
|
||||
error = OT_ERROR_FAILED;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
assert(mEncoderBuffer.IsEmpty());
|
||||
|
||||
SuccessOrExit(error = mHdlcEncoder.BeginFrame());
|
||||
SuccessOrExit(error = mHdlcEncoder.Encode(aFrame, aLength));
|
||||
SuccessOrExit(error = mHdlcEncoder.EndFrame());
|
||||
otLogDebgPlat("frame len to send = %d/%d", mEncoderBuffer.GetLength(), aLength);
|
||||
SuccessOrExit(error = Write(mEncoderBuffer.GetFrame(), mEncoderBuffer.GetLength()));
|
||||
|
||||
exit:
|
||||
|
||||
k_mutex_unlock(&spinel_hdlc_wr_lock);
|
||||
|
||||
if (error != OT_ERROR_NONE) {
|
||||
otLogCritPlat("error = 0x%x", error);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs)
|
||||
{
|
||||
otError error = OT_ERROR_RESPONSE_TIMEOUT;
|
||||
uint32_t timeout_ms = (uint32_t)(aTimeoutUs / 1000U);
|
||||
uint32_t event_bits;
|
||||
|
||||
do {
|
||||
/* Wait for k_spinel_hdlc_frame_ready_event indicating a frame has been received */
|
||||
event_bits = k_event_wait(&spinel_hdlc_event,
|
||||
HdlcInterface::k_spinel_hdlc_frame_ready_event, false,
|
||||
K_MSEC(timeout_ms));
|
||||
k_event_clear(&spinel_hdlc_event, HdlcInterface::k_spinel_hdlc_frame_ready_event);
|
||||
if ((event_bits & HdlcInterface::k_spinel_hdlc_frame_ready_event) != 0) {
|
||||
/* Event is set, it means a frame has been received and can be decoded
|
||||
* Note: The event is set whenever a frame is received, even when ot task is
|
||||
* not blocked in WaitForFrame it means the event could have been set for a
|
||||
* previous frame If TryReadAndDecode returns 0, it means the event was set
|
||||
* for a previous frame, so we loop and wait again If TryReadAndDecode
|
||||
* returns anything else, it means there's real data to process, so we can
|
||||
* exit from WaitForFrame */
|
||||
|
||||
if (TryReadAndDecode(true) != 0) {
|
||||
error = OT_ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* The event wasn't set within the timeout range, we exit with a timeout
|
||||
* error */
|
||||
otLogDebgPlat("WaitForFrame timeout");
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void HdlcInterface::ProcessRxData(uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint8_t event;
|
||||
uint32_t remainingRxBufferSize = 0;
|
||||
|
||||
k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
|
||||
|
||||
do {
|
||||
/* Check if we have enough space to store the frame in the buffer */
|
||||
remainingRxBufferSize =
|
||||
mRxSpinelFrameBuffer.GetFrameMaxLength() - mRxSpinelFrameBuffer.GetLength();
|
||||
otLogDebgPlat("remainingRxBufferSize = %u", remainingRxBufferSize);
|
||||
|
||||
if (remainingRxBufferSize >= len) {
|
||||
mHdlcSpinelDecoder.Decode(data, len);
|
||||
break;
|
||||
} else {
|
||||
mIsSpinelBufferFull = true;
|
||||
otLogDebgPlat("Spinel buffer full remainingRxLen = %u",
|
||||
remainingRxBufferSize);
|
||||
|
||||
/* Send a signal to the openthread task to indicate to empty the spinel
|
||||
* buffer */
|
||||
otTaskletsSignalPending(NULL);
|
||||
|
||||
/* Give the mutex */
|
||||
k_mutex_unlock(&spinel_hdlc_rd_lock);
|
||||
|
||||
/* Lock the task here until the spinel buffer becomes empty */
|
||||
k_msgq_get(&spinel_hdlc_msgq, &event, K_FOREVER);
|
||||
|
||||
/* take the mutex again */
|
||||
k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
k_mutex_unlock(&spinel_hdlc_rd_lock);
|
||||
}
|
||||
|
||||
otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
|
||||
{
|
||||
otError otResult = OT_ERROR_NONE;
|
||||
int ret;
|
||||
|
||||
otLogDebgPlat("Send tx frame len = %d", aLength);
|
||||
|
||||
ret = hdlc_api->send((uint8_t *)aFrame, aLength);
|
||||
if (ret != 0) {
|
||||
otResult = OT_ERROR_FAILED;
|
||||
otLogCritPlat("Error send %d", ret);
|
||||
}
|
||||
|
||||
/* Always clear the encoder */
|
||||
mEncoderBuffer.Clear();
|
||||
return otResult;
|
||||
}
|
||||
|
||||
uint32_t HdlcInterface::TryReadAndDecode(bool fullRead)
|
||||
{
|
||||
uint32_t totalBytesRead = 0;
|
||||
uint32_t i = 0;
|
||||
uint8_t event = 1;
|
||||
uint8_t *oldFrame = mSavedFrame;
|
||||
uint16_t oldLen = mSavedFrameLen;
|
||||
otError savedFrameFound = OT_ERROR_NONE;
|
||||
|
||||
(void)fullRead;
|
||||
|
||||
k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
|
||||
|
||||
savedFrameFound = mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
|
||||
|
||||
while (savedFrameFound == OT_ERROR_NONE) {
|
||||
/* Copy the data to the ot frame buffer */
|
||||
for (i = 0; i < mSavedFrameLen; i++) {
|
||||
if (mReceiveFrameBuffer->WriteByte(mSavedFrame[i]) != OT_ERROR_NONE) {
|
||||
mReceiveFrameBuffer->UndoLastWrites(i);
|
||||
/* No more space restore the mSavedFrame to the previous frame */
|
||||
mSavedFrame = oldFrame;
|
||||
mSavedFrameLen = oldLen;
|
||||
/* Signal the ot task to re-try later */
|
||||
otTaskletsSignalPending(NULL);
|
||||
otLogDebgPlat("No more space");
|
||||
k_mutex_unlock(&spinel_hdlc_rd_lock);
|
||||
return totalBytesRead;
|
||||
}
|
||||
totalBytesRead++;
|
||||
}
|
||||
otLogDebgPlat("Frame len %d consumed", mSavedFrameLen);
|
||||
mReceiveFrameCallback(mReceiveFrameContext);
|
||||
oldFrame = mSavedFrame;
|
||||
oldLen = mSavedFrameLen;
|
||||
savedFrameFound =
|
||||
mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
|
||||
}
|
||||
|
||||
if (savedFrameFound != OT_ERROR_NONE) {
|
||||
/* No more frame saved clear the buffer */
|
||||
mRxSpinelFrameBuffer.ClearSavedFrames();
|
||||
/* If the spinel queue was locked */
|
||||
if (mIsSpinelBufferFull) {
|
||||
mIsSpinelBufferFull = false;
|
||||
/* Send an event to unlock the task */
|
||||
k_msgq_put(&spinel_hdlc_msgq, (void *)&event, K_FOREVER);
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&spinel_hdlc_rd_lock);
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
|
||||
{
|
||||
static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
|
||||
}
|
||||
|
||||
void HdlcInterface::HandleHdlcFrame(otError aError)
|
||||
{
|
||||
uint8_t *buf = mRxSpinelFrameBuffer.GetFrame();
|
||||
uint16_t bufLength = mRxSpinelFrameBuffer.GetLength();
|
||||
|
||||
otDumpDebgPlat("RX FRAME", buf, bufLength);
|
||||
|
||||
if (aError == OT_ERROR_NONE && bufLength > 0) {
|
||||
if ((buf[0] & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG) {
|
||||
otLogDebgPlat("Frame correctly received %d", bufLength);
|
||||
/* Save the frame */
|
||||
mRxSpinelFrameBuffer.SaveFrame();
|
||||
|
||||
/* Send a signal to the openthread task to indicate that a spinel data is
|
||||
* pending */
|
||||
otTaskletsSignalPending(NULL);
|
||||
|
||||
/* Notify WaitForFrame that a frame is ready */
|
||||
/* TBC: k_event_post or k_event_set */
|
||||
k_event_set(&spinel_hdlc_event,
|
||||
HdlcInterface::k_spinel_hdlc_frame_ready_event);
|
||||
} else {
|
||||
/* Give a chance to a child class to process this HDLC content
|
||||
* The current class treats it as an error case because it's supposed to
|
||||
* receive only Spinel frames If there's a need to share a same transport
|
||||
* interface with another protocol, a child class must override this method
|
||||
*/
|
||||
HandleUnknownHdlcContent(buf, bufLength);
|
||||
|
||||
/* Not a Spinel frame, discard */
|
||||
mRxSpinelFrameBuffer.DiscardFrame();
|
||||
}
|
||||
} else {
|
||||
otLogCritPlat("Frame will be discarded error = 0x%x", aError);
|
||||
mRxSpinelFrameBuffer.DiscardFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcInterface::HdlcRxCallback(uint8_t *data, uint16_t len, void *param)
|
||||
{
|
||||
static_cast<HdlcInterface *>(param)->ProcessRxData(data, len);
|
||||
}
|
||||
|
||||
void HdlcInterface::HandleUnknownHdlcContent(uint8_t *buffer, uint16_t len)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(buffer);
|
||||
OT_UNUSED_VARIABLE(len);
|
||||
otLogCritPlat("Unsupported HDLC content received (not Spinel)");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void HdlcInterface::OnRcpReset(void)
|
||||
{
|
||||
mHdlcSpinelDecoder.Reset();
|
||||
}
|
||||
|
||||
} // namespace Hdlc
|
||||
|
||||
} /* namespace ot */
|
||||
206
modules/openthread/platform/hdlc_interface.hpp
Normal file
206
modules/openthread/platform/hdlc_interface.hpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright (c) 2021, The OpenThread Authors.
|
||||
* Copyright (c) 2022-2024, NXP.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OT_SPINEL_HDLC_HPP_
|
||||
#define OT_SPINEL_HDLC_HPP_
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h>
|
||||
|
||||
#include "lib/url/url.hpp"
|
||||
#include "lib/hdlc/hdlc.hpp"
|
||||
#include "lib/spinel/spinel.h"
|
||||
#include "lib/spinel/spinel_interface.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
namespace Hdlc {
|
||||
|
||||
typedef uint8_t HdlcSpinelContext;
|
||||
|
||||
/**
|
||||
* This class defines an HDLC spinel interface to the Radio Co-processor (RCP).
|
||||
*
|
||||
*/
|
||||
class HdlcInterface : public ot::Spinel::SpinelInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This constructor initializes the object.
|
||||
*
|
||||
* @param[in] aRadioUrl Radio url
|
||||
*
|
||||
*/
|
||||
HdlcInterface(const Url::Url &aRadioUrl);
|
||||
|
||||
/**
|
||||
* This destructor deinitializes the object.
|
||||
*
|
||||
*/
|
||||
virtual ~HdlcInterface(void);
|
||||
|
||||
/**
|
||||
* This method initializes the HDLC interface.
|
||||
*
|
||||
*/
|
||||
otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer);
|
||||
|
||||
/**
|
||||
* This method deinitializes the HDLC interface.
|
||||
*
|
||||
*/
|
||||
void Deinit(void);
|
||||
|
||||
/**
|
||||
* This method performs radio driver processing.
|
||||
*
|
||||
* @param[in] aInstance The ot instance
|
||||
*
|
||||
*/
|
||||
void Process(const void *aInstance);
|
||||
|
||||
/**
|
||||
* This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
|
||||
*
|
||||
* This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for
|
||||
* up to `kMaxWaitTime` interval.
|
||||
*
|
||||
* @param[in] aFrame A pointer to buffer containing the spinel frame to send.
|
||||
* @param[in] aLength The length (number of bytes) in the frame.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame.
|
||||
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame.
|
||||
* @retval OT_ERROR_FAILED Failed to send due to socket not becoming writable within `kMaxWaitTime`.
|
||||
*
|
||||
*/
|
||||
otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* This method waits for receiving part or all of spinel frame within specified timeout.
|
||||
*
|
||||
* @param[in] aTimeoutUs The timeout value in microseconds.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Part or all of spinel frame is received.
|
||||
* @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeoutUs.
|
||||
*
|
||||
*/
|
||||
otError WaitForFrame(uint64_t aTimeoutUs);
|
||||
|
||||
/**
|
||||
* This method is called by the HDLC RX Callback when a HDLC message has been received
|
||||
*
|
||||
* It will decode and store the Spinel frames in a temporary Spinel frame buffer (mRxSpinelFrameBuffer)
|
||||
* This buffer will be then copied to the OpenThread Spinel frame buffer, from the OpenThread task context
|
||||
*
|
||||
* @param[in] data A pointer to buffer containing the HDLC message to decode.
|
||||
* @param[in] len The length (number of bytes) in the message.
|
||||
*/
|
||||
void ProcessRxData(uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* This method is called when RCP failure detected and resets internal states of the interface.
|
||||
*
|
||||
*/
|
||||
void OnRcpReset(void);
|
||||
|
||||
/**
|
||||
* This method is called when RCP is reset to recreate the connection with it.
|
||||
* Intentionally empty.
|
||||
*
|
||||
*/
|
||||
otError ResetConnection(void) { return OT_ERROR_NONE; }
|
||||
|
||||
/**
|
||||
* This method hardware resets the RCP.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully reset the RCP.
|
||||
* @retval OT_ERROR_NOT_IMPLEMENTED The hardware reset is not implemented.
|
||||
*
|
||||
*/
|
||||
otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
private:
|
||||
|
||||
enum
|
||||
{
|
||||
/* HDLC encoder buffer must be larger than the max spinel frame size to be able to handle the HDLC overhead
|
||||
* As a host, a TX frame will always contain only 1 spinel frame + HDLC overhead
|
||||
* Sizing the buffer for 2 spinel frames should be large enough to handle this */
|
||||
kEncoderBufferSize = SPINEL_FRAME_MAX_SIZE * 2,
|
||||
kMaxMultiFrameSize = 2048,
|
||||
k_spinel_hdlc_frame_ready_event = 1 << 0,
|
||||
};
|
||||
|
||||
ot::Spinel::FrameBuffer<kEncoderBufferSize> mEncoderBuffer;
|
||||
ot::Hdlc::Encoder mHdlcEncoder;
|
||||
hdlc_rx_callback_t hdlc_rx_callback;
|
||||
ot::Spinel::SpinelInterface::RxFrameBuffer *mReceiveFrameBuffer;
|
||||
ot::Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback;
|
||||
void *mReceiveFrameContext;
|
||||
ot::Spinel::MultiFrameBuffer<kMaxMultiFrameSize> mRxSpinelFrameBuffer;
|
||||
ot::Hdlc::Decoder mHdlcSpinelDecoder;
|
||||
bool mIsInitialized;
|
||||
uint8_t *mSavedFrame;
|
||||
uint16_t mSavedFrameLen;
|
||||
bool mIsSpinelBufferFull;
|
||||
const Url::Url &mRadioUrl;
|
||||
|
||||
/* Spinel HDLC interface */
|
||||
const struct device *radio_dev;
|
||||
struct hdlc_api *hdlc_api;
|
||||
|
||||
struct k_event spinel_hdlc_event;
|
||||
struct k_mutex spinel_hdlc_wr_lock;
|
||||
struct k_mutex spinel_hdlc_rd_lock;
|
||||
struct k_msgq spinel_hdlc_msgq;
|
||||
char spinel_hdlc_msgq_buffer;
|
||||
|
||||
otError Write(const uint8_t *aFrame, uint16_t aLength);
|
||||
uint32_t TryReadAndDecode(bool fullRead);
|
||||
void HandleHdlcFrame(otError aError);
|
||||
static void HandleHdlcFrame(void *aContext, otError aError);
|
||||
static void HdlcRxCallback(uint8_t *data, uint16_t len, void *param);
|
||||
|
||||
const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return nullptr;}
|
||||
uint32_t GetBusSpeed(void) const { return 0; }
|
||||
void UpdateFdSet(void *aMainloopContext)
|
||||
{
|
||||
(void)aMainloopContext;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void HandleUnknownHdlcContent(uint8_t *buffer, uint16_t len);
|
||||
};
|
||||
|
||||
} // namespace Zephyr
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OT_SPINEL_HDLC_HPP_
|
||||
591
modules/openthread/platform/radio_spinel.cpp
Normal file
591
modules/openthread/platform/radio_spinel.cpp
Normal file
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
* Copyright (c) 2021, The OpenThread Authors.
|
||||
* Copyright (c) 2022-2024, NXP.
|
||||
*
|
||||
* 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 implements OpenThread platform driver API in openthread/platform/radio.h.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openthread/platform/radio.h>
|
||||
#include <lib/platform/exit_code.h>
|
||||
#include <lib/spinel/radio_spinel.hpp>
|
||||
#include <lib/spinel/spinel.h>
|
||||
#include <lib/url/url.hpp>
|
||||
#include "hdlc_interface.hpp"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/net/net_pkt.h>
|
||||
#include <openthread-system.h>
|
||||
|
||||
enum pending_events {
|
||||
PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send */
|
||||
PENDING_EVENT_COUNT /* Keep last */
|
||||
};
|
||||
|
||||
ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT);
|
||||
K_FIFO_DEFINE(tx_pkt_fifo);
|
||||
|
||||
static ot::Spinel::RadioSpinel *psRadioSpinel;
|
||||
static ot::Url::Url *psRadioUrl;
|
||||
static ot::Hdlc::HdlcInterface *pSpinelInterface;
|
||||
static ot::Spinel::SpinelDriver *psSpinelDriver;
|
||||
|
||||
static const otRadioCaps sRequiredRadioCaps =
|
||||
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||
OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
|
||||
#endif
|
||||
OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
|
||||
|
||||
static inline bool is_pending_event_set(enum pending_events event)
|
||||
{
|
||||
return atomic_test_bit(pending_events, event);
|
||||
}
|
||||
|
||||
static void set_pending_event(enum pending_events event)
|
||||
{
|
||||
atomic_set_bit(pending_events, event);
|
||||
otSysEventSignalPending();
|
||||
}
|
||||
|
||||
static void reset_pending_event(enum pending_events event)
|
||||
{
|
||||
atomic_clear_bit(pending_events, event);
|
||||
}
|
||||
|
||||
static void openthread_handle_frame_to_send(otInstance *instance, struct net_pkt *pkt)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
otMessage *message;
|
||||
otMessageSettings settings;
|
||||
|
||||
NET_DBG("Sending Ip6 packet to ot stack");
|
||||
|
||||
settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL;
|
||||
settings.mLinkSecurityEnabled = true;
|
||||
message = otIp6NewMessage(instance, &settings);
|
||||
if (message == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (buf = pkt->buffer; buf; buf = buf->frags) {
|
||||
if (otMessageAppend(message, buf->data, buf->len) != OT_ERROR_NONE) {
|
||||
NET_ERR("Error while appending to otMessage");
|
||||
otMessageFree(message);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (otIp6Send(instance, message) != OT_ERROR_NONE) {
|
||||
NET_ERR("Error while calling otIp6Send");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
|
||||
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->GetIeeeEui64(aIeeeEui64));
|
||||
}
|
||||
|
||||
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->SetPanId(panid));
|
||||
}
|
||||
|
||||
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
otExtAddress addr;
|
||||
|
||||
for (size_t i = 0; i < sizeof(addr); i++) {
|
||||
addr.m8[i] = aAddress->m8[sizeof(addr) - 1 - i];
|
||||
}
|
||||
|
||||
SuccessOrDie(psRadioSpinel->SetExtendedAddress(addr));
|
||||
}
|
||||
|
||||
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->SetShortAddress(aAddress));
|
||||
}
|
||||
|
||||
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->SetPromiscuous(aEnable));
|
||||
}
|
||||
|
||||
bool otPlatRadioIsEnabled(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->IsEnabled();
|
||||
}
|
||||
|
||||
otError otPlatRadioEnable(otInstance *aInstance)
|
||||
{
|
||||
return psRadioSpinel->Enable(aInstance);
|
||||
}
|
||||
|
||||
otError otPlatRadioDisable(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->Disable();
|
||||
}
|
||||
|
||||
otError otPlatRadioSleep(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->Sleep();
|
||||
}
|
||||
|
||||
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->Receive(aChannel);
|
||||
}
|
||||
|
||||
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->Transmit(*aFrame);
|
||||
}
|
||||
|
||||
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return &psRadioSpinel->GetTransmitFrame();
|
||||
}
|
||||
|
||||
int8_t otPlatRadioGetRssi(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetRssi();
|
||||
}
|
||||
|
||||
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetRadioCaps();
|
||||
}
|
||||
|
||||
const char *otPlatRadioGetVersionString(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetVersion();
|
||||
}
|
||||
|
||||
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->IsPromiscuous();
|
||||
}
|
||||
|
||||
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->EnableSrcMatch(aEnable));
|
||||
}
|
||||
|
||||
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->AddSrcMatchShortEntry(aShortAddress);
|
||||
}
|
||||
|
||||
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
otExtAddress addr;
|
||||
|
||||
for (size_t i = 0; i < sizeof(addr); i++) {
|
||||
addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
|
||||
}
|
||||
|
||||
return psRadioSpinel->AddSrcMatchExtEntry(addr);
|
||||
}
|
||||
|
||||
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->ClearSrcMatchShortEntry(aShortAddress);
|
||||
}
|
||||
|
||||
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
otExtAddress addr;
|
||||
|
||||
for (size_t i = 0; i < sizeof(addr); i++) {
|
||||
addr.m8[i] = aExtAddress->m8[sizeof(addr) - 1 - i];
|
||||
}
|
||||
|
||||
return psRadioSpinel->ClearSrcMatchExtEntry(addr);
|
||||
}
|
||||
|
||||
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->ClearSrcMatchShortEntries());
|
||||
}
|
||||
|
||||
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
SuccessOrDie(psRadioSpinel->ClearSrcMatchExtEntries());
|
||||
}
|
||||
|
||||
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->EnergyScan(aScanChannel, aScanDuration);
|
||||
}
|
||||
|
||||
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
|
||||
{
|
||||
otError error;
|
||||
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
VerifyOrExit(aPower != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
error = psRadioSpinel->GetTransmitPower(*aPower);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->SetTransmitPower(aPower);
|
||||
}
|
||||
|
||||
otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
|
||||
{
|
||||
otError error;
|
||||
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
VerifyOrExit(aThreshold != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
error = psRadioSpinel->GetCcaEnergyDetectThreshold(*aThreshold);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->SetCcaEnergyDetectThreshold(aThreshold);
|
||||
}
|
||||
|
||||
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetReceiveSensitivity();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
|
||||
otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->SetCoexEnabled(aEnabled);
|
||||
}
|
||||
|
||||
bool otPlatRadioIsCoexEnabled(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->IsCoexEnabled();
|
||||
}
|
||||
|
||||
otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aCoexMetrics != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
error = psRadioSpinel->GetCoexMetrics(*aCoexMetrics);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
otError otPlatDiagProcess(otInstance *aInstance, int argc, char *argv[], char *aOutput,
|
||||
size_t aOutputMaxLen)
|
||||
{
|
||||
/* Deliver the platform specific diags commands to radio only ncp */
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'};
|
||||
char *cur = cmd;
|
||||
char *end = cmd + sizeof(cmd);
|
||||
|
||||
for (int index = 0; index < argc; index++) {
|
||||
cur += snprintf(cur, static_cast<size_t>(end - cur), "%s ", argv[index]);
|
||||
}
|
||||
|
||||
return psRadioSpinel->PlatDiagProcess(cmd, aOutput, aOutputMaxLen);
|
||||
}
|
||||
|
||||
void otPlatDiagModeSet(bool aMode)
|
||||
{
|
||||
SuccessOrExit(psRadioSpinel->PlatDiagProcess(aMode ? "start" : "stop", NULL, 0));
|
||||
psRadioSpinel->SetDiagEnabled(aMode);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool otPlatDiagModeGet(void)
|
||||
{
|
||||
return psRadioSpinel->IsDiagEnabled();
|
||||
}
|
||||
|
||||
void otPlatDiagTxPowerSet(int8_t aTxPower)
|
||||
{
|
||||
char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "power %d", aTxPower);
|
||||
SuccessOrExit(psRadioSpinel->PlatDiagProcess(cmd, NULL, 0));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void otPlatDiagChannelSet(uint8_t aChannel)
|
||||
{
|
||||
char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE];
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "channel %d", aChannel);
|
||||
SuccessOrExit(psRadioSpinel->PlatDiagProcess(cmd, NULL, 0));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
OT_UNUSED_VARIABLE(aFrame);
|
||||
OT_UNUSED_VARIABLE(aError);
|
||||
}
|
||||
|
||||
void otPlatDiagAlarmCallback(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
}
|
||||
#endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */
|
||||
|
||||
uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetRadioChannelMask(false);
|
||||
}
|
||||
|
||||
uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetRadioChannelMask(true);
|
||||
}
|
||||
|
||||
otRadioState otPlatRadioGetState(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetState();
|
||||
}
|
||||
|
||||
void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId,
|
||||
const otMacKeyMaterial *aPrevKey, const otMacKeyMaterial *aCurrKey,
|
||||
const otMacKeyMaterial *aNextKey, otRadioKeyType aKeyType)
|
||||
{
|
||||
SuccessOrDie(psRadioSpinel->SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey));
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
OT_UNUSED_VARIABLE(aKeyType);
|
||||
}
|
||||
|
||||
void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
|
||||
{
|
||||
SuccessOrDie(psRadioSpinel->SetMacFrameCounter(aMacFrameCounter, false));
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
}
|
||||
|
||||
void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)
|
||||
{
|
||||
SuccessOrDie(psRadioSpinel->SetMacFrameCounter(aMacFrameCounter, true));
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
}
|
||||
|
||||
uint64_t otPlatRadioGetNow(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetNow();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
return psRadioSpinel->GetCslAccuracy();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
return psRadioSpinel->GetCslUncertainty();
|
||||
}
|
||||
#endif
|
||||
|
||||
otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel,
|
||||
int8_t aMaxPower)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->SetChannelMaxTransmitPower(aChannel, aMaxPower);
|
||||
}
|
||||
|
||||
otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->SetRadioRegion(aRegionCode);
|
||||
}
|
||||
|
||||
otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
return psRadioSpinel->GetRadioRegion(aRegionCode);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
|
||||
otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics,
|
||||
const otShortAddress aShortAddress,
|
||||
const otExtAddress *aExtAddress)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
return psRadioSpinel->ConfigureEnhAckProbing(aLinkMetrics, aShortAddress, *aExtAddress);
|
||||
}
|
||||
#endif
|
||||
|
||||
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart,
|
||||
uint32_t aDuration)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
OT_UNUSED_VARIABLE(aChannel);
|
||||
OT_UNUSED_VARIABLE(aStart);
|
||||
OT_UNUSED_VARIABLE(aDuration);
|
||||
return OT_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" void platformRadioInit(void)
|
||||
{
|
||||
spinel_iid_t iidList[ot::Spinel::kSpinelHeaderMaxNumIid];
|
||||
struct ot::Spinel::RadioSpinelCallbacks callbacks;
|
||||
|
||||
iidList[0] = 0;
|
||||
|
||||
psRadioSpinel = new ot::Spinel::RadioSpinel();
|
||||
psSpinelDriver = new ot::Spinel::SpinelDriver();
|
||||
|
||||
psRadioUrl = new ot::Url::Url();
|
||||
pSpinelInterface = new ot::Hdlc::HdlcInterface(*psRadioUrl);
|
||||
|
||||
OT_UNUSED_VARIABLE(psSpinelDriver->Init(*pSpinelInterface, true /* aSoftwareReset */,
|
||||
iidList, OT_ARRAY_LENGTH(iidList)));
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone;
|
||||
callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone;
|
||||
#endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */
|
||||
callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone;
|
||||
callbacks.mReceiveDone = otPlatRadioReceiveDone;
|
||||
callbacks.mTransmitDone = otPlatRadioTxDone;
|
||||
callbacks.mTxStarted = otPlatRadioTxStarted;
|
||||
|
||||
psRadioSpinel->SetCallbacks(callbacks);
|
||||
|
||||
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && \
|
||||
OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
|
||||
bool aEnableRcpTimeSync = true;
|
||||
#else
|
||||
bool aEnableRcpTimeSync = false;
|
||||
#endif
|
||||
psRadioSpinel->Init(false /*aSkipRcpCompatibilityCheck*/, true /*aSoftwareReset*/,
|
||||
psSpinelDriver, sRequiredRadioCaps, aEnableRcpTimeSync);
|
||||
psRadioSpinel->SetTimeSyncState(true);
|
||||
}
|
||||
|
||||
extern "C" void platformRadioDeinit(void)
|
||||
{
|
||||
psRadioSpinel->Deinit();
|
||||
psSpinelDriver->Deinit();
|
||||
}
|
||||
|
||||
extern "C" int notify_new_rx_frame(struct net_pkt *pkt)
|
||||
{
|
||||
/* The RX frame is handled by Openthread stack */
|
||||
net_pkt_unref(pkt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int notify_new_tx_frame(struct net_pkt *pkt)
|
||||
{
|
||||
k_fifo_put(&tx_pkt_fifo, pkt);
|
||||
set_pending_event(PENDING_EVENT_FRAME_TO_SEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void platformRadioProcess(otInstance *aInstance)
|
||||
{
|
||||
if (is_pending_event_set(PENDING_EVENT_FRAME_TO_SEND)) {
|
||||
struct net_pkt *evt_pkt;
|
||||
|
||||
reset_pending_event(PENDING_EVENT_FRAME_TO_SEND);
|
||||
while ((evt_pkt = (struct net_pkt *)k_fifo_get(&tx_pkt_fifo, K_NO_WAIT)) != NULL) {
|
||||
openthread_handle_frame_to_send(aInstance, evt_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
psSpinelDriver->Process(aInstance);
|
||||
psRadioSpinel->Process(aInstance);
|
||||
}
|
||||
Loading…
Reference in a new issue