#include "ncp_uart.hpp"
#include <stdio.h>
#include <openthread/ncp.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/misc.h>
#include <openthread/platform/uart.h>
#include "openthread-core-config.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/new.hpp"
#include "net/ip6.hpp"
#include "utils/static_assert.hpp"
#if OPENTHREAD_CONFIG_NCP_UART_ENABLE
#if OPENTHREAD_CONFIG_DIAG_ENABLE
OT_STATIC_ASSERT(OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE <= OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE -
ot::Ncp::NcpBase::kSpinelCmdHeaderSize -
ot::Ncp::NcpBase::kSpinelPropIdSize,
"diag output should be smaller than NCP UART rx buffer");
OT_STATIC_ASSERT(OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE <= OPENTHREAD_CONFIG_NCP_UART_RX_BUFFER_SIZE,
"diag command line should be smaller than NCP UART rx buffer");
#endif
namespace ot {
namespace Ncp {
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0
static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpUart), uint64_t);
extern "C" void otNcpInit(otInstance *aInstance)
{
NcpUart * ncpUart = NULL;
Instance *instance = static_cast<Instance *>(aInstance);
ncpUart = new (&sNcpRaw) NcpUart(instance);
if (ncpUart == NULL || ncpUart != NcpBase::GetNcpInstance())
{
OT_ASSERT(false);
}
}
#endif
NcpUart::NcpUart(Instance *aInstance)
: NcpBase(aInstance)
, mFrameEncoder(mUartBuffer)
, mFrameDecoder(mRxBuffer, &NcpUart::HandleFrame, this)
, mUartBuffer()
, mState(kStartingFrame)
, mByte(0)
, mRxBuffer()
, mUartSendImmediate(false)
, mUartSendTask(*aInstance, EncodeAndSendToUart, this)
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
, mTxFrameBufferEncrypterReader(mTxFrameBuffer)
#endif {
mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
IgnoreError(otPlatUartEnable());
}
void NcpUart::HandleFrameAddedToNcpBuffer(void * aContext,
Spinel::Buffer::FrameTag aTag,
Spinel::Buffer::Priority aPriority,
Spinel::Buffer * aBuffer)
{
OT_UNUSED_VARIABLE(aBuffer);
OT_UNUSED_VARIABLE(aTag);
OT_UNUSED_VARIABLE(aPriority);
static_cast<NcpUart *>(aContext)->HandleFrameAddedToNcpBuffer();
}
void NcpUart::HandleFrameAddedToNcpBuffer(void)
{
if (mUartBuffer.IsEmpty())
{
mUartSendTask.Post();
}
}
void NcpUart::EncodeAndSendToUart(Tasklet &aTasklet)
{
OT_UNUSED_VARIABLE(aTasklet);
static_cast<NcpUart *>(GetNcpInstance())->EncodeAndSendToUart();
}
void NcpUart::EncodeAndSendToUart(void)
{
uint16_t len;
bool prevHostPowerState;
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
Spinel::BufferEncrypterReader &txFrameBuffer = mTxFrameBufferEncrypterReader;
#else
Spinel::Buffer &txFrameBuffer = mTxFrameBuffer;
#endif
while (!txFrameBuffer.IsEmpty() || (mState == kFinalizingFrame))
{
switch (mState)
{
case kStartingFrame:
if (super_t::ShouldWakeHost())
{
otPlatWakeHost();
}
VerifyOrExit(!super_t::ShouldDeferHostSend(), OT_NOOP);
SuccessOrExit(mFrameEncoder.BeginFrame());
IgnoreError(txFrameBuffer.OutFrameBegin());
mState = kEncodingFrame;
while (!txFrameBuffer.OutFrameHasEnded())
{
mByte = txFrameBuffer.OutFrameReadByte();
case kEncodingFrame:
SuccessOrExit(mFrameEncoder.Encode(mByte));
}
prevHostPowerState = mHostPowerStateInProgress;
IgnoreError(txFrameBuffer.OutFrameRemove());
if (prevHostPowerState && !mHostPowerStateInProgress)
{
mUartSendImmediate = true;
}
mState = kFinalizingFrame;
case kFinalizingFrame:
SuccessOrExit(mFrameEncoder.EndFrame());
mState = kStartingFrame;
if (mUartSendImmediate)
{
mUartSendImmediate = false;
break;
}
}
}
exit:
len = mUartBuffer.GetLength();
if (len > 0)
{
if (otPlatUartSend(mUartBuffer.GetFrame(), len) != OT_ERROR_NONE)
{
OT_ASSERT(false);
}
}
}
extern "C" void otPlatUartSendDone(void)
{
NcpUart *ncpUart = static_cast<NcpUart *>(NcpBase::GetNcpInstance());
if (ncpUart != NULL)
{
ncpUart->HandleUartSendDone();
}
}
void NcpUart::HandleUartSendDone(void)
{
mUartBuffer.Clear();
mUartSendTask.Post();
}
extern "C" void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength)
{
NcpUart *ncpUart = static_cast<NcpUart *>(NcpBase::GetNcpInstance());
if (ncpUart != NULL)
{
ncpUart->HandleUartReceiveDone(aBuf, aBufLength);
}
}
void NcpUart::HandleUartReceiveDone(const uint8_t *aBuf, uint16_t aBufLength)
{
mFrameDecoder.Decode(aBuf, aBufLength);
}
void NcpUart::HandleFrame(void *aContext, otError aError)
{
static_cast<NcpUart *>(aContext)->HandleFrame(aError);
}
void NcpUart::HandleFrame(otError aError)
{
uint8_t *buf = mRxBuffer.GetFrame();
uint16_t bufLength = mRxBuffer.GetLength();
if (aError == OT_ERROR_NONE)
{
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
size_t dataLen = bufLength;
if (SpinelEncrypter::DecryptInbound(buf, kRxBufferSize, &dataLen))
{
super_t::HandleReceive(buf, dataLen);
}
#else
super_t::HandleReceive(buf, bufLength);
#endif }
else
{
HandleError(aError, buf, bufLength);
}
mRxBuffer.Clear();
}
void NcpUart::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength)
{
char hexbuf[128];
uint16_t i = 0;
super_t::IncrementFrameErrorCounter();
snprintf(hexbuf, sizeof(hexbuf), "Framing error %d: [", aError);
IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf), static_cast<int>(strlen(hexbuf))));
for (i = 0; (i < aBufLength) && (i < (sizeof(hexbuf) - 3) / 3); i++)
{
snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, " %02X", static_cast<uint8_t>(aBuf[i]));
}
snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, "]\n");
IgnoreError(otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf + 1), static_cast<int>(strlen(hexbuf) - 1)));
}
#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER
NcpUart::Spinel::BufferEncrypterReader::SpinelBufferEncrypterReader(Spinel::Buffer &aTxFrameBuffer)
: mTxFrameBuffer(aTxFrameBuffer)
, mDataBufferReadIndex(0)
, mOutputDataLength(0)
{
}
bool NcpUart::Spinel::BufferEncrypterReader::IsEmpty(void) const
{
return mTxFrameBuffer.IsEmpty() && !mOutputDataLength;
}
otError NcpUart::Spinel::BufferEncrypterReader::OutFrameBegin(void)
{
otError status = OT_ERROR_FAILED;
Reset();
if ((status = mTxFrameBuffer.OutFrameBegin()) == OT_ERROR_NONE)
{
mOutputDataLength = mTxFrameBuffer.OutFrameGetLength();
if (mOutputDataLength > 0)
{
OT_ASSERT(mOutputDataLength <= sizeof(mDataBuffer));
mTxFrameBuffer.OutFrameRead(mOutputDataLength, mDataBuffer);
if (!SpinelEncrypter::EncryptOutbound(mDataBuffer, sizeof(mDataBuffer), &mOutputDataLength))
{
mOutputDataLength = 0;
status = OT_ERROR_FAILED;
}
}
else
{
status = OT_ERROR_FAILED;
}
}
return status;
}
bool NcpUart::Spinel::BufferEncrypterReader::OutFrameHasEnded(void)
{
return (mDataBufferReadIndex >= mOutputDataLength);
}
uint8_t NcpUart::Spinel::BufferEncrypterReader::OutFrameReadByte(void)
{
return mDataBuffer[mDataBufferReadIndex++];
}
otError NcpUart::Spinel::BufferEncrypterReader::OutFrameRemove(void)
{
return mTxFrameBuffer.OutFrameRemove();
}
void NcpUart::Spinel::BufferEncrypterReader::Reset(void)
{
mOutputDataLength = 0;
mDataBufferReadIndex = 0;
}
#endif
} }
#endif