[posix-app] add HdlcInterface class (#3293)

This commit adds a new class `HdlcInterface` under POSIX App which
performs HDLC encoding/decoding and read/write operations on the
interface to Radio Co-Processor (RCP). `RadioSpinel` is updated to
use the new class.
This commit is contained in:
Abtin Keshavarzian
2018-11-13 15:16:48 -08:00
committed by Jonathan Hui
parent 5da7469269
commit 0863b652b2
6 changed files with 698 additions and 412 deletions
+6 -5
View File
@@ -180,15 +180,16 @@ LOCAL_SRC_FILES := \
src/ncp/spinel_decoder.cpp \
src/ncp/spinel_encoder.cpp \
src/posix/platform/alarm.c \
src/posix/platform/misc.c \
src/posix/platform/frame_queue.cpp \
src/posix/platform/hdlc_interface.cpp \
src/posix/platform/logging.c \
src/posix/platform/misc.c \
src/posix/platform/radio_spinel.cpp \
src/posix/platform/random.c \
src/posix/platform/settings.cpp \
src/posix/platform/spi-stubs.c \
src/posix/platform/system.c \
src/posix/platform/uart.c \
src/posix/platform/spi-stubs.c \
src/posix/platform/frame_queue.cpp \
src/posix/platform/radio_spinel.cpp \
src/posix/platform/settings.cpp \
third_party/mbedtls/repo/library/md.c \
third_party/mbedtls/repo/library/md_wrap.c \
third_party/mbedtls/repo/library/memory_buffer_alloc.c \
+2
View File
@@ -42,6 +42,7 @@ libopenthread_posix_a_CPPFLAGS = \
libopenthread_posix_a_SOURCES = \
alarm.c \
frame_queue.cpp \
hdlc_interface.cpp \
logging.c \
misc.c \
radio_spinel.cpp \
@@ -75,6 +76,7 @@ noinst_HEADERS = \
frame_queue.hpp \
openthread-system.h \
platform-posix.h \
hdlc_interface.hpp \
radio_spinel.hpp \
$(NULL)
+457
View File
@@ -0,0 +1,457 @@
/*
* Copyright (c) 2018, 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 HDLC interface to radio (RCP).
*/
#include "hdlc_interface.hpp"
#include "platform-posix.h"
#include "radio_spinel.hpp"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
#ifdef OPENTHREAD_TARGET_DARWIN
#include <util.h>
#else
#include <pty.h>
#endif
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <syslog.h>
#include <termios.h>
#include <unistd.h>
#include <common/code_utils.hpp>
#include <common/logging.hpp>
#ifndef SOCKET_UTILS_DEFAULT_SHELL
#define SOCKET_UTILS_DEFAULT_SHELL "/bin/sh"
#endif
namespace ot {
namespace PosixApp {
class EncoderBuffer : public Hdlc::Encoder::BufferWriteIterator
{
public:
EncoderBuffer(void)
{
mWritePointer = mBuffer;
mRemainingLength = sizeof(mBuffer);
}
uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - mBuffer); }
const uint8_t *GetBuffer(void) const { return mBuffer; }
private:
uint8_t mBuffer[HdlcInterface::kMaxFrameSize];
};
HdlcInterface::HdlcInterface(Callbacks &aCallbacks)
: mCallbacks(aCallbacks)
, mSockFd(-1)
, mIsDecoding(false)
, mHdlcDecoder(mDecoderBuffer, sizeof(mDecoderBuffer), HandleHdlcFrame, HandleHdlcError, this)
{
}
otError HdlcInterface::Init(const char *aRadioFile, const char *aRadioConfig)
{
otError error = OT_ERROR_NONE;
struct stat st;
VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);
VerifyOrExit(stat(aRadioFile, &st) == 0, perror("stat ncp file failed"); error = OT_ERROR_INVALID_ARGS);
if (S_ISCHR(st.st_mode))
{
mSockFd = OpenFile(aRadioFile, aRadioConfig);
VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
}
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
else if (S_ISREG(st.st_mode))
{
mSockFd = ForkPty(aRadioFile, aRadioConfig);
VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
}
#endif // OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
else
{
otLogCritPlat("Radio file '%s' not supported", aRadioFile);
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
exit:
return error;
}
void HdlcInterface::Deinit(void)
{
assert(mSockFd != -1);
VerifyOrExit(0 == close(mSockFd), perror("close NCP"));
VerifyOrExit(-1 != wait(NULL), perror("wait NCP"));
mSockFd = -1;
exit:
return;
}
void HdlcInterface::Read(void)
{
uint8_t buffer[kMaxFrameSize];
ssize_t rval;
rval = read(mSockFd, buffer, sizeof(buffer));
if (rval < 0)
{
perror("HdlcInterface::Read()");
if (errno != EAGAIN)
{
abort();
}
}
if (rval > 0)
{
Decode(buffer, static_cast<uint16_t>(rval));
}
}
void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength)
{
mIsDecoding = true;
mHdlcDecoder.Decode(aBuffer, aLength);
mIsDecoding = false;
}
otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
{
otError error = OT_ERROR_NONE;
Hdlc::Encoder hdlcEncoder;
EncoderBuffer encoderBuffer;
SuccessOrExit(error = hdlcEncoder.Init(encoderBuffer));
SuccessOrExit(error = hdlcEncoder.Encode(aFrame, aLength, encoderBuffer));
SuccessOrExit(error = hdlcEncoder.Finalize(encoderBuffer));
SuccessOrExit(error = Write(encoderBuffer.GetBuffer(), encoderBuffer.GetLength()));
exit:
return error;
}
otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_POSIX_VIRTUAL_TIME
otSimSendRadioSpinelWriteEvent(aFrame, aLength);
#else
while (aLength)
{
ssize_t rval = write(mSockFd, aFrame, aLength);
if (rval > 0)
{
aLength -= static_cast<uint16_t>(rval);
aFrame += static_cast<uint16_t>(rval);
}
else if (rval < 0)
{
perror("HdlcInterface::Write");
ExitNow(error = OT_ERROR_FAILED);
}
else
{
ExitNow(error = OT_ERROR_FAILED);
}
}
exit:
#endif
return error;
}
int HdlcInterface::OpenFile(const char *aFile, const char *aConfig)
{
int fd = -1;
int rval = 0;
fd = open(aFile, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == -1)
{
perror("open uart failed");
ExitNow();
}
if (isatty(fd))
{
struct termios tios;
int speed = 115200;
int cstopb = 1;
char parity = 'N';
VerifyOrExit((rval = tcgetattr(fd, &tios)) == 0);
cfmakeraw(&tios);
tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
// example: 115200N1
sscanf(aConfig, "%u%c%d", &speed, &parity, &cstopb);
switch (parity)
{
case 'N':
break;
case 'E':
tios.c_cflag |= PARENB;
break;
case 'O':
tios.c_cflag |= (PARENB | PARODD);
break;
default:
// not supported
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
switch (cstopb)
{
case 1:
tios.c_cflag &= static_cast<unsigned long>(~CSTOPB);
break;
case 2:
tios.c_cflag |= CSTOPB;
break;
default:
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
switch (speed)
{
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
#ifdef B230400
case 230400:
speed = B230400;
break;
#endif
#ifdef B460800
case 460800:
speed = B460800;
break;
#endif
#ifdef B500000
case 500000:
speed = B500000;
break;
#endif
#ifdef B576000
case 576000:
speed = B576000;
break;
#endif
#ifdef B921600
case 921600:
speed = B921600;
break;
#endif
#ifdef B1000000
case 1000000:
speed = B1000000;
break;
#endif
#ifdef B1152000
case 1152000:
speed = B1152000;
break;
#endif
#ifdef B1500000
case 1500000:
speed = B1500000;
break;
#endif
#ifdef B2000000
case 2000000:
speed = B2000000;
break;
#endif
#ifdef B2500000
case 2500000:
speed = B2500000;
break;
#endif
#ifdef B3000000
case 3000000:
speed = B3000000;
break;
#endif
#ifdef B3500000
case 3500000:
speed = B3500000;
break;
#endif
#ifdef B4000000
case 4000000:
speed = B4000000;
break;
#endif
default:
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
VerifyOrExit((rval = cfsetspeed(&tios, static_cast<speed_t>(speed))) == 0, perror("cfsetspeed"));
VerifyOrExit((rval = tcsetattr(fd, TCSANOW, &tios)) == 0, perror("tcsetattr"));
VerifyOrExit((rval = tcflush(fd, TCIOFLUSH)) == 0);
}
exit:
if (rval != 0)
{
exit(OT_EXIT_FAILURE);
}
return fd;
}
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
int HdlcInterface::ForkPty(const char *aCommand, const char *aArguments)
{
int fd = -1;
int pid = -1;
{
struct termios tios;
memset(&tios, 0, sizeof(tios));
cfmakeraw(&tios);
tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
pid = forkpty(&fd, NULL, &tios, NULL);
VerifyOrExit(pid >= 0);
}
if (0 == pid)
{
const int kMaxCommand = 255;
char cmd[kMaxCommand];
int rval;
struct rlimit limit;
rval = getrlimit(RLIMIT_NOFILE, &limit);
rval = setenv("SHELL", SOCKET_UTILS_DEFAULT_SHELL, 0);
VerifyOrExit(rval == 0, perror("setenv failed"));
// Close all file descriptors larger than STDERR_FILENO.
for (rlim_t i = (STDERR_FILENO + 1); i < limit.rlim_cur; i++)
{
close(static_cast<int>(i));
}
rval = snprintf(cmd, sizeof(cmd), "exec %s %s", aCommand, aArguments);
VerifyOrExit(rval > 0 && static_cast<size_t>(rval) < sizeof(cmd),
otLogCritPlat("NCP file and configuration is too long!"));
execl(getenv("SHELL"), getenv("SHELL"), "-c", cmd, NULL);
perror("open pty failed");
exit(OT_EXIT_INVALID_ARGUMENTS);
}
else
{
int rval = fcntl(fd, F_GETFL);
if (rval != -1)
{
rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK);
}
if (rval == -1)
{
perror("set nonblock failed");
close(fd);
fd = -1;
}
}
exit:
return fd;
}
#endif // OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
void HdlcInterface::HandleHdlcFrame(void *aContext, uint8_t *aFrame, uint16_t aFrameLength)
{
static_cast<HdlcInterface *>(aContext)->mCallbacks.HandleReceivedFrame(aFrame, aFrameLength);
}
void HdlcInterface::HandleHdlcError(void *aContext, otError aError, uint8_t *aFrame, uint16_t aFrameLength)
{
otLogWarnPlat("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
OT_UNUSED_VARIABLE(aContext);
OT_UNUSED_VARIABLE(aError);
OT_UNUSED_VARIABLE(aFrame);
OT_UNUSED_VARIABLE(aFrameLength);
}
} // namespace PosixApp
} // namespace ot
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2018, 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 HDLC interface to radio (RCP).
*/
#ifndef POSIX_APP_HDLC_INTERFACE_HPP_
#define POSIX_APP_HDLC_INTERFACE_HPP_
#include "openthread-core-config.h"
#include "hdlc.hpp"
namespace ot {
namespace PosixApp {
/**
* This class defines an HDLC interface to the Radio Co-processor (RCP)
*
*/
class HdlcInterface
{
public:
enum
{
kMaxFrameSize = 2048, ///< Maximum frame size (number of bytes).
};
/**
* This class defines the callbacks provided by `HdlcInterfac` to its owner/user.
*
*/
class Callbacks
{
public:
/**
* This callback is invoked to notify owner/user of `HdlcInterface` of a received (and decoded) frame.
*
* @param[in] aFrame A pointer to buffer containing the received frame.
* @param[in] aLength The length (number of bytes) of the received frame.
*
*/
void HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength);
};
/**
* This constructor initializes the object.
*
* @param[in] aCallback A reference to a `Callback` object.
*
*/
explicit HdlcInterface(Callbacks &aCallbacks);
/**
* This method initializes the interface to the Radio Co-processor (RCP)
*
* @note This method should be called before reading and sending frames to the interface.
*
*
* @param[in] aRadioFile The path to either a UART device or an executable.
* @param[in] aRadioConfig Parameters to be given to the device or executable.
*
* @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 char *aRadioFile, const char *aRadioConfig);
/**
* This method deinitializes the interface to the RCP.
*
*/
void Deinit(void);
/**
*
* This method returns the socket file descriptor associate with the interface
*
* @returns The associated socket file descriptor, or -1 if interface is not initializes.
*
*/
int GetSocket(void) const { return mSockFd; }
/**
* This method indicates whether the `HdclInterface` is currently decoding a received frame or not.
*
* @returns TRUE if currently decoding a received frame, FALSE otherwise.
*
*/
bool IsDecoding(void) const { return mIsDecoding; }
/**
* This method instructs `HdlcInterface` to read and decode data from radio over the socket.
*
* If a full HDLC frame is decoded while reading data, this method invokes the `HandleReceivedFrame()` (on the
* `aCallback` object from constructor) to pass the received frame to be processed.
*
*/
void Read(void);
/**
* This method encodes and sends a frame to Radio Co-processor (RCP) over the socket.
*
* @param[in] aFrame A pointer to buffer containing the frame to send.
* @param[in] aLength The length (number of bytes) in the frame
*
* @retval OT_ERROR_NONE Successfully encoded and sent the frame.
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame.
* @retval OT_ERROR_FAILED Failed to send frame due to socket write failure.
*
*/
otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
#if OPENTHREAD_POSIX_VIRTUAL_TIME
/**
* This method process read data (decode the data).
*
* This method is intended only for virtual time simulation. Its behavior is similar to `Read()` but instead of
* reading the data from the radio socket, it uses the given data in the buffer `aBuffer`.
*
* @param[in] aBuffer A pointer to buffer containing data.
* @param[in] aLength The length (number of bytes) in the buffer.
*
*/
void ProcessReadData(const uint8_t *aBuffer, uint16_t aLength) { Decode(aBuffer, aLength); }
#endif
private:
otError Write(const uint8_t *aFrame, uint16_t aLength);
void Decode(const uint8_t *aBuffer, uint16_t aLength);
static void HandleHdlcFrame(void *aContext, uint8_t *aFrame, uint16_t aFrameLength);
static void HandleHdlcError(void *aContext, otError aError, uint8_t *aFrame, uint16_t aFrameLength);
static int OpenFile(const char *aFile, const char *aConfig);
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
static int ForkPty(const char *aCommand, const char *aArguments);
#endif
Callbacks & mCallbacks;
int mSockFd;
bool mIsDecoding;
Hdlc::Decoder mHdlcDecoder;
uint8_t mDecoderBuffer[kMaxFrameSize];
};
} // namespace PosixApp
} // namespace ot
#endif // POSIX_APP_HDLC_INTERFACE_HPP_
+37 -393
View File
@@ -37,14 +37,6 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
#ifdef OPENTHREAD_TARGET_DARWIN
#include <util.h>
#else
#include <pty.h>
#endif
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <sys/resource.h>
@@ -62,10 +54,6 @@
#include <openthread/platform/diag.h>
#include <openthread/platform/radio.h>
#ifndef SOCKET_UTILS_DEFAULT_SHELL
#define SOCKET_UTILS_DEFAULT_SHELL "/bin/sh"
#endif
enum
{
IEEE802154_MIN_LENGTH = 5,
@@ -115,7 +103,7 @@ enum
kDone,
};
static ot::RadioSpinel sRadioSpinel;
static ot::PosixApp::RadioSpinel sRadioSpinel;
static inline otPanId getDstPan(const uint8_t *frame)
{
@@ -143,6 +131,7 @@ static inline bool isAckRequested(const uint8_t *frame)
}
namespace ot {
namespace PosixApp {
static otError SpinelStatusToOtError(spinel_status_t aError)
{
@@ -229,266 +218,23 @@ static void LogIfFail(const char *aText, otError aError)
OT_UNUSED_VARIABLE(aError);
}
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
static int ForkPty(const char *aCommand, const char *aArguments)
void HdlcInterface::Callbacks::HandleReceivedFrame(const uint8_t *aBuffer, uint16_t aLength)
{
int fd = -1;
int pid = -1;
{
struct termios tios;
memset(&tios, 0, sizeof(tios));
cfmakeraw(&tios);
tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
pid = forkpty(&fd, NULL, &tios, NULL);
VerifyOrExit(pid >= 0);
}
if (0 == pid)
{
const int kMaxCommand = 255;
char cmd[kMaxCommand];
int rval;
struct rlimit limit;
rval = getrlimit(RLIMIT_NOFILE, &limit);
rval = setenv("SHELL", SOCKET_UTILS_DEFAULT_SHELL, 0);
VerifyOrExit(rval == 0, perror("setenv failed"));
// Close all file descriptors larger than STDERR_FILENO.
for (rlim_t i = (STDERR_FILENO + 1); i < limit.rlim_cur; i++)
{
close(static_cast<int>(i));
}
rval = snprintf(cmd, sizeof(cmd), "exec %s %s", aCommand, aArguments);
VerifyOrExit(rval > 0 && static_cast<size_t>(rval) < sizeof(cmd),
otLogCritPlat("NCP file and configuration is too long!"));
execl(getenv("SHELL"), getenv("SHELL"), "-c", cmd, NULL);
perror("open pty failed");
exit(OT_EXIT_INVALID_ARGUMENTS);
}
else
{
int rval = fcntl(fd, F_GETFL);
if (rval != -1)
{
rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK);
}
if (rval == -1)
{
perror("set nonblock failed");
close(fd);
fd = -1;
}
}
exit:
return fd;
static_cast<RadioSpinel *>(this)->HandleSpinelFrame(aBuffer, aLength);
}
#endif // OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
static int OpenFile(const char *aFile, const char *aConfig)
{
int fd = -1;
int rval = 0;
fd = open(aFile, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == -1)
{
perror("open uart failed");
ExitNow();
}
if (isatty(fd))
{
struct termios tios;
int speed = 115200;
int cstopb = 1;
char parity = 'N';
VerifyOrExit((rval = tcgetattr(fd, &tios)) == 0);
cfmakeraw(&tios);
tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
// example: 115200N1
sscanf(aConfig, "%u%c%d", &speed, &parity, &cstopb);
switch (parity)
{
case 'N':
break;
case 'E':
tios.c_cflag |= PARENB;
break;
case 'O':
tios.c_cflag |= (PARENB | PARODD);
break;
default:
// not supported
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
switch (cstopb)
{
case 1:
tios.c_cflag &= static_cast<unsigned long>(~CSTOPB);
break;
case 2:
tios.c_cflag |= CSTOPB;
break;
default:
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
switch (speed)
{
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
#ifdef B230400
case 230400:
speed = B230400;
break;
#endif
#ifdef B460800
case 460800:
speed = B460800;
break;
#endif
#ifdef B500000
case 500000:
speed = B500000;
break;
#endif
#ifdef B576000
case 576000:
speed = B576000;
break;
#endif
#ifdef B921600
case 921600:
speed = B921600;
break;
#endif
#ifdef B1000000
case 1000000:
speed = B1000000;
break;
#endif
#ifdef B1152000
case 1152000:
speed = B1152000;
break;
#endif
#ifdef B1500000
case 1500000:
speed = B1500000;
break;
#endif
#ifdef B2000000
case 2000000:
speed = B2000000;
break;
#endif
#ifdef B2500000
case 2500000:
speed = B2500000;
break;
#endif
#ifdef B3000000
case 3000000:
speed = B3000000;
break;
#endif
#ifdef B3500000
case 3500000:
speed = B3500000;
break;
#endif
#ifdef B4000000
case 4000000:
speed = B4000000;
break;
#endif
default:
assert(false);
exit(OT_EXIT_INVALID_ARGUMENTS);
break;
}
VerifyOrExit((rval = cfsetspeed(&tios, static_cast<speed_t>(speed))) == 0, perror("cfsetspeed"));
VerifyOrExit((rval = tcsetattr(fd, TCSANOW, &tios)) == 0, perror("tcsetattr"));
VerifyOrExit((rval = tcflush(fd, TCIOFLUSH)) == 0);
}
exit:
if (rval != 0)
{
exit(OT_EXIT_FAILURE);
}
return fd;
}
class UartTxBuffer : public Hdlc::Encoder::BufferWriteIterator
{
public:
UartTxBuffer(void)
{
mWritePointer = mBuffer;
mRemainingLength = sizeof(mBuffer);
}
uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - mBuffer); }
const uint8_t *GetBuffer(void) const { return mBuffer; }
private:
enum
{
kUartTxBufferSize = 512, // Uart tx buffer size.
};
uint8_t mBuffer[kUartTxBufferSize];
};
RadioSpinel::RadioSpinel(void)
: mCmdTidsInUse(0)
: mInstance(NULL)
, mHdlcInterface(*this)
, mCmdTidsInUse(0)
, mCmdNextTid(1)
, mTxRadioTid(0)
, mWaitingTid(0)
, mWaitingKey(SPINEL_PROP_LAST_STATUS)
, mHdlcDecoder(mHdlcBuffer, sizeof(mHdlcBuffer), HandleSpinelFrame, HandleHdlcError, this)
, mRxSensitivity(0)
, mTxState(kIdle)
, mSockFd(-1)
, mState(OT_RADIO_STATE_DISABLED)
, mIsAckRequested(false)
, mIsDecoding(false)
, mIsPromiscuous(false)
, mIsReady(false)
, mSupportsLogStream(false)
@@ -503,31 +249,9 @@ RadioSpinel::RadioSpinel(void)
void RadioSpinel::Init(const char *aRadioFile, const char *aRadioConfig)
{
otError error = OT_ERROR_NONE;
struct stat st;
otError error = OT_ERROR_NONE;
// not allowed to initialize again.
assert(mSockFd == -1);
VerifyOrExit(stat(aRadioFile, &st) == 0, perror("stat ncp file failed"); error = OT_ERROR_INVALID_ARGS);
if (S_ISCHR(st.st_mode))
{
mSockFd = OpenFile(aRadioFile, aRadioConfig);
VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
}
#if OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
else if (S_ISREG(st.st_mode))
{
mSockFd = ForkPty(aRadioFile, aRadioConfig);
VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
}
#endif // OPENTHREAD_CONFIG_POSIX_APP_ENABLE_PTY_DEVICE
else
{
otLogCritPlat("Radio file '%s' not supported", aRadioFile);
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
SuccessOrExit(error = mHdlcInterface.Init(aRadioFile, aRadioConfig));
SuccessOrExit(error = SendReset());
SuccessOrExit(error = WaitResponse());
@@ -640,14 +364,7 @@ exit:
void RadioSpinel::Deinit(void)
{
// this function is only allowed after successfully initialized.
assert(mSockFd != -1);
VerifyOrExit(0 == close(mSockFd), perror("close NCP"));
VerifyOrExit(-1 != wait(NULL), perror("wait NCP"));
exit:
return;
mHdlcInterface.Deinit();
}
void RadioSpinel::HandleSpinelFrame(const uint8_t *aBuffer, uint16_t aLength)
@@ -946,33 +663,6 @@ exit:
return error;
}
void RadioSpinel::DecodeHdlc(const uint8_t *aData, uint16_t aLength)
{
mIsDecoding = true;
mHdlcDecoder.Decode(aData, aLength);
mIsDecoding = false;
}
void RadioSpinel::ReadAll(void)
{
uint8_t buf[kMaxSpinelFrame];
ssize_t rval = read(mSockFd, buf, sizeof(buf));
if (rval < 0)
{
perror("read spinel");
if (errno != EAGAIN)
{
abort();
}
}
if (rval > 0)
{
DecodeHdlc(buf, static_cast<uint16_t>(rval));
}
}
void RadioSpinel::ProcessFrameQueue(void)
{
uint8_t length;
@@ -1038,23 +728,25 @@ exit:
void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout)
{
int sockFd = mHdlcInterface.GetSocket();
if ((mState != OT_RADIO_STATE_TRANSMIT || mTxState == kSent))
{
FD_SET(mSockFd, &aReadFdSet);
FD_SET(sockFd, &aReadFdSet);
if (aMaxFd < mSockFd)
if (aMaxFd < sockFd)
{
aMaxFd = mSockFd;
aMaxFd = sockFd;
}
}
if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kIdle)
{
FD_SET(mSockFd, &aWriteFdSet);
FD_SET(sockFd, &aWriteFdSet);
if (aMaxFd < mSockFd)
if (aMaxFd < sockFd)
{
aMaxFd = mSockFd;
aMaxFd = sockFd;
}
}
@@ -1067,14 +759,14 @@ void RadioSpinel::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMax
void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet)
{
if (FD_ISSET(mSockFd, &aReadFdSet) || !mFrameQueue.IsEmpty())
if (FD_ISSET(mHdlcInterface.GetSocket(), &aReadFdSet) || !mFrameQueue.IsEmpty())
{
// Handle frames received during WaitResponse()
ProcessFrameQueue();
if (FD_ISSET(mSockFd, &aReadFdSet))
if (FD_ISSET(mHdlcInterface.GetSocket(), &aReadFdSet))
{
ReadAll();
mHdlcInterface.Read();
ProcessFrameQueue();
}
}
@@ -1097,7 +789,7 @@ void RadioSpinel::Process(const fd_set &aReadFdSet, const fd_set &aWriteFdSet)
mTxState = kIdle;
}
if (FD_ISSET(mSockFd, &aWriteFdSet))
if (FD_ISSET(mHdlcInterface.GetSocket(), &aWriteFdSet))
{
if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kIdle)
{
@@ -1308,7 +1000,7 @@ otError RadioSpinel::WaitResponse(void)
switch (event.mEvent)
{
case OT_SIM_EVENT_RADIO_SPINEL_WRITE:
DecodeHdlc(event.mData, event.mDataLength);
mHdlcInterface.ProcessReadData(event.mData, event.mDataLength);
break;
case OT_SIM_EVENT_ALARM_FIRED:
@@ -1322,24 +1014,25 @@ otError RadioSpinel::WaitResponse(void)
break;
}
#else // OPENTHREAD_POSIX_VIRTUAL_TIME
int sockFd = mHdlcInterface.GetSocket();
fd_set read_fds;
fd_set error_fds;
int rval;
FD_ZERO(&read_fds);
FD_ZERO(&error_fds);
FD_SET(mSockFd, &read_fds);
FD_SET(mSockFd, &error_fds);
FD_SET(sockFd, &read_fds);
FD_SET(sockFd, &error_fds);
rval = select(mSockFd + 1, &read_fds, NULL, &error_fds, &timeout);
rval = select(sockFd + 1, &read_fds, NULL, &error_fds, &timeout);
if (rval > 0)
{
if (FD_ISSET(mSockFd, &read_fds))
if (FD_ISSET(sockFd, &read_fds))
{
ReadAll();
mHdlcInterface.Read();
}
else if (FD_ISSET(mSockFd, &error_fds))
else if (FD_ISSET(sockFd, &error_fds))
{
fprintf(stderr, "NCP error\r\n");
exit(OT_EXIT_FAILURE);
@@ -1397,7 +1090,7 @@ spinel_tid_t RadioSpinel::GetNextTid(void)
}
/**
* This method delievers the radio frame to transceiver.
* This method delivers the radio frame to transceiver.
*
* otPlatRadioTxStarted() is triggered immediately for now, which may be earlier than real started time.
*
@@ -1440,42 +1133,10 @@ void RadioSpinel::RadioTransmit(void)
}
}
otError RadioSpinel::WriteAll(const uint8_t *aBuffer, uint16_t aLength)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_POSIX_VIRTUAL_TIME
otSimSendRadioSpinelWriteEvent(aBuffer, aLength);
#else
while (aLength)
{
ssize_t rval = write(mSockFd, aBuffer, aLength);
if (rval > 0)
{
aLength -= static_cast<uint16_t>(rval);
aBuffer += static_cast<uint16_t>(rval);
}
else if (rval < 0)
{
perror("send command failed");
ExitNow(error = OT_ERROR_FAILED);
}
else
{
ExitNow(error = OT_ERROR_FAILED);
}
}
exit:
#endif
return error;
}
otError RadioSpinel::SendReset(void)
{
otError error = OT_ERROR_NONE;
uint8_t buffer[kMaxSpinelFrame];
UartTxBuffer txBuffer;
spinel_ssize_t packed;
// Pack the header, command and key
@@ -1484,16 +1145,7 @@ otError RadioSpinel::SendReset(void)
VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
mHdlcEncoder.Init(txBuffer);
for (spinel_ssize_t i = 0; i < packed; i++)
{
error = mHdlcEncoder.Encode(buffer[i], txBuffer);
VerifyOrExit(error == OT_ERROR_NONE);
}
mHdlcEncoder.Finalize(txBuffer);
error = WriteAll(txBuffer.GetBuffer(), txBuffer.GetLength());
VerifyOrExit(error == OT_ERROR_NONE);
SuccessOrExit(error = mHdlcInterface.SendFrame(buffer, static_cast<uint16_t>(packed)));
sleep(0);
@@ -1509,7 +1161,6 @@ otError RadioSpinel::SendCommand(uint32_t aCommand,
{
otError error = OT_ERROR_NONE;
uint8_t buffer[kMaxSpinelFrame];
UartTxBuffer txBuffer;
spinel_ssize_t packed;
uint16_t offset;
@@ -1530,15 +1181,7 @@ otError RadioSpinel::SendCommand(uint32_t aCommand,
offset += static_cast<uint16_t>(packed);
}
mHdlcEncoder.Init(txBuffer);
for (uint8_t i = 0; i < offset; i++)
{
error = mHdlcEncoder.Encode(buffer[i], txBuffer);
VerifyOrExit(error == OT_ERROR_NONE);
}
mHdlcEncoder.Finalize(txBuffer);
error = WriteAll(txBuffer.GetBuffer(), txBuffer.GetLength());
error = mHdlcInterface.SendFrame(buffer, offset);
exit:
return error;
@@ -1753,6 +1396,7 @@ otError RadioSpinel::PlatDiagProcess(const char *aString, char *aOutput, size_t
}
#endif
} // namespace PosixApp
} // namespace ot
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
@@ -1960,7 +1604,7 @@ int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
}
#if OPENTHREAD_POSIX_VIRTUAL_TIME
void ot::RadioSpinel::Process(const Event &aEvent)
void ot::PosixApp::RadioSpinel::Process(const Event &aEvent)
{
if (!mFrameQueue.IsEmpty())
{
@@ -1970,7 +1614,7 @@ void ot::RadioSpinel::Process(const Event &aEvent)
// The current event can be other event types
if (aEvent.mEvent == OT_SIM_EVENT_RADIO_SPINEL_WRITE)
{
mHdlcDecoder.Decode(aEvent.mData, aEvent.mDataLength);
mHdlcInterface.ProcessReadData(aEvent.mData, aEvent.mDataLength);
ProcessFrameQueue();
}
@@ -1998,7 +1642,7 @@ void ot::RadioSpinel::Process(const Event &aEvent)
}
}
void ot::RadioSpinel::Update(struct timeval &aTimeout)
void ot::PosixApp::RadioSpinel::Update(struct timeval &aTimeout)
{
// Prevent sleep event when transmitting
if (mState == OT_RADIO_STATE_TRANSMIT && mTxState == kIdle)
+18 -14
View File
@@ -37,12 +37,13 @@
#include <openthread/platform/radio.h>
#include "frame_queue.hpp"
#include "hdlc.hpp"
#include "hdlc_interface.hpp"
#include "spinel.h"
namespace ot {
namespace PosixApp {
class RadioSpinel
class RadioSpinel : public HdlcInterface::Callbacks
{
public:
/**
@@ -439,10 +440,19 @@ public:
otError PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen);
#endif
/**
* This method processes a received Spinel frame.
*
* @param[in] aBuffer A pointer to buffer containing the frame.
* @param[in] aLength Length (number of bytes) in the received frame.
*
*/
void HandleSpinelFrame(const uint8_t *aFrame, uint16_t aLength);
private:
enum
{
kMaxSpinelFrame = 2048, ///< Max size in bytes for transferring spinel frames.
kMaxSpinelFrame = HdlcInterface::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.
@@ -451,9 +461,6 @@ private:
otError CheckSpinelVersion(void);
otError CheckCapabilities(void);
otError CheckRadioCapabilities(void);
void DecodeHdlc(const uint8_t *aData, uint16_t aLength);
void ReadAll(void);
otError WriteAll(const uint8_t *aBuffer, uint16_t aLength);
void ProcessFrameQueue(void);
/**
@@ -531,7 +538,6 @@ private:
{
static_cast<RadioSpinel *>(aContext)->HandleSpinelFrame(aBuffer, aLength);
}
void HandleSpinelFrame(const uint8_t *aBuffer, uint16_t aLength);
/**
* This method returns if the property changed event is safe to be handled now.
@@ -546,7 +552,7 @@ private:
*/
bool IsSafeToHandleNow(spinel_prop_key_t aKey) const
{
return !((mIsDecoding || mWaitingKey != SPINEL_PROP_LAST_STATUS) &&
return !((mHdlcInterface.IsDecoding() || mWaitingKey != SPINEL_PROP_LAST_STATUS) &&
(aKey == SPINEL_PROP_STREAM_RAW || aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT));
}
@@ -562,6 +568,8 @@ private:
otInstance *mInstance;
HdlcInterface mHdlcInterface;
uint16_t mCmdTidsInUse; ///< Used transaction ids.
spinel_tid_t mCmdNextTid; ///< Next available transaction id.
spinel_tid_t mTxRadioTid; ///< The transaction id used to send a radio frame.
@@ -572,10 +580,7 @@ private:
uint32_t mExpectedCommand; ///< Expected response command of current transaction.
otError mError; ///< The result of current transaction.
uint8_t mHdlcBuffer[kMaxSpinelFrame];
Hdlc::Decoder mHdlcDecoder;
Hdlc::Encoder mHdlcEncoder;
FrameQueue mFrameQueue;
FrameQueue mFrameQueue;
uint8_t mRxPsdu[OT_RADIO_FRAME_MAX_SIZE];
uint8_t mTxPsdu[OT_RADIO_FRAME_MAX_SIZE];
@@ -593,10 +598,8 @@ private:
otError mTxError;
char mVersion[kVersionStringSize];
int mSockFd;
otRadioState mState;
bool mIsAckRequested : 1; ///< Ack requested.
bool mIsDecoding : 1; ///< Decoding hdlc frames.
bool mIsPromiscuous : 1; ///< Promiscuous mode.
bool mIsReady : 1; ///< NCP ready.
bool mSupportsLogStream : 1; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format.
@@ -608,6 +611,7 @@ private:
#endif
};
} // namespace PosixApp
} // namespace ot
#endif // RADIO_SPINEL_HPP_