From a3b8361a19d7829ceeac1fc0b846645cdce6b52b Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 10 Dec 2025 00:48:37 +0800 Subject: [PATCH] [simulation] set virtual UART at runtime (#12199) This commit changes the virtual UART to be configured at runtime, which eliminates a build variants, which causes code coverage process problem. --- .github/workflows/simulation-1.4.yml | 2 +- .../simulation/platform-simulation.h | 11 ++++ examples/platforms/simulation/simul_utils.h | 2 + examples/platforms/simulation/uart.c | 64 +++++++++++++++++-- .../simulation/virtual_time/platform-sim.c | 40 ++++++------ script/test | 9 --- tests/scripts/thread-cert/node.py | 27 ++++---- 7 files changed, 104 insertions(+), 51 deletions(-) diff --git a/.github/workflows/simulation-1.4.yml b/.github/workflows/simulation-1.4.yml index ea66c433a..a1bdf14af 100644 --- a/.github/workflows/simulation-1.4.yml +++ b/.github/workflows/simulation-1.4.yml @@ -351,7 +351,7 @@ jobs: retention-days: 1 thread-1-4-posix: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: COVERAGE: 1 PYTHONUNBUFFERED: 1 diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index a985b68fb..2fad9d1ae 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -354,4 +354,15 @@ void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct time */ void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet); +/** + * Send bytes over Virtual UART. + * + * @param[in] aData A pointer to the data buffer. + * @param[in] aLength Number of bytes to transmit. + * + * @retval OT_ERROR_NONE Successfully started transmission. + * @retval OT_ERROR_FAILED Failed to start the transmission. + */ +otError platformUartSendVirtual(const uint8_t *aData, uint16_t aLength); + #endif // PLATFORM_SIMULATION_H_ diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h index bc36f80ff..05e86f53e 100644 --- a/examples/platforms/simulation/simul_utils.h +++ b/examples/platforms/simulation/simul_utils.h @@ -53,6 +53,8 @@ typedef struct utilsSocket extern const char *gLocalInterface; ///< Local interface name or address to use for sockets +extern bool gVirtualUart; ///< Whether to use Virtual UART or not. + /** * Adds a file descriptor (FD) to a given FD set. * diff --git a/examples/platforms/simulation/uart.c b/examples/platforms/simulation/uart.c index aed440bde..fb44b6048 100644 --- a/examples/platforms/simulation/uart.c +++ b/examples/platforms/simulation/uart.c @@ -45,13 +45,15 @@ #include "utils/code_utils.h" #include "utils/uart.h" -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0 +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME +bool gVirtualUart = ((OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART) == 1); +#endif static uint8_t s_receive_buffer[128]; static const uint8_t *s_write_buffer; static uint16_t s_write_length; -static int s_in_fd; -static int s_out_fd; +static int s_in_fd = -1; +static int s_out_fd = -1; static struct termios original_stdin_termios; static struct termios original_stdout_termios; @@ -62,9 +64,18 @@ static void restore_stdout_termios(void) { tcsetattr(s_out_fd, TCSAFLUSH, &origi void platformUartRestore(void) { +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + restore_stdin_termios(); restore_stdout_termios(); dup2(s_out_fd, STDOUT_FILENO); + +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME +exit: + return; +#endif } otError otPlatUartEnable(void) @@ -72,6 +83,10 @@ otError otPlatUartEnable(void) otError error = OT_ERROR_NONE; struct termios termios; +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + s_in_fd = dup(STDIN_FILENO); s_out_fd = dup(STDOUT_FILENO); dup2(STDERR_FILENO, STDOUT_FILENO); @@ -144,8 +159,14 @@ otError otPlatUartEnable(void) return error; exit: - close(s_in_fd); - close(s_out_fd); + if (s_in_fd != -1) + { + close(s_in_fd); + } + if (s_out_fd != -1) + { + close(s_out_fd); + } return error; } @@ -153,9 +174,16 @@ otError otPlatUartDisable(void) { otError error = OT_ERROR_NONE; +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + close(s_in_fd); close(s_out_fd); +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME +exit: +#endif return error; } @@ -163,6 +191,10 @@ otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) { otError error = OT_ERROR_NONE; +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT_ACTION(!gVirtualUart, error = platformUartSendVirtual(aBuf, aBufLength)); +#endif + otEXPECT_ACTION(s_write_length == 0, error = OT_ERROR_BUSY); s_write_buffer = aBuf; @@ -174,6 +206,10 @@ exit: void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) { +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd); utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd); @@ -182,6 +218,11 @@ void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aE utilsAddFdToFdSet(s_out_fd, aWriteFdSet, aMaxFd); utilsAddFdToFdSet(s_out_fd, aErrorFdSet, aMaxFd); } + +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME +exit: + return; +#endif } otError otPlatUartFlush(void) @@ -189,6 +230,10 @@ otError otPlatUartFlush(void) otError error = OT_ERROR_NONE; ssize_t count; +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + otEXPECT_ACTION(s_write_buffer != NULL && s_write_length > 0, error = OT_ERROR_INVALID_STATE); while ((count = write(s_out_fd, s_write_buffer, s_write_length)) > 0 && (s_write_length -= count) > 0) @@ -220,6 +265,10 @@ void platformUartProcess(void) {s_out_fd, POLLOUT | error_flags, 0}, }; +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME + otEXPECT(!gVirtualUart); +#endif + errno = 0; rval = poll(pollfd, sizeof(pollfd) / sizeof(*pollfd), 0); @@ -278,8 +327,11 @@ void platformUartProcess(void) } } } +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME +exit: + return; +#endif } -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0 #if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART && (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) diff --git a/examples/platforms/simulation/virtual_time/platform-sim.c b/examples/platforms/simulation/virtual_time/platform-sim.c index 192552684..637697462 100644 --- a/examples/platforms/simulation/virtual_time/platform-sim.c +++ b/examples/platforms/simulation/virtual_time/platform-sim.c @@ -50,6 +50,7 @@ #include #include +#include "../simul_utils.h" #include "lib/platform/exit_code.h" #include "utils/uart.h" @@ -148,14 +149,7 @@ static void platformSendSleepEvent(void) otSimSendEvent(&event); } -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART -void platformUartRestore(void) {} - -otError otPlatUartEnable(void) { return OT_ERROR_NONE; } - -otError otPlatUartDisable(void) { return OT_ERROR_NONE; } - -otError otPlatUartSend(const uint8_t *aData, uint16_t aLength) +otError platformUartSendVirtual(const uint8_t *aData, uint16_t aLength) { otError error = OT_ERROR_NONE; struct Event event; @@ -173,9 +167,6 @@ otError otPlatUartSend(const uint8_t *aData, uint16_t aLength) return error; } -otError otPlatUartFlush(void) { return OT_ERROR_NONE; } -#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART - static void socket_init(void) { { @@ -247,6 +238,7 @@ static void socket_init(void) void otSysInit(int argc, char *argv[]) { char *endptr; + int argi = 1; if (gPlatformPseudoResetWasRequested) { @@ -254,22 +246,30 @@ void otSysInit(int argc, char *argv[]) return; } - if (argc != 2) - { - DieNow(OT_EXIT_FAILURE); - } - openlog(basename(argv[0]), LOG_PID, LOG_USER); setlogmask(setlogmask(0) & LOG_UPTO(LOG_NOTICE)); gArgumentsCount = argc; gArguments = argv; - gNodeId = (uint32_t)strtol(argv[1], &endptr, 0); + if (!strcmp(argv[argi], "-U")) + { + gVirtualUart = true; + ++argi; + } + + gNodeId = (uint32_t)strtol(argv[argi], &endptr, 0); if (*endptr != '\0' || gNodeId < 1 || gNodeId > MAX_NETWORK_SIZE) { - fprintf(stderr, "Invalid NodeId: %s\n", argv[1]); + fprintf(stderr, "Invalid NodeId: %s\n", argv[argi]); + DieNow(OT_EXIT_FAILURE); + } + + ++argi; + + if (argi != argc) + { DieNow(OT_EXIT_FAILURE); } @@ -307,9 +307,7 @@ void otSysProcessDrivers(otInstance *aInstance) FD_SET(sSockFd, &read_fds); max_fd = sSockFd; -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0 platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); -#endif if (!otTaskletsArePending(aInstance) && platformAlarmGetNext() > 0 && !platformRadioIsTransmitPending()) { @@ -331,9 +329,7 @@ void otSysProcessDrivers(otInstance *aInstance) platformAlarmProcess(aInstance); platformRadioProcess(aInstance, &read_fds, &write_fds); -#if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART == 0 platformUartProcess(); -#endif } #if OPENTHREAD_CONFIG_OTNS_ENABLE diff --git a/script/test b/script/test index 6c72d8375..c03969620 100755 --- a/script/test +++ b/script/test @@ -164,20 +164,11 @@ build_simulation() OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" - if [[ ${VIRTUAL_TIME} == 1 ]] && [[ ${OT_NODE_TYPE} == rcp* ]]; then - OT_CMAKE_NINJA_TARGET=ot-rcp OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" "-DOT_SIMULATION_VIRTUAL_TIME_UART=ON" - fi - if [[ ${version} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then options+=("-DOT_BACKBONE_ROUTER=ON") OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}-bbr" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" - - if [[ ${VIRTUAL_TIME} == 1 ]] && [[ ${OT_NODE_TYPE} == rcp* ]]; then - OT_CMAKE_NINJA_TARGET=ot-rcp OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}-bbr" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" "-DOT_SIMULATION_VIRTUAL_TIME_UART=ON" - fi - fi } diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index b4dd24a83..51311a633 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -91,11 +91,12 @@ class OtbrDocker: logging.info(f"socat running: device PTY: {rcp_device_pty}, device: {rcp_device}") ot_rcp_path = self._get_ot_rcp_path() - self._ot_rcp_proc = subprocess.Popen(f"{ot_rcp_path} {nodeid} > {rcp_device_pty} < {rcp_device_pty}", - shell=True, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + self._ot_rcp_proc = subprocess.Popen( + f"{ot_rcp_path} {'-U' if config.VIRTUAL_TIME else ''} {nodeid} > {rcp_device_pty} < {rcp_device_pty}", + shell=True, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) try: self._ot_rcp_proc.wait(1) @@ -644,8 +645,8 @@ class OtCli: cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode) if 'RADIO_DEVICE' in os.environ: - cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE'], - nodeid) + cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?%sforkpty-arg=%d' % ( + os.environ['RADIO_DEVICE'], 'forkpty-arg=-U&' if config.VIRTUAL_TIME else '', nodeid) self.is_posix = True else: cmd += ' %d' % nodeid @@ -660,8 +661,8 @@ class OtCli: cmd = '%s/examples/apps/cli/ot-cli-%s' % (srcdir, mode) if 'RADIO_DEVICE_1_1' in os.environ: - cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?forkpty-arg=%d' % ( - os.environ['RADIO_DEVICE_1_1'], nodeid) + cmd += ' --real-time-signal=+1 -v spinel+hdlc+uart://%s?%sforkpty-arg=%d' % ( + os.environ['RADIO_DEVICE_1_1'], 'forkpty-arg=-U&' if config.VIRTUAL_TIME else '', nodeid) self.is_posix = True else: cmd += ' %d' % nodeid @@ -689,8 +690,8 @@ class OtCli: # If Thread version of node matches the testing environment version. if self.version == self.env_version: if 'RADIO_DEVICE' in os.environ: - args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE'], - nodeid) + args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?%sforkpty-arg=%d' % ( + os.environ['RADIO_DEVICE'], 'forkpty-arg=-U&' if config.VIRTUAL_TIME else '', nodeid) self.is_posix = True else: args = '' @@ -729,8 +730,8 @@ class OtCli: # Load Thread 1.1 node when testing Thread 1.2 scenarios for interoperability. elif self.version == '1.1': if 'RADIO_DEVICE_1_1' in os.environ: - args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?forkpty-arg=%d' % (os.environ['RADIO_DEVICE_1_1'], - nodeid) + args = ' --real-time-signal=+1 spinel+hdlc+uart://%s?%sforkpty-arg=%d' % ( + os.environ['RADIO_DEVICE_1_1'], 'forkpty-arg=-U&' if config.VIRTUAL_TIME else '', nodeid) self.is_posix = True else: args = ''