[posix-host] add SPI interface support (#4431)

This commit is contained in:
Zhanglong Xia
2020-01-21 19:24:42 -08:00
committed by Jonathan Hui
parent ef4530a187
commit f0c063f8e9
17 changed files with 1273 additions and 186 deletions
+4
View File
@@ -69,6 +69,10 @@ jobs:
os: linux
compiler: clang
script: .travis/script.sh
- env: BUILD_TARGET="posix-app-spi" VERBOSE=1 COVERAGE=1
os: linux
compiler: gcc
script: .travis/script.sh
- env: BUILD_TARGET="android-build" VERBOSE=1
os: linux
dist: trusty
+5
View File
@@ -626,6 +626,11 @@ build_samr21() {
REFERENCE_DEVICE=1 COVERAGE=1 PYTHONUNBUFFERED=1 OT_NCP_PATH="$(pwd)/$(ls output/posix/*/bin/ot-ncp)" RADIO_DEVICE="$(pwd)/$(ls output/*/bin/ot-rcp)" NODE_TYPE=ncp-sim make -f src/posix/Makefile-posix check || die
}
[ $BUILD_TARGET != posix-app-spi ] || {
./bootstrap || die
REFERENCE_DEVICE=1 READLINE=readline RCP_SPI=1 make -f src/posix/Makefile-posix || die
}
[ $BUILD_TARGET != posix-ncp ] || {
./bootstrap || die
REFERENCE_DEVICE=1 COVERAGE=1 PYTHONUNBUFFERED=1 NODE_TYPE=ncp-sim make -f examples/Makefile-posix check || die
+4
View File
@@ -99,6 +99,7 @@ LOCAL_CFLAGS := \
-DOPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE=1 \
-DOPENTHREAD_FTD=1 \
-DOPENTHREAD_POSIX=1 \
-DOPENTHREAD_POSIX_RCP_UART_ENABLE=1 \
-DSPINEL_PLATFORM_HEADER=\"spinel_platform.h\" \
$(OPENTHREAD_PROJECT_CFLAGS) \
$(NULL)
@@ -242,6 +243,7 @@ LOCAL_SRC_FILES := \
src/posix/platform/misc.c \
src/posix/platform/radio_spinel.cpp \
src/posix/platform/settings.cpp \
src/posix/platform/spi_interface.cpp \
src/posix/platform/system.c \
src/posix/platform/uart.c \
third_party/mbedtls/repo/library/md.c \
@@ -299,6 +301,7 @@ LOCAL_CFLAGS := \
-DOPENTHREAD_FTD=1 \
-DOPENTHREAD_POSIX=1 \
-DOPENTHREAD_POSIX_APP_TYPE=2 \
-DOPENTHREAD_POSIX_RCP_UART_ENABLE=1 \
-DSPINEL_PLATFORM_HEADER=\"spinel_platform.h\" \
$(OPENTHREAD_PROJECT_CFLAGS) \
$(NULL)
@@ -350,6 +353,7 @@ LOCAL_CFLAGS := \
-DOPENTHREAD_FTD=1 \
-DOPENTHREAD_POSIX=1 \
-DOPENTHREAD_POSIX_APP_TYPE=1 \
-DOPENTHREAD_POSIX_RCP_UART_ENABLE=1 \
-DSPINEL_PLATFORM_HEADER=\"spinel_platform.h\" \
$(OPENTHREAD_PROJECT_CFLAGS) \
$(NULL)
+16
View File
@@ -163,6 +163,14 @@ public:
*/
bool IsValid(void) const { return ((mBuffer[kIndexFlagByte] & kFlagPatternMask) == kFlagPattern); }
/**
* This method indicates whether or not the "RST" bit is set.
*
* @returns TRUE if the "RST" bit is set, FALSE otherwise.
*
*/
bool IsResetFlagSet(void) const { return ((mBuffer[kIndexFlagByte] & kFlagReset) == kFlagReset); }
/**
* This method sets the "flag byte" field in the SPI frame header.
*
@@ -171,6 +179,14 @@ public:
*/
void SetHeaderFlagByte(bool aResetFlag) { mBuffer[kIndexFlagByte] = kFlagPattern | (aResetFlag ? kFlagReset : 0); }
/**
* This method gets the "flag byte" field in the SPI frame header.
*
* @returns The flag byte.
*
*/
uint8_t GetHeaderFlagByte(void) const { return mBuffer[kIndexFlagByte]; }
/**
* This method sets the "accept len" field in the SPI frame header.
*
+6
View File
@@ -102,6 +102,12 @@ ifneq ($(READLINE),)
configure_OPTIONS += --with-readline=$(READLINE)
endif
ifeq ($(RCP_SPI),1)
COMMONCFLAGS += -DOPENTHREAD_POSIX_RCP_SPI_ENABLE=1
else
COMMONCFLAGS += -DOPENTHREAD_POSIX_RCP_UART_ENABLE=1
endif
ifeq ($(VIRTUAL_TIME),1)
COMMONCFLAGS += -DOPENTHREAD_POSIX_VIRTUAL_TIME=1
endif
+117 -29
View File
@@ -90,15 +90,48 @@ static jmp_buf gResetJump;
void __gcov_flush();
/**
* This enumeration defines the argument return values.
*
*/
enum
{
ARG_PRINT_RADIO_VERSION = 1001,
ARG_NO_RADIO_RESET = 1002,
ARG_RESTORE_NCP_DATASET = 1003,
ARG_SPI_GPIO_INT_DEV = 1011,
ARG_SPI_GPIO_INT_LINE = 1012,
ARG_SPI_GPIO_RESET_DEV = 1013,
ARG_SPI_GPIO_RESET_LINE = 1014,
ARG_SPI_MODE = 1015,
ARG_SPI_SPEED = 1016,
ARG_SPI_CS_DELAY = 1017,
ARG_SPI_RESET_DELAY = 1018,
ARG_SPI_ALIGN_ALLOWANCE = 1019,
ARG_SPI_SMALL_PACKET = 1020,
};
static const struct option kOptions[] = {{"debug-level", required_argument, NULL, 'd'},
{"dry-run", no_argument, NULL, 'n'},
{"help", no_argument, NULL, 'h'},
{"interface-name", required_argument, NULL, 'I'},
{"no-reset", no_argument, NULL, 0},
{"radio-version", no_argument, NULL, 0},
{"no-reset", no_argument, NULL, ARG_NO_RADIO_RESET},
{"radio-version", no_argument, NULL, ARG_PRINT_RADIO_VERSION},
{"ncp-dataset", no_argument, NULL, ARG_RESTORE_NCP_DATASET},
{"time-speed", required_argument, NULL, 's'},
{"verbose", no_argument, NULL, 'v'},
{"ncp-dataset", no_argument, NULL, 0},
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
{"gpio-int-dev", required_argument, NULL, ARG_SPI_GPIO_INT_DEV},
{"gpio-int-line", required_argument, NULL, ARG_SPI_GPIO_INT_LINE},
{"gpio-reset-dev", required_argument, NULL, ARG_SPI_GPIO_RESET_DEV},
{"gpio-reset-line", required_argument, NULL, ARG_SPI_GPIO_RESET_LINE},
{"spi-mode", required_argument, NULL, ARG_SPI_MODE},
{"spi-speed", required_argument, NULL, ARG_SPI_SPEED},
{"spi-cs-delay", required_argument, NULL, ARG_SPI_CS_DELAY},
{"spi-reset-delay", required_argument, NULL, ARG_SPI_RESET_DELAY},
{"spi-align-allowance", required_argument, NULL, ARG_SPI_ALIGN_ALLOWANCE},
{"spi-small-packet", required_argument, NULL, ARG_SPI_SMALL_PACKET},
#endif
{0, 0, 0, 0}};
static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
@@ -107,15 +140,38 @@ static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
"Syntax:\n"
" %s [Options] NodeId|Device|Command [DeviceConfig|CommandArgs]\n"
"Options:\n"
" -I --interface-name name Thread network interface name.\n"
" -d --debug-level Debug level of logging.\n"
" -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n"
" --no-reset Do not reset RCP on initialization\n"
" --radio-version Print radio firmware version\n"
" --ncp-dataset Retrieve and save NCP dataset to file\n"
" -s --time-speed factor Time speed up factor.\n"
" -v --verbose Also log to stderr.\n"
" -h --help Display this usage information.\n",
" -I --interface-name name Thread network interface name.\n"
" -d --debug-level Debug level of logging.\n"
" -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n"
" --no-reset Do not send Spinel reset command to RCP on initialization.\n"
" --radio-version Print radio firmware version.\n"
" --ncp-dataset Retrieve and save NCP dataset to file.\n"
" -s --time-speed factor Time speed up factor.\n"
" -v --verbose Also log to stderr.\n"
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
" --gpio-int-dev[=gpio-device-path]\n"
" Specify a path to the Linux sysfs-exported GPIO device for the\n"
" `I̅N̅T̅` pin. If not specified, `SPI` interface will fall back to\n"
" polling, which is inefficient.\n"
" --gpio-int-line[=line-offset]\n"
" The offset index of `I̅N̅T̅` pin for the associated GPIO device.\n"
" If not specified, `SPI` interface will fall back to polling,\n"
" which is inefficient.\n"
" --gpio-reset-dev[=gpio-device-path]\n"
" Specify a path to the Linux sysfs-exported GPIO device for the\n"
" `R̅E̅S̅` pin.\n"
" --gpio-reset-line[=line-offset]"
" The offset index of `R̅E̅S̅` pin for the associated GPIO device.\n"
" --spi-mode[=mode] Specify the SPI mode to use (0-3).\n"
" --spi-speed[=hertz] Specify the SPI speed in hertz.\n"
" --spi-cs-delay[=usec] Specify the delay after C̅S̅ assertion, in µsec.\n"
" --spi-reset-delay[=ms] Specify the delay after R̅E̅S̅E̅T̅ assertion, in milliseconds.\n"
" --spi-align-allowance[=n] Specify the maximum number of 0xFF bytes to clip from start of\n"
" MISO frame. Max value is 16.\n"
" --spi-small-packet=[n] Specify the smallest packet we can receive in a single transaction.\n"
" (larger packets will require two transactions). Default value is 32.\n"
#endif
" -h --help Display this usage information.\n",
aProgramName);
exit(aExitCode);
}
@@ -124,9 +180,15 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
{
memset(aConfig, 0, sizeof(PosixConfig));
aConfig->mPlatformConfig.mSpeedUpFactor = 1;
aConfig->mPlatformConfig.mResetRadio = true;
aConfig->mLogLevel = OT_LOG_LEVEL_CRIT;
aConfig->mPlatformConfig.mSpeedUpFactor = 1;
aConfig->mPlatformConfig.mResetRadio = true;
aConfig->mPlatformConfig.mSpiSpeed = OT_PLATFORM_CONFIG_SPI_DEFAULT_SPEED_HZ;
aConfig->mPlatformConfig.mSpiCsDelay = OT_PLATFORM_CONFIG_SPI_DEFAULT_CS_DELAY_US;
aConfig->mPlatformConfig.mSpiResetDelay = OT_PLATFORM_CONFIG_SPI_DEFAULT_RESET_DELAY_MS;
aConfig->mPlatformConfig.mSpiAlignAllowance = OT_PLATFORM_CONFIG_SPI_DEFAULT_ALIGN_ALLOWANCE;
aConfig->mPlatformConfig.mSpiSmallPacketSize = OT_PLATFORM_CONFIG_SPI_DEFAULT_SMALL_PACKET_SIZE;
aConfig->mPlatformConfig.mSpiMode = OT_PLATFORM_CONFIG_SPI_DEFAULT_MODE;
aConfig->mLogLevel = OT_LOG_LEVEL_CRIT;
optind = 1;
@@ -170,21 +232,47 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
case 'v':
aConfig->mIsVerbose = true;
break;
case 0:
if (!strcmp(kOptions[index].name, "radio-version"))
{
aConfig->mPrintRadioVersion = true;
}
else if (!strcmp(kOptions[index].name, "no-reset"))
{
aConfig->mPlatformConfig.mResetRadio = false;
}
else if (!strcmp(kOptions[index].name, "ncp-dataset"))
{
aConfig->mPlatformConfig.mRestoreDatasetFromNcp = true;
}
case ARG_PRINT_RADIO_VERSION:
aConfig->mPrintRadioVersion = true;
break;
case ARG_NO_RADIO_RESET:
aConfig->mPlatformConfig.mResetRadio = false;
break;
case ARG_RESTORE_NCP_DATASET:
aConfig->mPlatformConfig.mRestoreDatasetFromNcp = true;
break;
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
case ARG_SPI_GPIO_INT_DEV:
aConfig->mPlatformConfig.mSpiGpioIntDevice = optarg;
break;
case ARG_SPI_GPIO_INT_LINE:
aConfig->mPlatformConfig.mSpiGpioIntLine = (uint8_t)atoi(optarg);
break;
case ARG_SPI_GPIO_RESET_DEV:
aConfig->mPlatformConfig.mSpiGpioResetDevice = optarg;
break;
case ARG_SPI_GPIO_RESET_LINE:
aConfig->mPlatformConfig.mSpiGpioResetLine = (uint8_t)atoi(optarg);
break;
case ARG_SPI_MODE:
aConfig->mPlatformConfig.mSpiMode = (uint8_t)atoi(optarg);
break;
case ARG_SPI_SPEED:
aConfig->mPlatformConfig.mSpiSpeed = atoi(optarg);
break;
case ARG_SPI_CS_DELAY:
aConfig->mPlatformConfig.mSpiCsDelay = atoi(optarg);
break;
case ARG_SPI_RESET_DELAY:
aConfig->mPlatformConfig.mSpiResetDelay = atoi(optarg);
break;
case ARG_SPI_ALIGN_ALLOWANCE:
aConfig->mPlatformConfig.mSpiAlignAllowance = atoi(optarg);
break;
case ARG_SPI_SMALL_PACKET:
aConfig->mPlatformConfig.mSpiSmallPacketSize = atoi(optarg);
break;
#endif // OPENTHREAD_POSIX_RCP_SPI_ENABLE
case '?':
PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
break;
+2
View File
@@ -37,6 +37,7 @@ list(APPEND OT_PLATFORM_DEFINES
"OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1"
"OPENTHREAD_CONFIG_NCP_UART_ENABLE=1"
"OPENTHREAD_POSIX=1"
"OPENTHREAD_POSIX_RCP_UART_ENABLE=1"
"OPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"openthread-core-posix-config.h\""
)
set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE)
@@ -51,6 +52,7 @@ add_library(openthread-posix
radio_spinel.cpp
settings.cpp
sim.c
spi_interface.cpp
system.c
uart.c
udp.cpp
+2
View File
@@ -51,6 +51,7 @@ libopenthread_posix_a_SOURCES = \
radio_spinel.cpp \
settings.cpp \
sim.c \
spi_interface.cpp \
system.c \
uart.c \
udp.cpp \
@@ -73,6 +74,7 @@ dist_openthread_HEADERS = $(openthread_headers)
PRETTY_FILES = \
$(libopenthread_posix_a_SOURCES) \
$(noinst_HEADERS) \
$(openthread_HEADERS) \
$(NULL)
if OPENTHREAD_BUILD_COVERAGE
+12 -8
View File
@@ -119,14 +119,16 @@
#endif // __APPLE__
#if OPENTHREAD_POSIX_RCP_UART_ENABLE
namespace ot {
namespace PosixApp {
HdlcInterface::HdlcInterface(Callbacks &aCallbacks)
: SpinelInterface()
, mCallbacks(aCallbacks)
HdlcInterface::HdlcInterface(SpinelInterface::Callbacks &aCallback, SpinelInterface::RxFrameBuffer &aFrameBuffer)
: mCallbacks(aCallback)
, mRxFrameBuffer(aFrameBuffer)
, mSockFd(-1)
, mHdlcDecoder(mRxFrameBuffer, HandleHdlcFrame, this)
, mHdlcDecoder(aFrameBuffer, HandleHdlcFrame, this)
{
}
@@ -247,14 +249,15 @@ exit:
return error;
}
otError HdlcInterface::WaitForFrame(struct timeval &aTimeout)
otError HdlcInterface::WaitForFrame(const struct timeval &aTimeout)
{
otError error = OT_ERROR_NONE;
otError error = OT_ERROR_NONE;
struct timeval timeout = aTimeout;
#if OPENTHREAD_POSIX_VIRTUAL_TIME
struct Event event;
platformSimSendSleepEvent(&aTimeout);
platformSimSendSleepEvent(&timeout);
platformSimReceiveEvent(&event);
switch (event.mEvent)
@@ -281,7 +284,7 @@ otError HdlcInterface::WaitForFrame(struct timeval &aTimeout)
FD_SET(mSockFd, &read_fds);
FD_SET(mSockFd, &error_fds);
rval = select(mSockFd + 1, &read_fds, NULL, &error_fds, &aTimeout);
rval = select(mSockFd + 1, &read_fds, NULL, &error_fds, &timeout);
if (rval > 0)
{
@@ -623,3 +626,4 @@ void HdlcInterface::HandleHdlcFrame(otError aError)
} // namespace PosixApp
} // namespace ot
#endif // OPENTHREAD_POSIX_RCP_UART_ENABLE
+17 -6
View File
@@ -36,9 +36,10 @@
#include "platform-config.h"
#include "spinel_interface.hpp"
#include "ncp/hdlc.hpp"
#if OPENTHREAD_POSIX_RCP_UART_ENABLE
namespace ot {
namespace PosixApp {
@@ -46,16 +47,17 @@ namespace PosixApp {
* This class defines an HDLC interface to the Radio Co-processor (RCP)
*
*/
class HdlcInterface : public SpinelInterface
class HdlcInterface
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aCallback A reference to a `Callback` object.
* @param[in] aCallback A reference to a `Callback` object.
* @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object.
*
*/
explicit HdlcInterface(Callbacks &aCallbacks);
HdlcInterface(SpinelInterface::Callbacks &aCallback, SpinelInterface::RxFrameBuffer &aFrameBuffer);
/**
* This destructor deinitializes the object.
@@ -108,7 +110,7 @@ public:
* @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
*
*/
otError WaitForFrame(struct timeval &aTimeout);
otError WaitForFrame(const struct timeval &aTimeout);
/**
* This method updates the file descriptor sets with file descriptors used by the radio driver.
@@ -199,7 +201,15 @@ private:
static int ForkPty(const char *aCommand, const char *aArguments);
#endif
Callbacks & mCallbacks;
enum
{
kMaxFrameSize = SpinelInterface::kMaxFrameSize,
kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`).
};
SpinelInterface::Callbacks & mCallbacks;
SpinelInterface::RxFrameBuffer &mRxFrameBuffer;
int mSockFd;
Hdlc::Decoder mHdlcDecoder;
};
@@ -207,4 +217,5 @@ private:
} // namespace PosixApp
} // namespace ot
#endif // OPENTHREAD_POSIX_RCP_UART_ENABLE
#endif // POSIX_APP_HDLC_INTERFACE_HPP_
+27
View File
@@ -84,6 +84,22 @@ enum
OT_EXIT_ERROR_ERRNO = 5,
};
/**
* This enumeration represents default parameters for the SPI interface.
*
*/
enum
{
OT_PLATFORM_CONFIG_SPI_DEFAULT_MODE = 0, ///< Default SPI Mode: CPOL=0, CPHA=0.
OT_PLATFORM_CONFIG_SPI_DEFAULT_SPEED_HZ = 1000000, ///< Default SPI speed in hertz.
OT_PLATFORM_CONFIG_SPI_DEFAULT_CS_DELAY_US = 20, ///< Default delay after SPI C̅S̅ assertion, in µsec.
OT_PLATFORM_CONFIG_SPI_DEFAULT_RESET_DELAY_MS = 0, ///< Default delay after R̅E̅S̅E̅T̅ assertion, in miliseconds.
OT_PLATFORM_CONFIG_SPI_DEFAULT_ALIGN_ALLOWANCE =
16, ///< Default maximum number of 0xFF bytes to clip from start of MISO frame.
OT_PLATFORM_CONFIG_SPI_DEFAULT_SMALL_PACKET_SIZE =
32, ///< Default smallest SPI packet size we can receive in a single transaction.
};
/**
* This structure represents platform specific configurations.
*
@@ -97,6 +113,17 @@ typedef struct otPlatformConfig
const char *mRadioConfig; ///< Radio configurations.
bool mResetRadio; ///< Whether to reset RCP when initializing.
bool mRestoreDatasetFromNcp; ///< Whether to retrieve dataset from NCP and save to file.
char * mSpiGpioIntDevice; ///< Path to the Linux GPIO character device for the `I̅N̅T̅` pin.
char * mSpiGpioResetDevice; ///< Path to the Linux GPIO character device for the `R̅E̅S̅E̅T̅` pin.
uint8_t mSpiGpioIntLine; ///< Line index of the `I̅N̅T̅` pin for the associated GPIO character device.
uint8_t mSpiGpioResetLine; ///< Line index of the `R̅E̅S̅E̅T̅` pin for the associated GPIO character device.
uint8_t mSpiMode; ///< SPI mode to use (0-3).
uint32_t mSpiSpeed; ///< SPI speed in hertz.
uint32_t mSpiResetDelay; ///< The delay after R̅E̅S̅E̅T̅ assertion, in miliseconds.
uint16_t mSpiCsDelay; ///< The delay after SPI C̅S̅ assertion, in µsec.
uint8_t mSpiAlignAllowance; ///< Maximum number of 0xFF bytes to clip from start of MISO frame.
uint8_t mSpiSmallPacketSize; ///< Smallest SPI packet size we can receive in a single transaction.
} otPlatformConfig;
/**
+31 -40
View File
@@ -156,9 +156,15 @@ static void LogIfFail(const char *aText, otError aError)
}
}
void SpinelInterface::Callbacks::HandleReceivedFrame(void)
{
static_cast<RadioSpinel *>(this)->HandleReceivedFrame();
}
RadioSpinel::RadioSpinel(void)
: mInstance(NULL)
, mHdlcInterface(*this)
, mRxFrameBuffer()
, mSpinelInterface(*this, mRxFrameBuffer)
, mCmdTidsInUse(0)
, mCmdNextTid(1)
, mTxRadioTid(0)
@@ -192,7 +198,7 @@ void RadioSpinel::Init(const otPlatformConfig &aPlatformConfig)
otError error = OT_ERROR_NONE;
bool isRcp;
SuccessOrExit(error = mHdlcInterface.Init(aPlatformConfig));
SuccessOrExit(error = mSpinelInterface.Init(aPlatformConfig));
if (aPlatformConfig.mResetRadio)
{
@@ -339,19 +345,18 @@ exit:
void RadioSpinel::Deinit(void)
{
mHdlcInterface.Deinit();
mSpinelInterface.Deinit();
// This allows implementing pseudo reset.
new (this) RadioSpinel();
}
void RadioSpinel::HandleReceivedFrame(void)
{
otError error = OT_ERROR_NONE;
SpinelInterface::RxFrameBuffer &frameBuffer = mHdlcInterface.GetRxFrameBuffer();
uint8_t header;
spinel_ssize_t unpacked;
otError error = OT_ERROR_NONE;
uint8_t header;
spinel_ssize_t unpacked;
unpacked = spinel_datatype_unpack(frameBuffer.GetFrame(), frameBuffer.GetLength(), "C", &header);
unpacked = spinel_datatype_unpack(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), "C", &header);
VerifyOrExit(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG &&
SPINEL_HEADER_GET_IID(header) == 0,
@@ -359,23 +364,23 @@ void RadioSpinel::HandleReceivedFrame(void)
if (SPINEL_HEADER_GET_TID(header) == 0)
{
HandleNotification(frameBuffer);
HandleNotification(mRxFrameBuffer);
}
else
{
HandleResponse(frameBuffer.GetFrame(), frameBuffer.GetLength());
frameBuffer.DiscardFrame();
HandleResponse(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength());
mRxFrameBuffer.DiscardFrame();
}
exit:
if (error != OT_ERROR_NONE)
{
frameBuffer.DiscardFrame();
mRxFrameBuffer.DiscardFrame();
otLogWarnPlat("Error handling hdlc frame: %s", otThreadErrorToString(error));
}
}
void RadioSpinel::HandleNotification(HdlcInterface::RxFrameBuffer &aFrameBuffer)
void RadioSpinel::HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer)
{
spinel_prop_key_t key;
spinel_size_t len = 0;
@@ -858,12 +863,12 @@ void RadioSpinel::ProcessFrameQueue(void)
uint8_t *frame = NULL;
uint16_t length;
while (mHdlcInterface.GetRxFrameBuffer().GetNextSavedFrame(frame, length) == OT_ERROR_NONE)
while (mRxFrameBuffer.GetNextSavedFrame(frame, length) == OT_ERROR_NONE)
{
HandleNotification(frame, length);
}
mHdlcInterface.GetRxFrameBuffer().ClearSavedFrames();
mRxFrameBuffer.ClearSavedFrames();
}
void RadioSpinel::RadioReceive(void)
@@ -914,7 +919,7 @@ void RadioSpinel::TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, ot
void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout)
{
mHdlcInterface.UpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd, aTimeout);
mSpinelInterface.UpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd, aTimeout);
if (mState == kStateTransmitting)
{
@@ -937,7 +942,7 @@ void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMax
}
}
if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame() || (mState == kStateTransmitDone))
if (mRxFrameBuffer.HasSavedFrame() || (mState == kStateTransmitDone))
{
aTimeout.tv_sec = 0;
aTimeout.tv_usec = 0;
@@ -946,15 +951,15 @@ void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMax
void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet)
{
if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame())
if (mRxFrameBuffer.HasSavedFrame())
{
// Handle frames received and saved during `WaitResponse()`
ProcessFrameQueue();
}
mHdlcInterface.Process(aReadFdSet, aWriteFdSet);
mSpinelInterface.Process(aReadFdSet, aWriteFdSet);
if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame())
if (mRxFrameBuffer.HasSavedFrame())
{
ProcessFrameQueue();
}
@@ -1236,7 +1241,7 @@ otError RadioSpinel::WaitResponse(void)
do
{
if (mHdlcInterface.WaitForFrame(timeout) == OT_ERROR_RESPONSE_TIMEOUT)
if (mSpinelInterface.WaitForFrame(timeout) == OT_ERROR_RESPONSE_TIMEOUT)
{
FreeTid(mWaitingTid);
mWaitingTid = 0;
@@ -1292,7 +1297,7 @@ otError RadioSpinel::SendReset(void)
VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = mHdlcInterface.SendFrame(buffer, static_cast<uint16_t>(packed)));
SuccessOrExit(error = mSpinelInterface.SendFrame(buffer, static_cast<uint16_t>(packed)));
sleep(0);
@@ -1328,7 +1333,7 @@ otError RadioSpinel::SendCommand(uint32_t aCommand,
offset += static_cast<uint16_t>(packed);
}
error = mHdlcInterface.SendFrame(buffer, offset);
error = mSpinelInterface.SendFrame(buffer, offset);
exit:
return error;
@@ -1844,7 +1849,7 @@ exit:
#if OPENTHREAD_POSIX_VIRTUAL_TIME
void ot::PosixApp::RadioSpinel::Process(const Event &aEvent)
{
if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame())
if (mRxFrameBuffer.HasSavedFrame())
{
ProcessFrameQueue();
}
@@ -1852,10 +1857,10 @@ void ot::PosixApp::RadioSpinel::Process(const Event &aEvent)
// The current event can be other event types
if (aEvent.mEvent == OT_SIM_EVENT_RADIO_SPINEL_WRITE)
{
mHdlcInterface.ProcessReadData(aEvent.mData, aEvent.mDataLength);
mSpinelInterface.ProcessReadData(aEvent.mData, aEvent.mDataLength);
}
if (mHdlcInterface.GetRxFrameBuffer().HasSavedFrame())
if (mRxFrameBuffer.HasSavedFrame())
{
ProcessFrameQueue();
}
@@ -1958,17 +1963,3 @@ uint32_t otPlatRadioGetPreferredChannelMask(otInstance *aInstance)
OT_UNUSED_VARIABLE(aInstance);
return sRadioSpinel.GetRadioChannelMask(true);
}
/*
* Calling a pure virtual function is illegal. If the code calls a pure virtual function, then the compiler
* calls the function "__cxa_pure_virtual()" to handle it. The call to this function should never happen in
* the normal application run. If it happens it means there is a bug.
*
* This function is defined here to avoid that some libraries don't define it when COVERAGE is set to 1.
*
*/
extern "C" void __cxa_pure_virtual()
{
otLogCritPlat("__cxa_pure_virtual() is called");
exit(OT_EXIT_FAILURE);
}
+18 -3
View File
@@ -36,7 +36,14 @@
#include <openthread/platform/radio.h>
#if OPENTHREAD_POSIX_RCP_UART_ENABLE
#include "hdlc_interface.hpp"
#endif
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
#include "spi_interface.hpp"
#endif
#include "spinel_interface.hpp"
#include "ncp/ncp_config.h"
#include "ncp/spinel.h"
@@ -522,7 +529,7 @@ public:
private:
enum
{
kMaxSpinelFrame = HdlcInterface::kMaxFrameSize,
kMaxSpinelFrame = SpinelInterface::kMaxFrameSize,
kMaxWaitTime = 2000, ///< Max time to wait for response in milliseconds.
kVersionStringSize = 128, ///< Max size of version string.
kCapsBufferSize = 100, ///< Max buffer size used to store `SPINEL_PROP_CAPS` value.
@@ -632,7 +639,7 @@ private:
return !(aKey == SPINEL_PROP_STREAM_RAW || aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT);
}
void HandleNotification(HdlcInterface::RxFrameBuffer &aFrameBuffer);
void HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer);
void HandleNotification(const uint8_t *aBuffer, uint16_t aLength);
void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
@@ -657,7 +664,15 @@ private:
otInstance *mInstance;
HdlcInterface mHdlcInterface;
SpinelInterface::RxFrameBuffer mRxFrameBuffer;
#if OPENTHREAD_POSIX_RCP_UART_ENABLE
HdlcInterface mSpinelInterface;
#endif
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
SpiInterface mSpinelInterface;
#endif
uint16_t mCmdTidsInUse; ///< Used transaction ids.
spinel_tid_t mCmdNextTid; ///< Next available transaction id.
+782
View File
@@ -0,0 +1,782 @@
/*
* Copyright (c) 2019, 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 includes the implementation for the SPI interface to radio (RCP).
*/
#include "openthread-core-config.h"
#include "platform-posix.h"
#include "spi_interface.hpp"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/ucontext.h>
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
#include <linux/gpio.h>
#include <linux/ioctl.h>
#include <linux/spi/spidev.h>
namespace ot {
namespace PosixApp {
SpiInterface::SpiInterface(SpinelInterface::Callbacks &aCallback, SpinelInterface::RxFrameBuffer &aFrameBuffer)
: mCallbacks(aCallback)
, mRxFrameBuffer(aFrameBuffer)
, mSpiDevFd(-1)
, mResetGpioValueFd(-1)
, mIntGpioValueFd(-1)
, mSlaveResetCount(0)
, mSpiFrameCount(0)
, mSpiValidFrameCount(0)
, mSpiGarbageFrameCount(0)
, mSpiDuplexFrameCount(0)
, mSpiUnresponsiveFrameCount(0)
, mSpiRxFrameCount(0)
, mSpiRxFrameByteCount(0)
, mSpiTxFrameCount(0)
, mSpiTxFrameByteCount(0)
, mSpiTxIsReady(false)
, mSpiTxRefusedCount(0)
, mSpiTxPayloadSize(0)
, mDidPrintRateLimitLog(false)
, mSpiSlaveDataLen(0)
{
}
otError SpiInterface::Init(const otPlatformConfig &aPlatformConfig)
{
VerifyOrDie(aPlatformConfig.mSpiAlignAllowance <= kSpiAlignAllowanceMax, OT_EXIT_FAILURE);
mSpiCsDelayUs = aPlatformConfig.mSpiCsDelay;
mSpiSmallPacketSize = aPlatformConfig.mSpiSmallPacketSize;
mSpiAlignAllowance = aPlatformConfig.mSpiAlignAllowance;
if (aPlatformConfig.mSpiGpioIntDevice != NULL)
{
// If the interrupt pin is not set, SPI interface will use polling mode.
InitIntPin(aPlatformConfig.mSpiGpioIntDevice, aPlatformConfig.mSpiGpioIntLine);
otLogNotePlat("SPI interface enters polling mode.");
}
InitResetPin(aPlatformConfig.mSpiGpioResetDevice, aPlatformConfig.mSpiGpioResetLine);
InitSpiDev(aPlatformConfig.mRadioFile, aPlatformConfig.mSpiMode, aPlatformConfig.mSpiSpeed);
// Reset RCP chip.
TrigerReset();
// Waiting for the RCP chip starts up.
usleep(static_cast<useconds_t>(aPlatformConfig.mSpiResetDelay) * kUsecPerMsec);
return OT_ERROR_NONE;
}
SpiInterface::~SpiInterface(void)
{
Deinit();
}
void SpiInterface::Deinit(void)
{
if (mSpiDevFd >= 0)
{
close(mSpiDevFd);
mSpiDevFd = -1;
}
if (mResetGpioValueFd >= 0)
{
close(mResetGpioValueFd);
mResetGpioValueFd = -1;
}
if (mIntGpioValueFd >= 0)
{
close(mIntGpioValueFd);
mIntGpioValueFd = -1;
}
}
int SpiInterface::SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel)
{
struct gpiohandle_request req;
int ret;
assert(strlen(aLabel) < sizeof(req.consumer_label));
req.flags = aHandleFlags;
req.lines = 1;
req.lineoffsets[0] = aLine;
req.default_values[0] = 1;
snprintf(req.consumer_label, sizeof(req.consumer_label), "%s", aLabel);
VerifyOrDie((ret = ioctl(aFd, GPIO_GET_LINEHANDLE_IOCTL, &req)) != -1, OT_EXIT_ERROR_ERRNO);
return req.fd;
}
int SpiInterface::SetupGpioEvent(int aFd,
uint8_t aLine,
uint32_t aHandleFlags,
uint32_t aEventFlags,
const char *aLabel)
{
struct gpioevent_request req;
int ret;
assert(strlen(aLabel) < sizeof(req.consumer_label));
req.lineoffset = aLine;
req.handleflags = aHandleFlags;
req.eventflags = aEventFlags;
snprintf(req.consumer_label, sizeof(req.consumer_label), "%s", aLabel);
VerifyOrDie((ret = ioctl(aFd, GPIO_GET_LINEEVENT_IOCTL, &req)) != -1, OT_EXIT_ERROR_ERRNO);
return req.fd;
}
void SpiInterface::SetGpioValue(int aFd, uint8_t aValue)
{
struct gpiohandle_data data;
data.values[0] = aValue;
VerifyOrDie(ioctl(aFd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) != -1, OT_EXIT_ERROR_ERRNO);
}
uint8_t SpiInterface::GetGpioValue(int aFd)
{
struct gpiohandle_data data;
VerifyOrDie(ioctl(aFd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) != -1, OT_EXIT_ERROR_ERRNO);
return data.values[0];
}
void SpiInterface::InitResetPin(const char *aCharDev, uint8_t aLine)
{
char label[] = "SOC_THREAD_RESET";
int fd;
otLogDebgPlat("InitResetPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
VerifyOrDie((aCharDev != NULL) && (aLine < GPIOHANDLES_MAX), OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
mResetGpioValueFd = SetupGpioHandle(fd, aLine, GPIOHANDLE_REQUEST_OUTPUT, label);
close(fd);
}
void SpiInterface::InitIntPin(const char *aCharDev, uint8_t aLine)
{
char label[] = "THREAD_SOC_INT";
int fd;
otLogDebgPlat("InitIntPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
VerifyOrDie((aCharDev != NULL) && (aLine < GPIOHANDLES_MAX), OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
mIntGpioValueFd = SetupGpioEvent(fd, aLine, GPIOHANDLE_REQUEST_INPUT, GPIOEVENT_REQUEST_FALLING_EDGE, label);
close(fd);
}
void SpiInterface::InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed)
{
const uint8_t wordBits = kSpiBitsPerWord;
int fd;
otLogDebgPlat("InitSpiDev: path=%s, mode=%" PRIu8 ", speed=%" PRIu32, aPath, aMode, aSpeed);
VerifyOrDie((aPath != NULL) && (aMode <= kSpiModeMax), OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aPath, O_RDWR | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
VerifyOrExit(ioctl(fd, SPI_IOC_WR_MODE, &aMode) != -1, LogError("ioctl(SPI_IOC_WR_MODE)"));
VerifyOrExit(ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &aSpeed) != -1, LogError("ioctl(SPI_IOC_WR_MAX_SPEED_HZ)"));
VerifyOrExit(ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &wordBits) != -1, LogError("ioctl(SPI_IOC_WR_BITS_PER_WORD)"));
VerifyOrExit(flock(fd, LOCK_EX | LOCK_NB) != -1, LogError("flock"));
mSpiDevFd = fd;
mSpiMode = aMode;
mSpiSpeedHz = aSpeed;
fd = -1;
exit:
if (fd >= 0)
{
close(fd);
}
}
void SpiInterface::TrigerReset(void)
{
// Set Reset pin to low level.
SetGpioValue(mResetGpioValueFd, 0);
usleep(kResetHoldOnUsec);
// Set Reset pin to high level.
SetGpioValue(mResetGpioValueFd, 1);
otLogNotePlat("Triggered hardware reset");
}
uint8_t *SpiInterface::GetRealRxFrameStart(void)
{
uint8_t * ret = mSpiRxFrameBuffer;
const uint8_t *end = mSpiRxFrameBuffer + mSpiAlignAllowance;
for (; ret != end && ret[0] == 0xff; ret++)
;
return ret;
}
otError SpiInterface::DoSpiTransfer(uint32_t aLength)
{
int ret;
struct spi_ioc_transfer transfer[2];
memset(&transfer[0], 0, sizeof(transfer));
// This part is the delay between C̅S̅ being asserted and the SPI clock
// starting. This is not supported by all Linux SPI drivers.
transfer[0].tx_buf = 0;
transfer[0].rx_buf = 0;
transfer[0].len = 0;
transfer[0].speed_hz = mSpiSpeedHz;
transfer[0].delay_usecs = mSpiCsDelayUs;
transfer[0].bits_per_word = kSpiBitsPerWord;
transfer[0].cs_change = false;
// This part is the actual SPI transfer.
transfer[1].tx_buf = reinterpret_cast<uintptr_t>(mSpiTxFrameBuffer);
transfer[1].rx_buf = reinterpret_cast<uintptr_t>(mSpiRxFrameBuffer);
transfer[1].len = aLength + kSpiFrameHeaderSize + mSpiAlignAllowance;
transfer[1].speed_hz = mSpiSpeedHz;
transfer[1].delay_usecs = 0;
transfer[1].bits_per_word = kSpiBitsPerWord;
transfer[1].cs_change = false;
if (mSpiCsDelayUs > 0)
{
// A C̅S̅ delay has been specified. Start transactions with both parts.
ret = ioctl(mSpiDevFd, SPI_IOC_MESSAGE(2), &transfer[0]);
}
else
{
// No C̅S̅ delay has been specified, so we skip the first part because it causes some SPI drivers to croak.
ret = ioctl(mSpiDevFd, SPI_IOC_MESSAGE(1), &transfer[1]);
}
if (ret != -1)
{
otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, transfer[1].len);
otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer, transfer[1].len);
mSpiFrameCount++;
}
return (ret < 0) ? OT_ERROR_FAILED : OT_ERROR_NONE;
}
otError SpiInterface::PushPullSpi(void)
{
otError error;
uint8_t * spiRxFrameBuffer = NULL;
uint16_t spiTransferBytes = 0;
uint8_t successfulExchanges = 0;
uint8_t slaveHeader;
uint16_t slaveAcceptLen;
Ncp::SpiFrame txFrame(mSpiTxFrameBuffer);
if (mSpiValidFrameCount == 0)
{
// Set the reset flag to indicate to our slave that we are coming up from scratch.
txFrame.SetHeaderFlagByte(true);
}
else
{
txFrame.SetHeaderFlagByte(false);
}
// Zero out our rx_accept and our data_len for now.
txFrame.SetHeaderAcceptLen(0);
txFrame.SetHeaderDataLen(0);
// Sanity check.
if (mSpiSlaveDataLen > kMaxFrameSize)
{
mSpiSlaveDataLen = 0;
}
if (mSpiTxIsReady)
{
// Go ahead and try to immediately send a frame if we have it queued up.
txFrame.SetHeaderDataLen(mSpiTxPayloadSize);
if (mSpiTxPayloadSize > spiTransferBytes)
{
spiTransferBytes = mSpiTxPayloadSize;
}
}
if (mSpiSlaveDataLen != 0)
{
// In a previous transaction the slave indicated it had something to send us. Make sure our transaction
// is large enough to handle it.
if (mSpiSlaveDataLen > spiTransferBytes)
{
spiTransferBytes = mSpiSlaveDataLen;
}
}
else
{
// Set up a minimum transfer size to allow small frames the slave wants to send us to be handled in a
// single transaction.
if (spiTransferBytes < mSpiSmallPacketSize)
{
spiTransferBytes = mSpiSmallPacketSize;
}
}
txFrame.SetHeaderAcceptLen(spiTransferBytes);
// Perform the SPI transaction.
error = DoSpiTransfer(spiTransferBytes);
if (error != OT_ERROR_NONE)
{
otLogCritPlat("PushPullSpi:DoSpiTransfer: errno=%s", strerror(errno));
// Print out a helpful error message for a common error.
if ((mSpiCsDelayUs != 0) && (errno == EINVAL))
{
otLogWarnPlat("SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
}
LogStats();
DieNow(OT_EXIT_FAILURE);
}
// Account for misalignment (0xFF bytes at the start)
spiRxFrameBuffer = GetRealRxFrameStart();
{
Ncp::SpiFrame rxFrame(spiRxFrameBuffer);
otLogDebgPlat("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
otLogDebgPlat("spi_transfer RX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, rxFrame.GetHeaderFlagByte(),
rxFrame.GetHeaderAcceptLen(), rxFrame.GetHeaderDataLen());
slaveHeader = rxFrame.GetHeaderFlagByte();
if ((slaveHeader == 0xFF) || (slaveHeader == 0x00))
{
if ((slaveHeader == spiRxFrameBuffer[1]) && (slaveHeader == spiRxFrameBuffer[2]) &&
(slaveHeader == spiRxFrameBuffer[3]) && (slaveHeader == spiRxFrameBuffer[4]))
{
// Device is off or in a bad state. In some cases may be induced by flow control.
if (mSpiSlaveDataLen == 0)
{
otLogDebgPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
}
else
{
otLogWarnPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
}
mSpiUnresponsiveFrameCount++;
}
else
{
// Header is full of garbage
mSpiGarbageFrameCount++;
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrameBuffer[0], spiRxFrameBuffer[1],
spiRxFrameBuffer[2], spiRxFrameBuffer[3], spiRxFrameBuffer[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
}
mSpiTxRefusedCount++;
ExitNow();
}
slaveAcceptLen = rxFrame.GetHeaderAcceptLen();
mSpiSlaveDataLen = rxFrame.GetHeaderDataLen();
if (!rxFrame.IsValid() || (slaveAcceptLen > kMaxFrameSize) || (mSpiSlaveDataLen > kMaxFrameSize))
{
mSpiGarbageFrameCount++;
mSpiTxRefusedCount++;
mSpiSlaveDataLen = 0;
otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrameBuffer[0], spiRxFrameBuffer[1],
spiRxFrameBuffer[2], spiRxFrameBuffer[3], spiRxFrameBuffer[4]);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
otDumpWarn(OT_LOG_REGION_PLATFORM, "SPI-RX", mSpiRxFrameBuffer,
spiTransferBytes + kSpiFrameHeaderSize + mSpiAlignAllowance);
ExitNow();
}
mSpiValidFrameCount++;
if (rxFrame.IsResetFlagSet())
{
mSlaveResetCount++;
otLogNotePlat("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
LogStats();
}
// Handle received packet, if any.
if ((mSpiSlaveDataLen != 0) && (mSpiSlaveDataLen <= txFrame.GetHeaderAcceptLen()))
{
mSpiRxFrameByteCount += mSpiSlaveDataLen;
mSpiSlaveDataLen = 0;
mSpiRxFrameCount++;
successfulExchanges++;
HandleReceivedFrame(rxFrame);
}
}
// Handle transmitted packet, if any.
if (mSpiTxIsReady && (mSpiTxPayloadSize == txFrame.GetHeaderDataLen()))
{
if (txFrame.GetHeaderDataLen() <= slaveAcceptLen)
{
// Our outbound packet has been successfully transmitted. Clear mSpiTxPayloadSize and mSpiTxIsReady so
// that uplayer can pull another packet for us to send.
successfulExchanges++;
mSpiTxFrameCount++;
mSpiTxFrameByteCount += mSpiTxPayloadSize;
mSpiTxIsReady = false;
mSpiTxPayloadSize = 0;
mSpiTxRefusedCount = 0;
}
else
{
// The slave wasn't ready for what we had to send them. Incrementing this counter will turn on rate
// limiting so that we don't waste a ton of CPU bombarding them with useless SPI transfers.
mSpiTxRefusedCount++;
}
}
if (!mSpiTxIsReady)
{
mSpiTxRefusedCount = 0;
}
if (successfulExchanges == 2)
{
mSpiDuplexFrameCount++;
}
exit:
return error;
}
bool SpiInterface::CheckInterrupt(void)
{
return (mIntGpioValueFd >= 0) ? (GetGpioValue(mIntGpioValueFd) == kGpioIntAssertState) : true;
}
void SpiInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout)
{
struct timeval timeout = {kSecPerDay, 0};
struct timeval pollingTimeout = {0, kSpiPollPeriodUs};
OT_UNUSED_VARIABLE(aWriteFdSet);
if (mSpiTxIsReady)
{
// We have data to send to the slave.
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
if (mIntGpioValueFd >= 0)
{
if (aMaxFd < mIntGpioValueFd)
{
aMaxFd = mIntGpioValueFd;
}
if (CheckInterrupt())
{
// Interrupt pin is asserted, set the timeout to be 0.
timeout.tv_sec = 0;
timeout.tv_usec = 0;
otLogDebgPlat("UpdateFdSet(): Interrupt.");
}
else
{
// The interrupt pin was not asserted, so we wait for the interrupt pin to be asserted by adding it to the
// read set.
FD_SET(mIntGpioValueFd, &aReadFdSet);
}
}
else if (timercmp(&pollingTimeout, &timeout, <))
{
// In this case we don't have an interrupt, so we revert to SPI polling.
timeout = pollingTimeout;
}
if (mSpiTxRefusedCount)
{
struct timeval minTimeout = {0, 0};
// We are being rate-limited by the slave. This is fairly normal behavior. Based on number of times slave has
// refused a transmission, we apply a minimum timeout.
if (mSpiTxRefusedCount < kImmediateRetryCount)
{
minTimeout.tv_usec = kImmediateRetryTimeoutUs;
}
else if (mSpiTxRefusedCount < kFastRetryCount)
{
minTimeout.tv_usec = kFastRetryTimeoutUs;
}
else
{
minTimeout.tv_usec = kSlowRetryTimeoutUs;
}
if (timercmp(&timeout, &minTimeout, <))
{
timeout = minTimeout;
}
if (mSpiTxIsReady && !mDidPrintRateLimitLog && (mSpiTxRefusedCount > 1))
{
// To avoid printing out this message over and over, we only print it out once the refused count is at two
// or higher when we actually have something to send the slave. And then, we only print it once.
otLogInfoPlat("Slave is rate limiting transactions");
mDidPrintRateLimitLog = true;
}
if (mSpiTxRefusedCount == kSpiTxRefuseWarnCount)
{
// Ua-oh. The slave hasn't given us a chance to send it anything for over thirty frames. If this ever
// happens, print out a warning to the logs.
otLogWarnPlat("Slave seems stuck.");
}
else if (mSpiTxRefusedCount == kSpiTxRefuseExitCount)
{
// Double ua-oh. The slave hasn't given us a chance to send it anything for over a hundred frames.
// This almost certainly means that the slave has locked up or gotten into an unrecoverable state.
DieNowWithMessage("Slave seems REALLY stuck.", OT_EXIT_FAILURE);
}
}
else
{
mDidPrintRateLimitLog = false;
}
if (timercmp(&timeout, &aTimeout, <))
{
aTimeout = timeout;
}
}
void SpiInterface::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet)
{
OT_UNUSED_VARIABLE(aWriteFdSet);
if (FD_ISSET(mIntGpioValueFd, &aReadFdSet))
{
struct gpioevent_data event;
otLogDebgPlat("Process(): Interrupt.");
// Read event data to clear interrupt.
VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_ERROR_ERRNO);
}
// Service the SPI port if we can receive a packet or we have a packet to be sent.
if (mSpiTxIsReady || CheckInterrupt())
{
// We guard this with the above check because we don't want to overwrite any previously received frames.
PushPullSpi();
}
}
otError SpiInterface::WaitForFrame(const struct timeval &aTimeout)
{
otError error = OT_ERROR_NONE;
struct timeval timeout = {kSecPerDay, 0};
fd_set readFdSet;
int ret;
FD_ZERO(&readFdSet);
if (mIntGpioValueFd >= 0)
{
if (CheckInterrupt())
{
// Interrupt pin is asserted, set the timeout to be 0.
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
else
{
// The interrupt pin was not asserted, so we wait for the interrupt pin to be asserted by adding it to the
// read set.
FD_SET(mIntGpioValueFd, &readFdSet);
}
}
else
{
// In this case we don't have an interrupt, so we revert to SPI polling.
timeout.tv_sec = 0;
timeout.tv_usec = kSpiPollPeriodUs;
}
if (timercmp(&aTimeout, &timeout, <))
{
timeout = aTimeout;
}
ret = select(mIntGpioValueFd + 1, &readFdSet, NULL, NULL, &timeout);
if (ret > 0)
{
if (FD_ISSET(mIntGpioValueFd, &readFdSet))
{
struct gpioevent_data event;
// Read event data to clear interrupt.
VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_FAILURE);
}
// If we can receive a packet.
if (CheckInterrupt())
{
otLogDebgPlat("WaitForFrame(): Interrupt.");
PushPullSpi();
}
}
else if (ret == 0)
{
ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
}
else if (errno != EINTR)
{
DieNow(OT_EXIT_ERROR_ERRNO);
}
exit:
return error;
}
otError SpiInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aLength < (kMaxFrameSize - kSpiFrameHeaderSize), error = OT_ERROR_NO_BUFS);
VerifyOrExit(!mSpiTxIsReady, error = OT_ERROR_BUSY);
memcpy(&mSpiTxFrameBuffer[kSpiFrameHeaderSize], aFrame, aLength);
mSpiTxIsReady = true;
mSpiTxPayloadSize = aLength;
PushPullSpi();
exit:
return error;
}
void SpiInterface::HandleReceivedFrame(Ncp::SpiFrame &aSpiFrame)
{
const uint8_t *spinelFrame = aSpiFrame.GetData();
for (uint16_t i = 0; i < aSpiFrame.GetHeaderDataLen(); i++)
{
if (mRxFrameBuffer.WriteByte(spinelFrame[i]) != OT_ERROR_NONE)
{
mRxFrameBuffer.DiscardFrame();
otLogNotePlat("No enough memory buffers, drop packet");
ExitNow();
}
}
mCallbacks.HandleReceivedFrame();
exit:
return;
}
void SpiInterface::LogError(const char *aString)
{
OT_UNUSED_VARIABLE(aString);
otLogWarnPlat("%s: %s", aString, strerror(errno));
}
void SpiInterface::LogStats(void)
{
otLogInfoPlat("INFO: mSlaveResetCount=%" PRIu64, mSlaveResetCount);
otLogInfoPlat("INFO: mSpiFrameCount=%" PRIu64, mSpiFrameCount);
otLogInfoPlat("INFO: mSpiValidFrameCount=%" PRIu64, mSpiValidFrameCount);
otLogInfoPlat("INFO: mSpiDuplexFrameCount=%" PRIu64, mSpiDuplexFrameCount);
otLogInfoPlat("INFO: mSpiUnresponsiveFrameCount=%" PRIu64, mSpiUnresponsiveFrameCount);
otLogInfoPlat("INFO: mSpiGarbageFrameCount=%" PRIu64, mSpiGarbageFrameCount);
otLogInfoPlat("INFO: mSpiRxFrameCount=%" PRIu64, mSpiRxFrameCount);
otLogInfoPlat("INFO: mSpiRxFrameByteCount=%" PRIu64, mSpiRxFrameByteCount);
otLogInfoPlat("INFO: mSpiTxFrameCount=%" PRIu64, mSpiTxFrameCount);
otLogInfoPlat("INFO: mSpiTxFrameByteCount=%" PRIu64, mSpiTxFrameByteCount);
}
} // namespace PosixApp
} // namespace ot
#endif // OPENTHREAD_POSIX_RCP_SPI_ENABLE
+228
View File
@@ -0,0 +1,228 @@
/*
* Copyright (c) 2019, 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 includes definitions for the SPI interface to radio (RCP).
*/
#ifndef POSIX_APP_SPI_INTERFACE_HPP_
#define POSIX_APP_SPI_INTERFACE_HPP_
#include "spinel_interface.hpp"
#include "ncp/hdlc.hpp"
#include <openthread-system.h>
#if OPENTHREAD_POSIX_RCP_SPI_ENABLE
#include "ncp/ncp_spi.hpp"
namespace ot {
namespace PosixApp {
/**
* This class defines an SPI interface to the Radio Co-processor (RCP).
*
*/
class SpiInterface
{
public:
/**
* This constructor initializes the object.
*
* @param[in] aCallback A reference to a `Callback` object.
* @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object.
*
*/
SpiInterface(SpinelInterface::Callbacks &aCallback, SpinelInterface::RxFrameBuffer &aFrameBuffer);
/**
* This destructor deinitializes the object.
*
*/
~SpiInterface(void);
/**
* This method initializes the interface to the Radio Co-processor (RCP).
*
* @note This method should be called before reading and sending spinel frames to the interface.
*
* @param[in] aPlatformConfig Platform configuration structure.
*
* @retval OT_ERROR_NONE The interface is initialized successfully.
* @retval OT_ERROR_ALREADY The interface is already initialized.
* @retval OT_ERROR_INVALID_ARGS The UART device or executable cannot be found or failed to open/run.
*
*/
otError Init(const otPlatformConfig &aPlatformConfig);
/**
* This method deinitializes the interface to the RCP.
*
*/
void Deinit(void);
/**
* This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
*
* @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_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame.
* @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame.
*
*/
otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
/**
* This method waits for receiving part or all of spinel frame within specified interval.
*
* @param[in] aTimeout A reference to the timeout.
*
* @retval OT_ERROR_NONE Part or all of spinel frame is received.
* @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
*
*/
otError WaitForFrame(const struct timeval &aTimeout);
/**
* This method updates the file descriptor sets with file descriptors used by the radio driver.
*
* @param[inout] aReadFdSet A reference to the read file descriptors.
* @param[inout] aWriteFdSet A reference to the write file descriptors.
* @param[inout] aMaxFd A reference to the max file descriptor.
* @param[inout] aTimeout A reference to the timeout.
*
*/
void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout);
/**
* This method performs radio driver processing.
*
* @param[in] aReadFdSet A reference to the read file descriptors.
* @param[in] aWriteFdSet A reference to the write file descriptors.
*
*/
void Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet);
private:
int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel);
int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel);
void SetGpioValue(int aFd, uint8_t aValue);
uint8_t GetGpioValue(int aFd);
void InitResetPin(const char *aCharDev, uint8_t aLine);
void InitIntPin(const char *aCharDev, uint8_t aLine);
void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed);
void TrigerReset(void);
uint8_t *GetRealRxFrameStart(void);
otError DoSpiTransfer(uint32_t aLength);
otError PushPullSpi(void);
bool CheckInterrupt(void);
void HandleReceivedFrame(Ncp::SpiFrame &aSpiFrame);
void LogStats(void);
void LogError(const char * aString);
void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce);
enum
{
kSpiModeMax = 3,
kSpiAlignAllowanceMax = 16,
kSpiFrameHeaderSize = 5,
kSpiBitsPerWord = 8,
kSpiTxRefuseWarnCount = 30,
kSpiTxRefuseExitCount = 100,
kImmediateRetryCount = 5,
kFastRetryCount = 15,
kDebugBytesPerLine = 16,
kGpioIntAssertState = 0,
kGpioResetAssertState = 0,
};
enum
{
kMsecPerSec = 1000,
kUsecPerMsec = 1000,
kSpiPollPeriodUs = kMsecPerSec * kUsecPerMsec / 30,
kSecPerDay = 60 * 60 * 24,
kResetHoldOnUsec = 10 * kUsecPerMsec,
kImmediateRetryTimeoutUs = 1 * kUsecPerMsec,
kFastRetryTimeoutUs = 10 * kUsecPerMsec,
kSlowRetryTimeoutUs = 33 * kUsecPerMsec,
};
enum
{
kMaxFrameSize = SpinelInterface::kMaxFrameSize,
};
SpinelInterface::Callbacks & mCallbacks;
SpinelInterface::RxFrameBuffer &mRxFrameBuffer;
int mSpiDevFd;
int mResetGpioValueFd;
int mIntGpioValueFd;
uint8_t mSpiMode;
uint8_t mSpiAlignAllowance;
uint16_t mSpiCsDelayUs;
uint16_t mSpiSmallPacketSize;
uint32_t mSpiSpeedHz;
uint64_t mSlaveResetCount;
uint64_t mSpiFrameCount;
uint64_t mSpiValidFrameCount;
uint64_t mSpiGarbageFrameCount;
uint64_t mSpiDuplexFrameCount;
uint64_t mSpiUnresponsiveFrameCount;
uint64_t mSpiRxFrameCount;
uint64_t mSpiRxFrameByteCount;
uint64_t mSpiTxFrameCount;
uint64_t mSpiTxFrameByteCount;
uint8_t mSpiRxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax];
bool mSpiTxIsReady;
uint16_t mSpiTxRefusedCount;
uint16_t mSpiTxPayloadSize;
uint8_t mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax];
bool mDidPrintRateLimitLog;
uint16_t mSpiSlaveDataLen;
};
} // namespace PosixApp
} // namespace ot
#endif // OPENTHREAD_POSIX_RCP_SPI_ENABLE
#endif // POSIX_APP_SPI_INTERFACE_HPP_
+1 -99
View File
@@ -45,7 +45,6 @@ class SpinelInterface
public:
enum
{
kMaxWaitTime = 2000, ///< Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`).
kMaxFrameSize = 2048, ///< Maximum frame size (number of bytes).
};
@@ -74,106 +73,9 @@ public:
* processed later.
*
*/
virtual void HandleReceivedFrame(void) = 0;
void HandleReceivedFrame(void);
};
/**
* This constructor initializes the object.
*
*/
SpinelInterface()
: mRxFrameBuffer()
{
}
/**
* This method gets the `RxFrameBuffer`.
*
* The receive frame buffer is an `Hdlc::MultiFrameBuffer` and therefore it is capable of storing multiple
* spinel frames in a FIFO queue manner. The `RxFrameBuffer` contains the received spinel frames.
*
* When during `Process()` or `WaitForFrame()` the `Callbacks::HandleReceivedFrame()` is invoked, the newly
* received spinel frame is available in the receive frame buffer. The callback is expected to either process
* and then discard the frame (using `RxFrameBuffer::DiscardFrame()` method) or save the frame
* (using `RxFrameBuffer::SaveFrame()` so that it can be read later.
*
* @returns A reference to receive frame buffer containing newly received spinel frame or previously saved spinel
* frames.
*
*/
RxFrameBuffer &GetRxFrameBuffer(void) { return mRxFrameBuffer; }
/**
* This method initializes the interface to the Radio Co-processor (RCP)
*
* @note This method should be called before reading and sending spinel frames to the interface.
*
* @param[in] aPlatformConfig Platform configuration structure.
*
* @retval OT_ERROR_NONE The interface is initialized successfully
* @retval OT_ERROR_ALREADY The interface is already initialized.
* @retval OT_ERROR_INVALID_ARGS The device or executable cannot be found or failed to open/run.
*
*/
virtual otError Init(const otPlatformConfig &aPlatformConfig) = 0;
/**
* This method deinitializes the interface to the Radio Co-processor (RCP).
*
*/
virtual void Deinit(void) = 0;
/**
* 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`.
*
*/
virtual otError SendFrame(const uint8_t *aFrame, uint16_t aLength) = 0;
/**
* This method waits for receiving part or all of spinel frame within specified interval.
*
* @param[in] aTimeout A reference to the timeout.
*
* @retval OT_ERROR_NONE Part or all of spinel frame is received.
* @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
*
*/
virtual otError WaitForFrame(struct timeval &aTimeout) = 0;
/**
* This method updates the file descriptor sets with file descriptors used by the radio driver.
*
* @param[inout] aReadFdSet A reference to the read file descriptors.
* @param[inout] aWriteFdSet A reference to the write file descriptors.
* @param[inout] aMaxFd A reference to the max file descriptor.
* @param[inout] aTimeout A reference to the timeout.
*
*/
virtual void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) = 0;
/**
* This method performs radio driver processing.
*
* @param[in] aReadFdSet A reference to the read file descriptors.
* @param[in] aWriteFdSet A reference to the write file descriptors.
*
*/
virtual void Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet) = 0;
protected:
RxFrameBuffer mRxFrameBuffer;
};
} // namespace PosixApp
} // namespace ot
+1 -1
View File
@@ -126,7 +126,7 @@ case ${build_config} in
echo "===================================================================================================="
./bootstrap || die
./configure \
CPPFLAGS="$cppflags_config -DOPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE=1" \
CPPFLAGS="$cppflags_config -DOPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE=1 -DOPENTHREAD_POSIX_RCP_UART_ENABLE=1" \
--enable-posix-app \
$configure_options || die
make -j 8 || die