[simulation] add APIs for select() based event loop (#12135)

This commit adds select API so that simulation and be integrated with
other mainloops.

This commit also adds a flag to disable UART on simulation platform.
This commit is contained in:
Yakun Xu
2025-11-19 14:19:04 +08:00
committed by GitHub
parent 76f3418796
commit 3e0920c575
4 changed files with 161 additions and 37 deletions
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2025, 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
* @brief
* This file defines the APIs for integrating with select() based event loop.
*/
#ifndef OPENTHREAD_SELECT_H_
#define OPENTHREAD_SELECT_H_
#include <sys/select.h>
#include <openthread/instance.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Updates the file descriptor sets with file descriptors used by OpenThread drivers.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in,out] aMaxFd A pointer to the max file descriptor.
* @param[in,out] aReadFdSet A pointer to the read file descriptors, which may already contain some FDs.
* @param[in,out] aWriteFdSet A pointer to the write file descriptors, which may already contain some FDs.
* @param[in,out] aErrorFdSet A pointer to the error file descriptors, which may already contain some FDs.
* @param[in,out] aTimeout A pointer to an initialized timeout. The caller must initialize this to the maximum.
* desired timeout before calling this function; the function may reduce the value, but
* will not increase it. The output should be no larger than the input.
*/
void otSysUpdateEvents(otInstance *aInstance,
int *aMaxFd,
fd_set *aReadFdSet,
fd_set *aWriteFdSet,
fd_set *aErrorFdSet,
struct timeval *aTimeout);
/**
* Performs all platform-specific processing for OpenThread's example applications.
*
* @note This function is not called by the OpenThread library. Instead, the system/RTOS should call this function
* in the main loop when processing OpenThread's drivers is most appropriate.
* @note This should only be called when the fd_set are meaningful, that is, the select() call was successful.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aReadFdSet A pointer to the read file descriptors.
* @param[in] aWriteFdSet A pointer to the write file descriptors.
* @param[in] aErrorFdSet A pointer to the error file descriptors.
*/
void otSysProcessEvents(otInstance *aInstance,
const fd_set *aReadFdSet,
const fd_set *aWriteFdSet,
const fd_set *aErrorFdSet);
#ifdef __cplusplus
} // end of extern "C"
#endif
#endif // OPENTHREAD_SELECT_H_
+1 -1
View File
@@ -50,7 +50,7 @@ extern "C" {
* @param[in] argc Number of arguments in @p argv.
* @param[in] argv Argument vector.
*/
void otSysInit(int argc, char *argv[]);
void otSysInit(int aArgCount, char *aArgVector[]);
/**
* Performs all platform-specific deinitialization for OpenThread's drivers.
@@ -40,6 +40,15 @@
#define OPENTHREAD_SIMULATION_UART_BAUDRATE B115200
#endif
/**
* @def OPENTHREAD_SIMULATION_UART_ENABLE
*
* Define as 1 to enable UART transport.
*/
#ifndef OPENTHREAD_SIMULATION_UART_ENABLE
#define OPENTHREAD_SIMULATION_UART_ENABLE 1
#endif
/**
* @def OPENTHREAD_SIMULATION_VIRTUAL_TIME
*
+65 -36
View File
@@ -45,12 +45,15 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <openthread-select.h>
#include <openthread-system.h>
#include <openthread/tasklet.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/toolchain.h>
#include "simul_utils.h"
@@ -221,67 +224,93 @@ void otSysDeinit(void)
void otSysProcessDrivers(otInstance *aInstance)
{
fd_set read_fds;
fd_set write_fds;
fd_set error_fds;
int max_fd = -1;
fd_set readFdSet;
fd_set writeFdSet;
fd_set errorFdSet;
int maxFd = -1;
struct timeval timeout;
int rval;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&error_fds);
FD_ZERO(&readFdSet);
FD_ZERO(&writeFdSet);
FD_ZERO(&errorFdSet);
platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd);
platformAlarmUpdateTimeout(&timeout);
platformRadioUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
otSysUpdateEvents(aInstance, &maxFd, &readFdSet, &writeFdSet, &errorFdSet, &timeout);
if (select(maxFd + 1, &readFdSet, &writeFdSet, &errorFdSet, &timeout) < 0)
{
if (errno != EINTR)
{
perror("select");
exit(EXIT_FAILURE);
}
FD_ZERO(&readFdSet);
FD_ZERO(&writeFdSet);
FD_ZERO(&errorFdSet);
}
otSysProcessEvents(aInstance, &readFdSet, &writeFdSet, &errorFdSet);
}
void otSysUpdateEvents(otInstance *aInstance,
int *aMaxFd,
fd_set *aReadFdSet,
fd_set *aWriteFdSet,
fd_set *aErrorFdSet,
struct timeval *aTimeout)
{
OT_UNUSED_VARIABLE(aErrorFdSet);
#if OPENTHREAD_SIMULATION_UART_ENABLE
platformUartUpdateFdSet(aReadFdSet, aWriteFdSet, aErrorFdSet, aMaxFd);
#endif
platformAlarmUpdateTimeout(aTimeout);
platformRadioUpdateFdSet(aReadFdSet, aWriteFdSet, aTimeout, aMaxFd);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
platformTrelUpdateFdSet(aReadFdSet, aWriteFdSet, aTimeout, aMaxFd);
#endif
#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfUpdateFdSet(&read_fds, &write_fds, &max_fd);
platformInfraIfUpdateFdSet(aReadFdSet, aWriteFdSet, aMaxFd);
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
platformMdnsSocketUpdateFdSet(&read_fds, &max_fd);
platformMdnsSocketUpdateFdSet(aReadFdSet, aMaxFd);
#endif
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
platformBleUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
platformBleUpdateFdSet(aReadFdSet, aWriteFdSet, aTimeout, aMaxFd);
#endif
if (otTaskletsArePending(aInstance))
{
timeout.tv_sec = 0;
timeout.tv_usec = 0;
aTimeout->tv_sec = 0;
aTimeout->tv_usec = 0;
}
}
rval = select(max_fd + 1, &read_fds, &write_fds, &error_fds, &timeout);
void otSysProcessEvents(otInstance *aInstance,
const fd_set *aReadFdSet,
const fd_set *aWriteFdSet,
const fd_set *aErrorFdSet)
{
OT_UNUSED_VARIABLE(aErrorFdSet);
if (rval >= 0)
{
platformUartProcess();
platformRadioProcess(aInstance, &read_fds, &write_fds);
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
platformBleProcess(aInstance, &read_fds, &write_fds);
#if OPENTHREAD_SIMULATION_UART_ENABLE
platformUartProcess();
#endif
platformRadioProcess(aInstance, aReadFdSet, aWriteFdSet);
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
platformBleProcess(aInstance, aReadFdSet, aWriteFdSet);
#endif
}
else if (errno != EINTR)
{
perror("select");
exit(EXIT_FAILURE);
}
platformAlarmProcess(aInstance);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelProcess(aInstance, &read_fds, &write_fds);
platformTrelProcess(aInstance, aReadFdSet, aWriteFdSet);
#endif
#if OPENTHREAD_SIMULATION_IMPLEMENT_INFRA_IF && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfProcess(aInstance, &read_fds, &write_fds);
platformInfraIfProcess(aInstance, aReadFdSet, aWriteFdSet);
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_SIMULATION_MDNS_SOCKET_IMPLEMENT_POSIX
platformMdnsSocketProcess(aInstance, &read_fds);
platformMdnsSocketProcess(aInstance, aReadFdSet);
#endif
if (gTerminate)
{
exit(0);