mirror of
https://github.com/espressif/openthread.git
synced 2026-07-26 13:59:05 +00:00
[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:
committed by
Jonathan Hui
parent
5da7469269
commit
0863b652b2
+6
-5
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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_
|
||||
|
||||
Reference in New Issue
Block a user