diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index 4e7970b22..9379421d4 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -163,10 +163,11 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen * * @param[in,out] aReadFdSet A pointer to the read file descriptors. * @param[in,out] aWriteFdSet A pointer to the write file descriptors. + * @param[in,out] aTimeout A pointer to the timeout. * @param[in,out] aMaxFd A pointer to the max file descriptor. * */ -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd); +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd); /** * This function performs radio driver processing. diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 2faaa2f5f..2b627c2f2 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -29,6 +29,7 @@ #include "platform-simulation.h" #include +#include #include #include @@ -47,6 +48,9 @@ // The IPv4 group for receiving packets of radio simulation #define OT_RADIO_GROUP "224.0.0.116" +#define MS_PER_S 1000 +#define US_PER_MS 1000 + enum { IEEE802154_ACK_LENGTH = 5, @@ -75,6 +79,10 @@ static uint16_t sPortOffset = 0; static uint16_t sPort = 0; #endif +static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID; +static bool sEnergyScanning = false; +static uint32_t sEnergyScanEndTime = 0; + enum { SIM_RADIO_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN, @@ -156,6 +164,13 @@ static otMacKeyMaterial sCurrKey; static otMacKeyMaterial sNextKey; static otRadioKeyType sKeyType; +static int8_t GetRssi(uint16_t aChannel); + +static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) +{ + return (aTimeA - aTimeB) < (1U << 31); +} + static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrigin) { for (size_t i = 0; i < sizeof(*aReversed); i++) @@ -493,20 +508,23 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) int8_t otPlatRadioGetRssi(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - assert(aInstance != NULL); - int8_t rssi = SIM_LOW_RSSI_SAMPLE; - uint8_t channel = sReceiveFrame.mChannel; + return GetRssi(sReceiveFrame.mChannel); +} + +static int8_t GetRssi(uint16_t aChannel) +{ + int8_t rssi = SIM_LOW_RSSI_SAMPLE; uint32_t probabilityThreshold; - otEXPECT((SIM_RADIO_CHANNEL_MIN <= channel) && channel <= (SIM_RADIO_CHANNEL_MAX)); + otEXPECT((SIM_RADIO_CHANNEL_MIN <= aChannel) && aChannel <= (SIM_RADIO_CHANNEL_MAX)); // To emulate a simple interference model, we return either a high or // a low RSSI value with a fixed probability per each channel. The // probability is increased per channel by a constant. - probabilityThreshold = (channel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; + probabilityThreshold = (aChannel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; if (otRandomNonCryptoGetUint16() < (probabilityThreshold * 0xffff / 100)) { @@ -736,7 +754,7 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen radioReceive(aInstance); } #else -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd) +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)) { @@ -757,6 +775,25 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMax *aMaxFd = sTxFd; } } + + if (sEnergyScanning) + { + struct timeval tv = {0, 0}; + uint32_t now = otPlatAlarmMilliGetNow(); + + if (IsTimeAfterOrEqual(sEnergyScanEndTime, now)) + { + uint32_t remaining = sEnergyScanEndTime - now; + + tv.tv_sec = remaining / MS_PER_S; + tv.tv_usec = (remaining % MS_PER_S) * US_PER_MS; + } + + if (timercmp(&tv, aTimeout, <)) + { + *aTimeout = tv; + } + } } // no need to close in virtual time mode. @@ -811,11 +848,16 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const } } #endif - if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); } + + if (sEnergyScanning && IsTimeAfterOrEqual(otPlatAlarmMilliGetNow(), sEnergyScanEndTime)) + { + sEnergyScanning = false; + otPlatRadioEnergyScanDone(aInstance, sEnergyScanResult); + } } void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame) @@ -982,13 +1024,22 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1 { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aScanChannel); - OT_UNUSED_VARIABLE(aScanDuration); + + otError error = OT_ERROR_NONE; assert(aInstance != NULL); assert(aScanChannel >= SIM_RADIO_CHANNEL_MIN && aScanChannel <= SIM_RADIO_CHANNEL_MAX); assert(aScanDuration > 0); - return OT_ERROR_NOT_IMPLEMENTED; + otEXPECT_ACTION((gRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN), error = OT_ERROR_NOT_IMPLEMENTED); + otEXPECT_ACTION(!sEnergyScanning, error = OT_ERROR_BUSY); + + sEnergyScanResult = GetRssi(aScanChannel); + sEnergyScanning = true; + sEnergyScanEndTime = otPlatAlarmMilliGetNow() + aScanDuration; + +exit: + return error; } otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index 57bb30cd4..f9282a64a 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -70,10 +70,11 @@ static void handleSignal(int aSignal) */ enum { - OT_SIM_OPT_HELP = 'h', - OT_SIM_OPT_SLEEP_TO_TX = 't', - OT_SIM_OPT_TIME_SPEED = 's', - OT_SIM_OPT_UNKNOWN = '?', + OT_SIM_OPT_HELP = 'h', + OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E', + OT_SIM_OPT_SLEEP_TO_TX = 't', + OT_SIM_OPT_TIME_SPEED = 's', + OT_SIM_OPT_UNKNOWN = '?', }; static void PrintUsage(const char *aProgramName, int aExitCode) @@ -82,9 +83,10 @@ static void PrintUsage(const char *aProgramName, int aExitCode) "Syntax:\n" " %s [Options] NodeId\n" "Options:\n" - " -h --help Display this usage information.\n" - " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" - " -s --time-speed=val Speed up the time in simulation.\n", + " -h --help Display this usage information.\n" + " -E --enable-energy-scan Enable energy scan capability.\n" + " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" + " -s --time-speed=val Speed up the time in simulation.\n", aProgramName); exit(aExitCode); @@ -97,6 +99,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) static const struct option long_options[] = { {"help", no_argument, 0, OT_SIM_OPT_HELP}, + {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED}, {0, 0, 0, 0}, @@ -112,7 +115,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) while (true) { - int c = getopt_long(aArgCount, aArgVector, "hts:", long_options, NULL); + int c = getopt_long(aArgCount, aArgVector, "Ehts:", long_options, NULL); if (c == -1) { @@ -127,6 +130,9 @@ void otSysInit(int aArgCount, char *aArgVector[]) case OT_SIM_OPT_HELP: PrintUsage(aArgVector[0], EXIT_SUCCESS); break; + case OT_SIM_OPT_ENABLE_ENERGY_SCAN: + gRadioCaps |= OT_RADIO_CAPS_ENERGY_SCAN; + break; case OT_SIM_OPT_SLEEP_TO_TX: gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX; break; @@ -197,8 +203,8 @@ void otSysProcessDrivers(otInstance *aInstance) FD_ZERO(&error_fds); platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); - platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd); platformAlarmUpdateTimeout(&timeout); + platformRadioUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #endif diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f917e7f77..5bf972369 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (208) +#define OPENTHREAD_API_VERSION (209) /** * @addtogroup api-instance diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 116fea654..02293e990 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -236,6 +236,7 @@ typedef void (*otLinkRawEnergyScanDone)(otInstance *aInstance, int8_t aEnergySca * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. * diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index ba7314ebb..e3d0dda25 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -857,6 +857,7 @@ int8_t otPlatRadioGetRssi(otInstance *aInstance); * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * */ diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 16204b9e6..e0a66c99f 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -164,6 +164,7 @@ public: * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorNotImplemented The radio doesn't support energy scanning. * @retval kErrorInvalidState If the raw link-layer isn't enabled. * diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index ff6821cf5..85aa0e54b 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -572,6 +572,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported by radio link. * diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 4e243e97b..da0dbc6ae 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -379,6 +379,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported (applicable in link-raw/radio mode only). * diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 10e5fd1c9..aa7233e18 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -522,6 +522,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorNotImplemented The radio doesn't support energy scanning. * */ diff --git a/tests/scripts/expect/posix-rcp-energy-scan.exp b/tests/scripts/expect/posix-rcp-energy-scan.exp new file mode 100755 index 000000000..93a7a2ade --- /dev/null +++ b/tests/scripts/expect/posix-rcp-energy-scan.exp @@ -0,0 +1,42 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2022, 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. +# + +source "tests/scripts/expect/_common.exp" + +spawn_node 1 "rcp" "spinel+hdlc+forkpty://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1&forkpty-arg=-E&no-reset=1" + +send "ifconfig up\n" +expect_line "Done" +# Actually returns last otPlatRadioGetRssi +send "scan energy 0\n" +expect_line "Done" +# Actually performs energy scan on RCP +send "scan energy 100\n" +expect_line "Done" +dispose_all