mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
ffbe65c6dd
* Add otInstance type declaration and update ot APIs to take it as input.
359 lines
10 KiB
C++
359 lines
10 KiB
C++
/*
|
|
* Copyright (c) 2016, Nest Labs, Inc.
|
|
* 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 a SPI interface to the OpenThread stack.
|
|
*/
|
|
|
|
#include <common/code_utils.hpp>
|
|
#include <common/new.hpp>
|
|
#include <net/ip6.hpp>
|
|
#include <ncp/ncp.h>
|
|
#include <ncp/ncp_spi.hpp>
|
|
#include <platform/spi-slave.h>
|
|
#include <core/openthread-core-config.h>
|
|
|
|
#define SPI_RESET_FLAG 0x80
|
|
#define SPI_CRC_FLAG 0x40
|
|
#define SPI_PATTERN_VALUE 0x02
|
|
#define SPI_PATTERN_MASK 0x03
|
|
|
|
namespace Thread {
|
|
|
|
static otDEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpSpi), uint64_t);
|
|
static NcpSpi *sNcpSpi;
|
|
|
|
extern Ip6::Ip6 *sIp6;
|
|
|
|
extern "C" void otNcpInit(otInstance *aInstance)
|
|
{
|
|
sNcpSpi = new(&sNcpRaw) NcpSpi(aInstance);
|
|
}
|
|
|
|
static void spi_header_set_flag_byte(uint8_t *header, uint8_t value)
|
|
{
|
|
header[0] = value;
|
|
}
|
|
|
|
static void spi_header_set_accept_len(uint8_t *header, uint16_t len)
|
|
{
|
|
header[1] = ((len >> 0) & 0xFF);
|
|
header[2] = ((len >> 8) & 0xFF);
|
|
}
|
|
|
|
static void spi_header_set_data_len(uint8_t *header, uint16_t len)
|
|
{
|
|
header[3] = ((len >> 0) & 0xFF);
|
|
header[4] = ((len >> 8) & 0xFF);
|
|
}
|
|
|
|
static uint8_t spi_header_get_flag_byte(const uint8_t *header)
|
|
{
|
|
return header[0];
|
|
}
|
|
|
|
static uint16_t spi_header_get_accept_len(const uint8_t *header)
|
|
{
|
|
return ( header[1] + static_cast<uint16_t>(header[2] << 8) );
|
|
}
|
|
|
|
static uint16_t spi_header_get_data_len(const uint8_t *header)
|
|
{
|
|
return ( header[3] + static_cast<uint16_t>(header[4] << 8) );
|
|
}
|
|
|
|
NcpSpi::NcpSpi(otInstance *aInstance):
|
|
NcpBase(aInstance),
|
|
mHandleRxFrame(sIp6->mTaskletScheduler, &NcpSpi::HandleRxFrame, this),
|
|
mHandleSendDone(sIp6->mTaskletScheduler, &NcpSpi::PrepareTxFrame, this),
|
|
mTxFrameBuffer(aInstance, mTxBuffer, sizeof(mTxBuffer))
|
|
{
|
|
memset(mEmptySendFrame, 0, kSpiHeaderLength);
|
|
memset(mSendFrame, 0, kSpiHeaderLength);
|
|
|
|
mSending = false;
|
|
mHandlingSendDone = false;
|
|
mHandlingRxFrame = false;
|
|
|
|
mTxFrameBuffer.SetCallbacks(NULL, TxFrameBufferHasData, this);
|
|
|
|
spi_header_set_flag_byte(mSendFrame, SPI_RESET_FLAG|SPI_PATTERN_VALUE);
|
|
spi_header_set_flag_byte(mEmptySendFrame, SPI_RESET_FLAG|SPI_PATTERN_VALUE);
|
|
spi_header_set_accept_len(mSendFrame, sizeof(mReceiveFrame) - kSpiHeaderLength);
|
|
otPlatSpiSlaveEnable(&NcpSpi::SpiTransactionComplete, (void*)this);
|
|
|
|
// We signal an interrupt on this first transaction to
|
|
// make sure that the host processor knows that our
|
|
// reset flag was set.
|
|
otPlatSpiSlavePrepareTransaction(
|
|
mEmptySendFrame,
|
|
kSpiHeaderLength,
|
|
mEmptyReceiveFrame,
|
|
kSpiHeaderLength,
|
|
true
|
|
);
|
|
}
|
|
|
|
void
|
|
NcpSpi::SpiTransactionComplete(
|
|
void *aContext,
|
|
uint8_t *anOutputBuf,
|
|
uint16_t anOutputBufLen,
|
|
uint8_t *anInputBuf,
|
|
uint16_t anInputBufLen,
|
|
uint16_t aTransactionLength
|
|
)
|
|
{
|
|
static_cast<NcpSpi*>(aContext)->SpiTransactionComplete(
|
|
anOutputBuf,
|
|
anOutputBufLen,
|
|
anInputBuf,
|
|
anInputBufLen,
|
|
aTransactionLength
|
|
);
|
|
}
|
|
|
|
void
|
|
NcpSpi::SpiTransactionComplete(
|
|
uint8_t *aMISOBuf,
|
|
uint16_t aMISOBufLen,
|
|
uint8_t *aMOSIBuf,
|
|
uint16_t aMOSIBufLen,
|
|
uint16_t aTransactionLength
|
|
) {
|
|
// This may be executed from an interrupt context.
|
|
// Must return as quickly as possible.
|
|
|
|
uint16_t rx_data_len(0);
|
|
uint16_t rx_accept_len(0);
|
|
uint16_t tx_data_len(0);
|
|
uint16_t tx_accept_len(0);
|
|
|
|
// TODO: Check `PATTERN` bits of `HDR` and ignore frame if not set.
|
|
// Holding off on implementing this so as to not cause immediate
|
|
// compatibility problems, even though it is required by the spec.
|
|
|
|
if (aTransactionLength >= kSpiHeaderLength)
|
|
{
|
|
if (aMISOBufLen >= kSpiHeaderLength)
|
|
{
|
|
rx_accept_len = spi_header_get_accept_len(aMISOBuf);
|
|
tx_data_len = spi_header_get_data_len(aMISOBuf);
|
|
(void)spi_header_get_flag_byte(aMISOBuf);
|
|
}
|
|
|
|
if (aMOSIBufLen >= kSpiHeaderLength)
|
|
{
|
|
rx_data_len = spi_header_get_data_len(aMOSIBuf);
|
|
tx_accept_len = spi_header_get_accept_len(aMOSIBuf);
|
|
}
|
|
|
|
if ( !mHandlingRxFrame
|
|
&& (rx_data_len > 0)
|
|
&& (rx_data_len <= (aTransactionLength - kSpiHeaderLength))
|
|
&& (rx_data_len <= rx_accept_len)
|
|
) {
|
|
mHandlingRxFrame = true;
|
|
mHandleRxFrameTask.Post();
|
|
}
|
|
|
|
if ( mSending
|
|
&& !mHandlingSendDone
|
|
&& (tx_data_len > 0)
|
|
&& (tx_data_len <= (aTransactionLength - kSpiHeaderLength))
|
|
&& (tx_data_len <= tx_accept_len)
|
|
) {
|
|
// Our transmission was successful.
|
|
mHandlingSendDone = true;
|
|
mPrepareTxFrameTask.Post();
|
|
}
|
|
}
|
|
|
|
if ( (aTransactionLength >= 1)
|
|
&& (aMISOBufLen >= 1)
|
|
) {
|
|
// Clear the reset flag.
|
|
spi_header_set_flag_byte(mSendFrame, SPI_PATTERN_VALUE);
|
|
spi_header_set_flag_byte(mEmptySendFrame, SPI_PATTERN_VALUE);
|
|
}
|
|
|
|
if (mSending && !mHandlingSendDone)
|
|
{
|
|
aMISOBuf = mSendFrame;
|
|
aMISOBufLen = mSendFrameLen;
|
|
}
|
|
else
|
|
{
|
|
aMISOBuf = mEmptySendFrame;
|
|
aMISOBufLen = kSpiHeaderLength;
|
|
}
|
|
|
|
if (mHandlingRxFrame)
|
|
{
|
|
aMOSIBuf = mEmptyReceiveFrame;
|
|
aMOSIBufLen = kSpiHeaderLength;
|
|
spi_header_set_accept_len(aMISOBuf, 0);
|
|
}
|
|
else
|
|
{
|
|
aMOSIBuf = mReceiveFrame;
|
|
aMOSIBufLen = sizeof(mReceiveFrame);
|
|
spi_header_set_accept_len(aMISOBuf, sizeof(mReceiveFrame) - kSpiHeaderLength);
|
|
}
|
|
|
|
otPlatSpiSlavePrepareTransaction(
|
|
aMISOBuf,
|
|
aMISOBufLen,
|
|
aMOSIBuf,
|
|
aMOSIBufLen,
|
|
mSending && !mHandlingSendDone
|
|
);
|
|
}
|
|
|
|
ThreadError NcpSpi::OutboundFrameBegin(void)
|
|
{
|
|
return mTxFrameBuffer.InFrameBegin();
|
|
}
|
|
|
|
ThreadError NcpSpi::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
|
|
{
|
|
return mTxFrameBuffer.InFrameFeedData(aDataBuffer, aDataBufferLength);
|
|
}
|
|
|
|
ThreadError NcpSpi::OutboundFrameFeedMessage(Message &aMessage)
|
|
{
|
|
return mTxFrameBuffer.InFrameFeedMessage(aMessage);
|
|
}
|
|
|
|
ThreadError NcpSpi::OutboundFrameEnd(void)
|
|
{
|
|
return mTxFrameBuffer.InFrameEnd();
|
|
}
|
|
|
|
void NcpSpi::TxFrameBufferHasData(void *aContext, NcpFrameBuffer *aNcpFrameBuffer)
|
|
{
|
|
(void)aContext;
|
|
(void)aNcpFrameBuffer;
|
|
|
|
sNcpSpi->TxFrameBufferHasData();
|
|
}
|
|
|
|
void NcpSpi::TxFrameBufferHasData(void)
|
|
{
|
|
mPrepareTxFrameTask.Post();
|
|
}
|
|
|
|
ThreadError NcpSpi::PrepareNextSpiSendFrame(void)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t frameLength;
|
|
uint16_t readLength;
|
|
|
|
VerifyOrExit(!mTxFrameBuffer.IsEmpty(), ;);
|
|
|
|
SuccessOrExit(errorCode = mTxFrameBuffer.OutFrameBegin());
|
|
|
|
frameLength = mTxFrameBuffer.OutFrameGetLength();
|
|
VerifyOrExit(frameLength <= sizeof(mSendFrame) - kSpiHeaderLength, errorCode = kThreadError_NoBufs);
|
|
|
|
spi_header_set_data_len(mSendFrame, frameLength);
|
|
|
|
// Half-duplex to avoid race condition.
|
|
spi_header_set_accept_len(mSendFrame, 0);
|
|
|
|
readLength = mTxFrameBuffer.OutFrameRead(frameLength, mSendFrame + kSpiHeaderLength);
|
|
VerifyOrExit(readLength == frameLength, errorCode = kThreadError_Failed);
|
|
|
|
mSendFrameLen = frameLength + kSpiHeaderLength;
|
|
|
|
mSending = true;
|
|
|
|
errorCode = otPlatSpiSlavePrepareTransaction(
|
|
mSendFrame,
|
|
mSendFrameLen,
|
|
mEmptyReceiveFrame,
|
|
sizeof(mEmptyReceiveFrame),
|
|
true
|
|
);
|
|
|
|
if (errorCode == kThreadError_Busy)
|
|
{
|
|
// Being busy is OK. We will get the transaction
|
|
// set up properly when the current transaction
|
|
// is completed.
|
|
errorCode = kThreadError_None;
|
|
}
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
mSending = false;
|
|
}
|
|
|
|
// Remove the frame from tx buffer and inform the base
|
|
// class that space is now available for a new frame.
|
|
mTxFrameBuffer.OutFrameRemove();
|
|
super_t::HandleSpaceAvailableInTxBuffer();
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
void NcpSpi::PrepareTxFrame(void *aContext)
|
|
{
|
|
static_cast<NcpSpi*>(aContext)->PrepareTxFrame();
|
|
}
|
|
|
|
void NcpSpi::PrepareTxFrame(void)
|
|
{
|
|
if (mHandlingSendDone)
|
|
{
|
|
mSending = false;
|
|
PrepareNextSpiSendFrame();
|
|
mHandlingSendDone = false;
|
|
}
|
|
else if (!mSending)
|
|
{
|
|
PrepareNextSpiSendFrame();
|
|
}
|
|
}
|
|
|
|
void NcpSpi::HandleRxFrame(void *aContext)
|
|
{
|
|
static_cast<NcpSpi*>(aContext)->HandleRxFrame();
|
|
}
|
|
|
|
void NcpSpi::HandleRxFrame(void)
|
|
{
|
|
uint16_t rx_data_len( spi_header_get_data_len(mReceiveFrame) );
|
|
super_t::HandleReceive(mReceiveFrame + kSpiHeaderLength, rx_data_len);
|
|
mHandlingRxFrame = false;
|
|
}
|
|
|
|
} // namespace Thread
|
|
|