blob: de1be8dee072f97ed0d8f8671434c0625136d0a4 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 contains definitions for a UART based NCP interface to the OpenThread stack.
*/
#include <openthread/config.h>
#include "ncp_uart.hpp"
#include <stdio.h>
#include <openthread/ncp.h>
#include <openthread/platform/logging.h>
#include <openthread/platform/uart.h>
#include <openthread/platform/misc.h>
#include "openthread-core-config.h"
#include "openthread-instance.h"
#include "common/code_utils.hpp"
#include "common/new.hpp"
#include "common/debug.hpp"
#include "net/ip6.hpp"
#if OPENTHREAD_ENABLE_NCP_UART
namespace ot {
static otDEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpUart), uint64_t);
extern "C" void otNcpInit(otInstance *aInstance)
{
NcpUart *ncpUart = NULL;
ncpUart = new(&sNcpRaw) NcpUart(aInstance);
if (ncpUart == NULL || ncpUart != NcpBase::GetNcpInstance())
{
assert(false);
}
}
NcpUart::UartTxBuffer::UartTxBuffer(void)
: Hdlc::Encoder::BufferWriteIterator()
{
Clear();
}
void NcpUart::UartTxBuffer::Clear(void)
{
mWritePointer = mBuffer;
mRemainingLength = sizeof(mBuffer);
}
bool NcpUart::UartTxBuffer::IsEmpty(void) const
{
return mWritePointer == mBuffer;
}
uint16_t NcpUart::UartTxBuffer::GetLength(void) const
{
return static_cast<uint16_t>(mWritePointer - mBuffer);
}
const uint8_t *NcpUart::UartTxBuffer::GetBuffer(void) const
{
return mBuffer;
}
NcpUart::NcpUart(otInstance *aInstance):
NcpBase(aInstance),
mFrameDecoder(mRxBuffer, sizeof(mRxBuffer), &NcpUart::HandleFrame, &NcpUart::HandleError, this),
mUartBuffer(),
mState(kStartingFrame),
mByte(0),
mUartSendTask(aInstance->mIp6.mTaskletScheduler, EncodeAndSendToUart, this)
{
mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this);
otPlatUartEnable();
}
void NcpUart::HandleFrameAddedToNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aTag,
NcpFrameBuffer *aNcpFrameBuffer)
{
OT_UNUSED_VARIABLE(aNcpFrameBuffer);
OT_UNUSED_VARIABLE(aTag);
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();
}
// This method encodes a frame from the tx frame buffer (mTxFrameBuffer) into the uart buffer and sends it over uart.
// If the uart buffer gets full, it sends the current encoded portion. This method remembers current state, so on
// sub-sequent calls, it restarts encoding the bytes from where it left of in the frame .
void NcpUart::EncodeAndSendToUart(void)
{
uint16_t len;
while (!mTxFrameBuffer.IsEmpty())
{
switch (mState)
{
case kStartingFrame:
if (super_t::ShouldWakeHost())
{
otPlatWakeHost();
}
VerifyOrExit(super_t::ShouldDeferHostSend() == false);
SuccessOrExit(mFrameEncoder.Init(mUartBuffer));
mTxFrameBuffer.OutFrameBegin();
mState = kEncodingFrame;
while (!mTxFrameBuffer.OutFrameHasEnded())
{
mByte = mTxFrameBuffer.OutFrameReadByte();
case kEncodingFrame:
SuccessOrExit(mFrameEncoder.Encode(mByte, mUartBuffer));
}
mTxFrameBuffer.OutFrameRemove();
mState = kFinalizingFrame;
// fall through
case kFinalizingFrame:
SuccessOrExit(mFrameEncoder.Finalize(mUartBuffer));
mState = kStartingFrame;
}
}
exit:
len = mUartBuffer.GetLength();
if (len > 0)
{
if (otPlatUartSend(mUartBuffer.GetBuffer(), len) != OT_ERROR_NONE)
{
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, uint8_t *aBuf, uint16_t aBufLength)
{
static_cast<NcpUart *>(aContext)->HandleFrame(aBuf, aBufLength);
}
void NcpUart::HandleFrame(uint8_t *aBuf, uint16_t aBufLength)
{
super_t::HandleReceive(aBuf, aBufLength);
}
void NcpUart::HandleError(void *aContext, otError aError, uint8_t *aBuf, uint16_t aBufLength)
{
static_cast<NcpUart *>(aContext)->HandleError(aError, aBuf, aBufLength);
}
void NcpUart::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength)
{
char hexbuf[128];
uint16_t i = 0;
super_t::IncrementFrameErrorCounter();
// We can get away with sprintf because we know
// `hexbuf` is large enough.
snprintf(hexbuf, sizeof(hexbuf), "Framing error %d: [", aError);
// Write out the first part of our log message.
otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf), static_cast<int>(strlen(hexbuf)));
// The first '3' comes from the trailing "]\n\000" at the end o the string.
// The second '3' comes from the length of two hex digits and a space.
for (i = 0; (i < aBufLength) && (i < (sizeof(hexbuf) - 3) / 3); i++)
{
// We can get away with sprintf because we know
// `hexbuf` is large enough, based on our calculations
// above.
snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, " %02X", static_cast<uint8_t>(aBuf[i]));
}
// Append a final closing bracket and newline character
// so our log line looks nice.
snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, "]\n");
// Write out the second part of our log message.
// We skip the first byte since it has a space in it.
otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(hexbuf + 1), static_cast<int>(strlen(hexbuf) - 1));
}
#if OPENTHREAD_CONFIG_ENABLE_DEFAULT_LOG_OUTPUT
#ifdef __cplusplus
extern "C" {
#endif
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
char logString[128];
int charsWritten;
va_list args;
va_start(args, aFormat);
if ((charsWritten = vsnprintf(logString, sizeof(logString), aFormat, args)) > 0)
{
if (charsWritten > static_cast<int>(sizeof(logString) - 1))
{
charsWritten = static_cast<int>(sizeof(logString) - 1);
}
otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(logString), charsWritten);
}
va_end(args);
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
}
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_ENABLE_CLI_LOGGING
} // namespace ot
#endif // OPENTHREAD_ENABLE_NCP_UART