mirror of
https://github.com/espressif/openthread.git
synced 2026-08-04 18:07:47 +00:00
[posix] RCP(USB CDC ACM) hard reset support (#6454)
This commit handles the situation when RCP(USB CDC) performs hard reset. This commit brings an alternative to pseudo reset by handling the connection with RCP(USB CDC ACM) by reset the socket connection after the device is reset. When RCP is connected by USB CDC and we reset it, the RCP device is numerated in host operating system. So far for RCP(USB CDC) pseudo reset has been performing meaning only specific parts of firmware were reset thanks to results the connection hasn't been broken. For hard resetting RCP(USB CDC) device the host needs to handle the connection properly. It uses udev library to check if device enumeration has ended if libudev-dev package is present in the host system otherwise it waits few seconds. It works based on symlink to the RCP device created by udev in /dev/serial/by-id/ directory.
This commit is contained in:
@@ -614,6 +614,21 @@ AC_ARG_ENABLE(ncp,
|
||||
AC_MSG_RESULT(${enable_ncp})
|
||||
AM_CONDITIONAL([OPENTHREAD_ENABLE_NCP], [test "${enable_ncp}" = "yes"])
|
||||
|
||||
#
|
||||
# Udev - udev library to use for POSIX NCP
|
||||
#
|
||||
|
||||
AC_ARG_WITH([udev],
|
||||
AS_HELP_STRING([--with-udev], [Use udev library for monitoring tty events]))
|
||||
|
||||
AS_IF([test "x$with_udev" = "xyes"], [
|
||||
AC_CHECK_LIB([udev], [udev_new])
|
||||
AC_CHECK_HEADER([libudev.h])
|
||||
])
|
||||
|
||||
AS_IF([test "x$ac_cv_lib_udev_udev_new" == "xno" || test "x$ac_cv_header_libudev_h" == "xno"],
|
||||
[AC_MSG_ERROR([--with-udev was given, but test for udev failed])])
|
||||
|
||||
#
|
||||
# Readline - readline library to use for POSIX CLI
|
||||
#
|
||||
|
||||
@@ -52,6 +52,7 @@ This page lists the available common switches with description. Unless stated ot
|
||||
| OTNS | OT_OTNS | Enables support for [OpenThread Network Simulator](https://github.com/openthread/ot-ns). Enable this switch if you are building OpenThread for OpenThread Network Simulator. |
|
||||
| PLATFORM_UDP | OT_PLATFORM_UDP | Enables platform UDP support. |
|
||||
| REFERENCE_DEVICE | OT_REFERENCE_DEVICE | Enables support for Thread Test Harness reference device. Enable this switch on the reference device during certification. |
|
||||
| RESET_CONNECTION | OT_SPINEL_RESET_CONNECTION | Enables resetting connection with RCP device. Enable this switch if RCP uses USB transport and performs hard reset. Use symlink instead of file name pointing the RCP device i.e. /dev/serial/by-id/usb-device-name instead of /dev/ttyUSB0 in a connection configuration. |
|
||||
| SERVICE | OT_SERVICE | Enables support for injecting Service entries into the Thread Network Data. |
|
||||
| SETTINGS_RAM | OT_SETTINGS_RAM | Enables volatile-only storage of settings. |
|
||||
| SLAAC | OT_SLAAC | Enables support for adding auto-configured SLAAC addresses by OpenThread. This feature is enabled by default. |
|
||||
|
||||
@@ -55,4 +55,14 @@
|
||||
#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
*
|
||||
* Define 1 to reset connection after hard resetting RCP(USB CDC ACM) device.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
#define OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION 0
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_SPINEL_CONFIG_H_
|
||||
|
||||
@@ -234,6 +234,9 @@ void RadioSpinel<InterfaceType, ProcessContextType>::Init(bool aResetRadio,
|
||||
if (aResetRadio)
|
||||
{
|
||||
SuccessOrExit(error = SendReset());
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
SuccessOrDie(mSpinelInterface.ResetConnection());
|
||||
#endif
|
||||
}
|
||||
|
||||
SuccessOrExit(error = WaitResponse());
|
||||
@@ -2227,6 +2230,9 @@ void RadioSpinel<InterfaceType, ProcessContextType>::RecoverFromRcpFailure(void)
|
||||
if (mResetRadioOnStartup)
|
||||
{
|
||||
SuccessOrDie(SendReset());
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
SuccessOrDie(mSpinelInterface.ResetConnection());
|
||||
#endif
|
||||
}
|
||||
|
||||
SuccessOrDie(WaitResponse());
|
||||
|
||||
@@ -66,6 +66,7 @@ NEIGHBOR_DISCOVERY_AGENT ?= 1
|
||||
PING_SENDER ?= 1
|
||||
READLINE ?= readline
|
||||
REFERENCE_DEVICE ?= 1
|
||||
RESET_CONNECTION ?= 0
|
||||
SERVICE ?= 1
|
||||
SNTP_CLIENT ?= 1
|
||||
SRP_CLIENT ?= 1
|
||||
@@ -125,6 +126,11 @@ ifeq ($(VIRTUAL_TIME),1)
|
||||
COMMONCFLAGS += -DOPENTHREAD_POSIX_VIRTUAL_TIME=1
|
||||
endif
|
||||
|
||||
ifeq ($(RESET_CONNECTION),1)
|
||||
COMMONCFLAGS += -DOPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION=1
|
||||
configure_OPTIONS += --with-udev
|
||||
endif
|
||||
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../examples/common-switches.mk
|
||||
|
||||
CPPFLAGS += \
|
||||
|
||||
@@ -53,6 +53,17 @@ if(NOT OT_CONFIG)
|
||||
set(OT_CONFIG ${OT_CONFIG} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
option(OT_SPINEL_RESET_CONNECTION "reset connection after hard resetting RCP(USB CDC ACM) device" OFF)
|
||||
if (OT_SPINEL_RESET_CONNECTION)
|
||||
list(APPEND OT_PLATFORM_DEFINES "OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION=1")
|
||||
find_library(LIBUDEV "udev")
|
||||
if (LIBUDEV)
|
||||
set(UDEV_LINK_LIBRARIES "${LIBUDEV}")
|
||||
else()
|
||||
message(FATAL_ERROR "test for udev failed")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND OT_PLATFORM_DEFINES "OPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"${OT_CONFIG}\"")
|
||||
@@ -86,6 +97,7 @@ target_link_libraries(openthread-posix
|
||||
openthread-url
|
||||
ot-config
|
||||
util
|
||||
${UDEV_LINK_LIBRARIES}
|
||||
$<$<STREQUAL:${CMAKE_SYSTEM_NAME},Linux>:rt>
|
||||
)
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
#include <pty.h>
|
||||
#endif
|
||||
#endif
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
#include <libudev.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/resource.h>
|
||||
@@ -132,6 +135,7 @@ HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback,
|
||||
, mSockFd(-1)
|
||||
, mBaudRate(0)
|
||||
, mHdlcDecoder(aFrameBuffer, HandleHdlcFrame, this)
|
||||
, mRadioUrl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -167,6 +171,8 @@ otError HdlcInterface::Init(const Url::Url &aRadioUrl)
|
||||
ExitNow(error = OT_ERROR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
mRadioUrl = &aRadioUrl;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
@@ -178,15 +184,7 @@ HdlcInterface::~HdlcInterface(void)
|
||||
|
||||
void HdlcInterface::Deinit(void)
|
||||
{
|
||||
VerifyOrExit(mSockFd != -1);
|
||||
|
||||
VerifyOrExit(0 == close(mSockFd), perror("close RCP"));
|
||||
VerifyOrExit(-1 != wait(nullptr) || errno == ECHILD, perror("wait RCP"));
|
||||
|
||||
mSockFd = -1;
|
||||
|
||||
exit:
|
||||
return;
|
||||
CloseFile();
|
||||
}
|
||||
|
||||
void HdlcInterface::Read(void)
|
||||
@@ -586,6 +584,19 @@ exit:
|
||||
return fd;
|
||||
}
|
||||
|
||||
void HdlcInterface::CloseFile(void)
|
||||
{
|
||||
VerifyOrExit(mSockFd != -1);
|
||||
|
||||
VerifyOrExit(0 == close(mSockFd), perror("close RCP"));
|
||||
VerifyOrExit(-1 != wait(nullptr) || errno == ECHILD, perror("wait RCP"));
|
||||
|
||||
mSockFd = -1;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
|
||||
int HdlcInterface::ForkPty(const Url::Url &aRadioUrl)
|
||||
{
|
||||
@@ -656,6 +667,91 @@ void HdlcInterface::HandleHdlcFrame(otError aError)
|
||||
}
|
||||
}
|
||||
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
otError HdlcInterface::ResetConnection(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(mRadioUrl != nullptr, error = OT_ERROR_FAILED);
|
||||
SuccessOrExit(error = WaitForUsbDevice(mRadioUrl->GetPath()));
|
||||
|
||||
CloseFile();
|
||||
|
||||
mSockFd = OpenFile(*mRadioUrl);
|
||||
VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError HdlcInterface::WaitForUsbDevice(const char *aRadioUrlPath)
|
||||
{
|
||||
int fd;
|
||||
uint64_t end;
|
||||
struct udev_monitor *mon;
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
struct udev *udev = udev_new();
|
||||
VerifyOrExit(udev != nullptr, error = OT_ERROR_FAILED);
|
||||
|
||||
mon = udev_monitor_new_from_netlink(udev, "udev");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(mon, "tty", NULL);
|
||||
udev_monitor_enable_receiving(mon);
|
||||
fd = udev_monitor_get_fd(mon);
|
||||
|
||||
// wait maximally 10 seconds
|
||||
end = otPlatTimeGet() + 10 * US_PER_S;
|
||||
do
|
||||
{
|
||||
int ret;
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100 * US_PER_MS;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
ret = select(fd + 1, &fds, NULL, NULL, &tv);
|
||||
if (ret > 0 && FD_ISSET(fd, &fds))
|
||||
{
|
||||
struct udev_device *dev = udev_monitor_receive_device(mon);
|
||||
if (dev)
|
||||
{
|
||||
const char *action = udev_device_get_action(dev);
|
||||
VerifyOrExit(action != nullptr, error = OT_ERROR_FAILED);
|
||||
if (strcmp(action, "add") == 0)
|
||||
{
|
||||
struct udev_list_entry *entry;
|
||||
udev_list_entry_foreach(entry, udev_device_get_devlinks_list_entry(dev))
|
||||
{
|
||||
const char *name = udev_list_entry_get_name(entry);
|
||||
VerifyOrExit(name != nullptr, error = OT_ERROR_FAILED);
|
||||
if (strcmp(name, aRadioUrlPath) == 0)
|
||||
{
|
||||
udev_device_unref(dev);
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
} while (end > otPlatTimeGet());
|
||||
|
||||
error = OT_ERROR_FAILED;
|
||||
|
||||
exit:
|
||||
if (udev)
|
||||
{
|
||||
udev_unref(udev);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
#endif // OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
|
||||
} // namespace Posix
|
||||
} // namespace ot
|
||||
#endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "openthread-posix-config.h"
|
||||
#include "platform-posix.h"
|
||||
#include "lib/hdlc/hdlc.hpp"
|
||||
#include "lib/spinel/openthread-spinel-config.h"
|
||||
#include "lib/spinel/spinel_interface.hpp"
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART
|
||||
@@ -162,6 +163,14 @@ public:
|
||||
*/
|
||||
void OnRcpReset(void);
|
||||
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
/**
|
||||
* This method is called when RCP is reset to recreate the connection with it.
|
||||
*
|
||||
*/
|
||||
otError ResetConnection(void);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* This method instructs `HdlcInterface` to read and decode data from radio over the socket.
|
||||
@@ -212,7 +221,37 @@ private:
|
||||
static void HandleHdlcFrame(void *aContext, otError aError);
|
||||
void HandleHdlcFrame(otError aError);
|
||||
|
||||
/**
|
||||
* This method opens file specified by aRadioUrl.
|
||||
*
|
||||
* @param[in] aRadioUrl A reference to object containing path to file and data for configuring
|
||||
* the connection with tty type file.
|
||||
*
|
||||
* @retval The file descriptor of newly opened file.
|
||||
*/
|
||||
int OpenFile(const Url::Url &aRadioUrl);
|
||||
|
||||
/**
|
||||
* This method closes file associated with the file descriptor.
|
||||
*
|
||||
*/
|
||||
void CloseFile(void);
|
||||
|
||||
#if OPENTHREAD_SPINEL_CONFIG_RESET_CONNECTION
|
||||
/**
|
||||
* This method waits until enumeration of RCP(USB CDC ACM) device ends.
|
||||
*
|
||||
* This is blocking call, this method waits for up to 10 seconds.
|
||||
*
|
||||
* @param[in] aRadioUrlPath A path to RCP device.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The RCP device has been added to the host OS before timeout ends.
|
||||
* @retval OT_ERROR_FAILED The RCP device has not been added to the host OS before timeout ends.
|
||||
*
|
||||
*/
|
||||
otError WaitForUsbDevice(const char *aRadioUrlPath);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
|
||||
static int ForkPty(const Url::Url &aRadioUrl);
|
||||
#endif
|
||||
@@ -227,9 +266,10 @@ private:
|
||||
void * mReceiveFrameContext;
|
||||
Spinel::SpinelInterface::RxFrameBuffer & mReceiveFrameBuffer;
|
||||
|
||||
int mSockFd;
|
||||
uint32_t mBaudRate;
|
||||
Hdlc::Decoder mHdlcDecoder;
|
||||
int mSockFd;
|
||||
uint32_t mBaudRate;
|
||||
Hdlc::Decoder mHdlcDecoder;
|
||||
const Url::Url *mRadioUrl;
|
||||
|
||||
// Non-copyable, intentionally not implemented.
|
||||
HdlcInterface(const HdlcInterface &);
|
||||
|
||||
@@ -110,8 +110,10 @@ static const char *getTrelRadioUrl(otPlatformConfig *aPlatformConfig)
|
||||
|
||||
otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
|
||||
{
|
||||
otInstance * instance = nullptr;
|
||||
ot::Posix::RadioUrl radioUrl(get802154RadioUrl(aPlatformConfig));
|
||||
otInstance *instance = nullptr;
|
||||
// radioUrl must be static to prevent from having dangling pointers after this function call ends
|
||||
// and we still need to refer to the radioUrl from HdlcInterface object
|
||||
static ot::Posix::RadioUrl radioUrl(get802154RadioUrl(aPlatformConfig));
|
||||
|
||||
#if OPENTHREAD_POSIX_VIRTUAL_TIME
|
||||
// The last argument must be the node id
|
||||
|
||||
Reference in New Issue
Block a user