mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[efr32] add sleepy demo for efr32mg21 (#4138)
Sleepy Demo added to the efr32mg21 directory.
Updated efr32mg21 alarm.c to use changes for efr32mg12 in commit 89dca58.
This commit is contained in:
committed by
Jonathan Hui
parent
ef4adde7be
commit
87105e0694
@@ -1075,6 +1075,9 @@ examples/platforms/efr32mg12/sleepy-demo/Makefile
|
||||
examples/platforms/efr32mg12/sleepy-demo/sleepy-demo-ftd/Makefile
|
||||
examples/platforms/efr32mg12/sleepy-demo/sleepy-demo-mtd/Makefile
|
||||
examples/platforms/efr32mg21/Makefile
|
||||
examples/platforms/efr32mg21/sleepy-demo/Makefile
|
||||
examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-ftd/Makefile
|
||||
examples/platforms/efr32mg21/sleepy-demo/sleepy-demo-mtd/Makefile
|
||||
examples/platforms/gp712/Makefile
|
||||
examples/platforms/kw41z/Makefile
|
||||
examples/platforms/nrf52811/Makefile
|
||||
|
||||
@@ -222,7 +222,7 @@ static void efr32RailConfigLoad(efr32BandConfig *aBandConfig)
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
|
||||
static void efr32RadioSetTxPower(uint8_t aPowerDbm)
|
||||
static void efr32RadioSetTxPower(int8_t aPowerDbm)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
RAIL_TxPowerCurvesConfig_t txPowerCurvesConfig = {curves24Hp, curvesSg, curves24Lp, piecewiseSegments};
|
||||
|
||||
@@ -45,6 +45,13 @@ EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs
|
||||
libopenthread_efr32mg21_a_CPPFLAGS = \
|
||||
-D__START=main \
|
||||
-D__STARTUP_CLEAR_BSS \
|
||||
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
|
||||
-Wno-sign-compare \
|
||||
-DCORTEXM3 \
|
||||
-DPHY=EMBER_PHY_RAIL \
|
||||
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
|
||||
-DCORTEXM3_EFM32_MICRO \
|
||||
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/examples/platforms \
|
||||
-I$(top_srcdir)/examples/platforms/efr32mg21/$(EFR32_BOARD_DIR) \
|
||||
@@ -60,6 +67,11 @@ libopenthread_efr32mg21_a_CPPFLAGS =
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
|
||||
@@ -86,8 +98,10 @@ PLATFORM_SOURCES =
|
||||
logging.c \
|
||||
misc.c \
|
||||
openthread-core-efr32-config.h \
|
||||
openthread-core-efr32-config-check.h \
|
||||
platform-efr32.h \
|
||||
platform-band.h \
|
||||
radio.c \
|
||||
rail_config.h \
|
||||
startup-gcc.c \
|
||||
system.c \
|
||||
uart.c \
|
||||
@@ -104,7 +118,6 @@ libopenthread_efr32mg21_a_SOURCES =
|
||||
|
||||
PRETTY_FILES = \
|
||||
$(PLATFORM_SOURCES) \
|
||||
$(noinst_HEADERS) \
|
||||
$(NULL)
|
||||
|
||||
Dash = -
|
||||
@@ -112,4 +125,16 @@ libopenthread_efr32mg21_a_LIBADD
|
||||
$(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o") \
|
||||
$(shell find $(top_builddir)/third_party/jlink/SEGGER_RTT_V640/RTT $(Dash)type f $(Dash)name "*.o")
|
||||
|
||||
DIST_SUBDIRS = \
|
||||
sleepy-demo \
|
||||
$(NULL)
|
||||
|
||||
SUBDIRS = \
|
||||
sleepy-demo \
|
||||
$(NULL)
|
||||
|
||||
PRETTY_SUBDIRS = \
|
||||
sleepy-demo \
|
||||
$(NULL)
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
|
||||
@@ -32,13 +32,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "openthread-system.h"
|
||||
#include <openthread/config.h>
|
||||
#include <openthread/platform/alarm-milli.h>
|
||||
#include <openthread/platform/diag.h>
|
||||
#include "common/logging.hpp"
|
||||
|
||||
#include "platform-efr32.h"
|
||||
#include "utils/code_utils.h"
|
||||
|
||||
#include "em_core.h"
|
||||
@@ -47,12 +51,25 @@
|
||||
#define XTAL_ACCURACY 200
|
||||
#define US_IN_MS 1000
|
||||
|
||||
// Minimum duration of an alarm in milliseconds. Used to avoid setting the absolute
|
||||
// expiry time of an alarm to the current time or slightly in the past.
|
||||
#define TIMER_EPSILON_MS 1
|
||||
|
||||
// The longest Rail can set a timer is 53 minutes. Timers of a longer duration
|
||||
// must wake up before this and set another timer for the remainder. We currently
|
||||
// split long delays in 30 minute intervals using a value of 1800000.
|
||||
#define RAIL_TIMER_MAX_DELTA_MS 1800000
|
||||
|
||||
static uint32_t sTimerHi = 0;
|
||||
static uint32_t sTimerLo = 0;
|
||||
static uint32_t sAlarmT0 = 0;
|
||||
static uint32_t sAlarmDt = 0;
|
||||
static bool sIsRunning = false;
|
||||
|
||||
static void RAILCb_TimerExpired(RAIL_Handle_t aHandle)
|
||||
{
|
||||
}
|
||||
|
||||
void efr32AlarmInit(void)
|
||||
{
|
||||
}
|
||||
@@ -94,9 +111,47 @@ uint32_t otPlatTimeGetXtalAccuracy(void)
|
||||
void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
uint32_t expires_microsec;
|
||||
RAIL_Status_t status;
|
||||
|
||||
assert(gRailHandle != NULL);
|
||||
|
||||
if (sIsRunning)
|
||||
{
|
||||
RAIL_CancelTimer(gRailHandle);
|
||||
}
|
||||
|
||||
sAlarmT0 = t0;
|
||||
sAlarmDt = dt;
|
||||
|
||||
if (dt > RAIL_TIMER_MAX_DELTA_MS)
|
||||
{
|
||||
dt = RAIL_TIMER_MAX_DELTA_MS;
|
||||
}
|
||||
else if (dt < TIMER_EPSILON_MS)
|
||||
{
|
||||
dt = TIMER_EPSILON_MS;
|
||||
}
|
||||
|
||||
expires_microsec = (t0 + dt) * US_IN_MS;
|
||||
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
|
||||
|
||||
if (status != RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
// The RAIL timer could not be set due to expiration time being in the past with respect to RAIL's current
|
||||
// time which is in microseconds. We fallback to using a relative timer from the current time.
|
||||
|
||||
expires_microsec = dt * US_IN_MS;
|
||||
status = RAIL_SetTimer(gRailHandle, expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
|
||||
|
||||
if (status != RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
otLogCritPlat("Alarm start timer failed, status: %d, dt: %u, t0: %u, now: %u", status, dt, t0,
|
||||
otPlatAlarmMilliGetNow());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
sAlarmT0 = t0;
|
||||
sAlarmDt = dt;
|
||||
sIsRunning = true;
|
||||
}
|
||||
|
||||
@@ -105,48 +160,75 @@ void otPlatAlarmMilliStop(otInstance *aInstance)
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
sIsRunning = false;
|
||||
|
||||
assert(gRailHandle != NULL);
|
||||
RAIL_CancelTimer(gRailHandle);
|
||||
}
|
||||
|
||||
void efr32AlarmProcess(otInstance *aInstance)
|
||||
{
|
||||
uint32_t now = otPlatAlarmMilliGetNow();
|
||||
uint32_t expires;
|
||||
bool fire = false;
|
||||
uint32_t now;
|
||||
uint32_t new_expires_microsec;
|
||||
uint32_t dt;
|
||||
RAIL_Status_t status;
|
||||
|
||||
otEXPECT(sIsRunning);
|
||||
|
||||
expires = sAlarmT0 + sAlarmDt;
|
||||
assert(gRailHandle != NULL);
|
||||
|
||||
if (sAlarmT0 <= now)
|
||||
{
|
||||
fire = (expires >= sAlarmT0 && expires <= now);
|
||||
}
|
||||
else
|
||||
{
|
||||
fire = (expires >= sAlarmT0 || expires <= now);
|
||||
}
|
||||
|
||||
if (fire)
|
||||
if (RAIL_IsTimerExpired(gRailHandle))
|
||||
{
|
||||
sIsRunning = false;
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
|
||||
if (otPlatDiagModeGet())
|
||||
if (sAlarmDt > RAIL_TIMER_MAX_DELTA_MS)
|
||||
{
|
||||
otPlatDiagAlarmFired(aInstance);
|
||||
// We split longer delays in two due to the maximum allowed timer in RAIL. Here we
|
||||
// re-arm the RAIL timer with the remaining part of the alarm.
|
||||
|
||||
now = otPlatAlarmMilliGetNow();
|
||||
dt = (sAlarmT0 + sAlarmDt) - now;
|
||||
|
||||
if (dt > RAIL_TIMER_MAX_DELTA_MS)
|
||||
{
|
||||
dt = RAIL_TIMER_MAX_DELTA_MS;
|
||||
}
|
||||
else if (dt < TIMER_EPSILON_MS)
|
||||
{
|
||||
dt = TIMER_EPSILON_MS;
|
||||
}
|
||||
|
||||
new_expires_microsec = (now + dt) * US_IN_MS;
|
||||
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_ABSOLUTE, RAILCb_TimerExpired);
|
||||
|
||||
if (status != RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
new_expires_microsec = dt * US_IN_MS;
|
||||
status = RAIL_SetTimer(gRailHandle, new_expires_microsec, RAIL_TIME_DELAY, RAILCb_TimerExpired);
|
||||
|
||||
if (status != RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
otLogCritPlat("Alarm extend timer failed, status: %d, dt: %u, now: %u", status, dt,
|
||||
otPlatAlarmMilliGetNow());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
sIsRunning = true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
otPlatAlarmMilliFired(aInstance);
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
if (otPlatDiagModeGet())
|
||||
{
|
||||
otPlatDiagAlarmFired(aInstance);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
otPlatAlarmMilliFired(aInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void RAILCb_TimerExpired(void)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -41,13 +41,18 @@
|
||||
#include "rail_config.h"
|
||||
#include "rail_ieee802154.h"
|
||||
|
||||
#define RAIL_TX_FIFO_SIZE (OT_RADIO_FRAME_MAX_SIZE + 1)
|
||||
|
||||
typedef struct efr32CommonConfig
|
||||
{
|
||||
RAIL_Config_t mRailConfig;
|
||||
uint8_t
|
||||
mRailTxFifo[RAIL_TX_FIFO_SIZE]; // must be 2 power between 64 and 4096, and bigger than OT_RADIO_FRAME_MAX_SIZE
|
||||
} efr32CommonConfig;
|
||||
|
||||
typedef struct efr32BandConfig
|
||||
{
|
||||
RAIL_Handle_t mRailHandle;
|
||||
RAIL_Config_t mRailConfig;
|
||||
RAILSched_Config_t mRailSchedState;
|
||||
const RAIL_ChannelConfig_t *mChannelConfig;
|
||||
uint8_t mRailTxFifo[OT_RADIO_FRAME_MAX_SIZE + 1];
|
||||
uint8_t mChannelMin;
|
||||
uint8_t mChannelMax;
|
||||
} efr32BandConfig;
|
||||
|
||||
@@ -41,10 +41,14 @@
|
||||
#include "em_system.h"
|
||||
|
||||
#include "core_cm33.h"
|
||||
#include "rail.h"
|
||||
|
||||
// Global OpenThread instance structure
|
||||
extern otInstance *sInstance;
|
||||
|
||||
// Global reference to rail handle
|
||||
extern RAIL_Handle_t gRailHandle;
|
||||
|
||||
/**
|
||||
* This function initializes the alarm service used by OpenThread.
|
||||
*
|
||||
@@ -109,4 +113,26 @@ void efr32LogInit(void);
|
||||
*/
|
||||
void efr32LogDeinit(void);
|
||||
|
||||
/**
|
||||
* Registers the sleep callback handler. The callback is used to check that
|
||||
* the application has no work pending and that it is safe to put the EFR32
|
||||
* into a low energy sleep mode.
|
||||
*
|
||||
* The callback should return true if it is ok to enter sleep mode. Note
|
||||
* that the callback itself is run with interrupts disabled and so should
|
||||
* be kept as short as possible. Anny interrupt including those from timers
|
||||
* will wake the EFR32 out of sleep mode.
|
||||
*
|
||||
* @param[in] aCallback Callback function.
|
||||
*
|
||||
*/
|
||||
void efr32SetSleepCallback(bool (*aCallback)(void), void (*aCallbackWake)(void));
|
||||
|
||||
/**
|
||||
* Put the EFR32 into a low power mode. Before sleeping it will call a callback
|
||||
* in the application registered with efr32SetSleepCallback to ensure that there
|
||||
* is no outstanding work in the application to do.
|
||||
*/
|
||||
void efr32Sleep(void);
|
||||
|
||||
#endif // PLATFORM_EFR32_H_
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, The OpenThread Authors.
|
||||
* Copyright (c) 2019, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -45,8 +45,10 @@
|
||||
#include "utils/soft_source_match_table.h"
|
||||
|
||||
#include "board_config.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_core.h"
|
||||
#include "em_system.h"
|
||||
#include "hal-config.h"
|
||||
#include "openthread-core-efr32-config.h"
|
||||
#include "pa_conversions_efr32.h"
|
||||
#include "platform-band.h"
|
||||
@@ -69,7 +71,6 @@ enum
|
||||
enum
|
||||
{
|
||||
EFR32_RECEIVE_SENSITIVITY = -100, // dBm
|
||||
EFR32_RSSI_AVERAGING_TIME = 16, // us
|
||||
EFR32_RSSI_AVERAGING_TIMEOUT = 300, // us
|
||||
};
|
||||
|
||||
@@ -98,6 +99,8 @@ typedef enum
|
||||
ENERGY_SCAN_MODE_ASYNC
|
||||
} energyScanMode;
|
||||
|
||||
RAIL_Handle_t gRailHandle;
|
||||
|
||||
static volatile bool sTransmitBusy = false;
|
||||
static bool sPromiscuous = false;
|
||||
static bool sIsSrcMatchEnabled = false;
|
||||
@@ -111,7 +114,8 @@ static otRadioFrame sTransmitFrame;
|
||||
static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH];
|
||||
static volatile otError sTransmitError;
|
||||
|
||||
static efr32BandConfig sBandConfigs[EFR32_NUM_BAND_CONFIGS];
|
||||
static efr32CommonConfig sCommonConfig;
|
||||
static efr32BandConfig sBandConfigs[EFR32_NUM_BAND_CONFIGS];
|
||||
|
||||
static volatile energyScanStatus sEnergyScanStatus;
|
||||
static volatile int8_t sEnergyScanResultDbm;
|
||||
@@ -123,113 +127,99 @@ static energyScanMode sEnergyScanMode;
|
||||
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents);
|
||||
|
||||
static const RAIL_IEEE802154_Config_t sRailIeee802154Config = {
|
||||
NULL, // addresses
|
||||
{
|
||||
// ackConfig
|
||||
true, // ackConfig.enable
|
||||
894, // ackConfig.ackTimeout
|
||||
.addresses = NULL,
|
||||
.ackConfig =
|
||||
{
|
||||
// ackConfig.rxTransitions
|
||||
RAIL_RF_STATE_RX, // ackConfig.rxTransitions.success
|
||||
RAIL_RF_STATE_RX, // ackConfig.rxTransitions.error
|
||||
.enable = true,
|
||||
.ackTimeout = 894,
|
||||
.rxTransitions =
|
||||
{
|
||||
.success = RAIL_RF_STATE_RX,
|
||||
.error = RAIL_RF_STATE_RX,
|
||||
},
|
||||
.txTransitions =
|
||||
{
|
||||
.success = RAIL_RF_STATE_RX,
|
||||
.error = RAIL_RF_STATE_RX,
|
||||
},
|
||||
},
|
||||
.timings =
|
||||
{
|
||||
// ackConfig.txTransitions
|
||||
RAIL_RF_STATE_RX, // ackConfig.txTransitions.success
|
||||
RAIL_RF_STATE_RX, // ackConfig.txTransitions.error
|
||||
.idleToRx = 100,
|
||||
.txToRx = 192 - 10,
|
||||
.idleToTx = 100,
|
||||
.rxToTx = 192,
|
||||
.rxSearchTimeout = 0,
|
||||
.txToRxSearchTimeout = 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// timings
|
||||
100, // timings.idleToRx
|
||||
192 - 10, // timings.txToRx
|
||||
100, // timings.idleToTx
|
||||
192, // timings.rxToTx
|
||||
0, // timings.rxSearchTimeout
|
||||
0, // timings.txToRxSearchTimeout
|
||||
},
|
||||
RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES, // framesMask
|
||||
false, // promiscuousMode
|
||||
false, // isPanCoordinator
|
||||
.framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES,
|
||||
.promiscuousMode = false,
|
||||
.isPanCoordinator = false,
|
||||
};
|
||||
|
||||
RAIL_DECLARE_TX_POWER_VBAT_CURVES_ALT;
|
||||
|
||||
static int8_t sTxPowerDbm = OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER;
|
||||
|
||||
static efr32BandConfig *sTxBandConfig = NULL;
|
||||
static efr32BandConfig *sRxBandConfig = NULL;
|
||||
static efr32BandConfig *sCurrentBandConfig = NULL;
|
||||
|
||||
static RAIL_Handle_t efr32RailConfigInit(efr32BandConfig *aBandConfig)
|
||||
static RAIL_Handle_t efr32RailInit(efr32CommonConfig *aCommonConfig)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
RAIL_Handle_t handle;
|
||||
RAIL_DataConfig_t railDataConfig = {
|
||||
TX_PACKET_DATA,
|
||||
RX_PACKET_DATA,
|
||||
PACKET_MODE,
|
||||
PACKET_MODE,
|
||||
};
|
||||
RAIL_Status_t status;
|
||||
RAIL_Handle_t handle;
|
||||
|
||||
handle = RAIL_Init(&aBandConfig->mRailConfig, NULL);
|
||||
handle = RAIL_Init(&aCommonConfig->mRailConfig, NULL);
|
||||
assert(handle != NULL);
|
||||
|
||||
status = RAIL_ConfigData(handle, &railDataConfig);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
RAIL_Idle(handle, RAIL_IDLE, true);
|
||||
|
||||
status = RAIL_ConfigCal(handle, RAIL_CAL_ALL);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
if (aBandConfig->mChannelConfig != NULL)
|
||||
{
|
||||
RAIL_ConfigChannels(handle, aBandConfig->mChannelConfig, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = RAIL_IEEE802154_Config2p4GHzRadio(handle);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
|
||||
status = RAIL_IEEE802154_Init(handle, &sRailIeee802154Config);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
status = RAIL_ConfigEvents(handle, RAIL_EVENTS_ALL,
|
||||
RAIL_EVENT_RX_ACK_TIMEOUT | //
|
||||
RAIL_EVENT_TX_PACKET_SENT | //
|
||||
RAIL_EVENTS_TX_COMPLETION | //
|
||||
RAIL_EVENT_RX_PACKET_RECEIVED | //
|
||||
RAIL_EVENT_RSSI_AVERAGE_DONE | //
|
||||
RAIL_EVENT_SCHEDULER_STATUS | //
|
||||
RAIL_EVENT_TX_CHANNEL_BUSY | //
|
||||
RAIL_EVENT_TX_ABORTED | //
|
||||
RAIL_EVENT_TX_BLOCKED | //
|
||||
RAIL_EVENT_TX_UNDERFLOW | //
|
||||
RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND | //
|
||||
RAIL_EVENT_CAL_NEEDED //
|
||||
);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
RAIL_SetTxFifo(handle, aBandConfig->mRailTxFifo, 0, sizeof(aBandConfig->mRailTxFifo));
|
||||
uint16_t actualLenth = RAIL_SetTxFifo(handle, aCommonConfig->mRailTxFifo, 0, sizeof(aCommonConfig->mRailTxFifo));
|
||||
assert(actualLenth == sizeof(aCommonConfig->mRailTxFifo));
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void efr32RadioSetTxPower(RAIL_Handle_t aRailHandle,
|
||||
const RAIL_ChannelConfig_t *aChannelConfig,
|
||||
uint8_t aPowerDbm)
|
||||
static void efr32RailConfigLoad(efr32BandConfig *aBandConfig)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
#if HAL_PA_2P4_LOWPOWER == 1
|
||||
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_LP, HAL_PA_VOLTAGE, 10};
|
||||
#else
|
||||
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_HP, HAL_PA_VOLTAGE, 10};
|
||||
#endif
|
||||
|
||||
(void)aBandConfig;
|
||||
|
||||
status = RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
status = RAIL_ConfigTxPower(gRailHandle, &txPowerConfig);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
|
||||
static void efr32RadioSetTxPower(int8_t aPowerDbm)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
const RAIL_TxPowerCurvesConfigAlt_t txPowerCurvesConfig = RAIL_DECLARE_TX_POWER_CURVES_CONFIG_ALT;
|
||||
RAIL_TxPowerConfig_t txPowerConfig = {RAIL_TX_POWER_MODE_2P4_HP, 3300, 10};
|
||||
|
||||
status = RAIL_InitTxPowerCurvesAlt(&txPowerCurvesConfig);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
status = RAIL_ConfigTxPower(aRailHandle, &txPowerConfig);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
status = RAIL_SetTxPowerDbm(aRailHandle, ((RAIL_TxPower_t)aPowerDbm) * 10);
|
||||
status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPowerDbm) * 10);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
|
||||
@@ -249,39 +239,55 @@ static efr32BandConfig *efr32RadioGetBandConfig(uint8_t aChannel)
|
||||
return config;
|
||||
}
|
||||
|
||||
static void efr32BandConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, RAIL_Events_t events))
|
||||
static void efr32ConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, RAIL_Events_t events))
|
||||
{
|
||||
sCommonConfig.mRailConfig.eventsCallback = aEventCallback;
|
||||
sCommonConfig.mRailConfig.protocol = NULL; // only used by Bluetooth stack
|
||||
sCommonConfig.mRailConfig.scheduler = NULL; // only needed for DMP
|
||||
|
||||
uint8_t index = 0;
|
||||
|
||||
#if RADIO_CONFIG_2P4GHZ_OQPSK_SUPPORT
|
||||
sBandConfigs[index].mRailConfig.eventsCallback = aEventCallback;
|
||||
sBandConfigs[index].mRailConfig.protocol = NULL;
|
||||
sBandConfigs[index].mRailConfig.scheduler = &sBandConfigs[index].mRailSchedState;
|
||||
sBandConfigs[index].mChannelConfig = NULL;
|
||||
sBandConfigs[index].mChannelMin = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
|
||||
sBandConfigs[index].mChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
|
||||
sBandConfigs[index].mChannelConfig = NULL;
|
||||
sBandConfigs[index].mChannelMin = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
|
||||
sBandConfigs[index].mChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
|
||||
|
||||
assert((sBandConfigs[index].mRailHandle = efr32RailConfigInit(&sBandConfigs[index])) != NULL);
|
||||
index++;
|
||||
#endif
|
||||
gRailHandle = efr32RailInit(&sCommonConfig);
|
||||
assert(gRailHandle != NULL);
|
||||
efr32RailConfigLoad(&(sBandConfigs[0]));
|
||||
}
|
||||
|
||||
void efr32RadioInit(void)
|
||||
{
|
||||
efr32BandConfigInit(RAILCb_Generic);
|
||||
RAIL_Status_t status;
|
||||
|
||||
// check if RAIL_TX_FIFO_SIZE is power of two..
|
||||
assert((RAIL_TX_FIFO_SIZE & (RAIL_TX_FIFO_SIZE - 1)) == 0);
|
||||
|
||||
// check the limits of the RAIL_TX_FIFO_SIZE.
|
||||
assert((RAIL_TX_FIFO_SIZE >= 64) || (RAIL_TX_FIFO_SIZE <= 4096));
|
||||
|
||||
efr32ConfigInit(RAILCb_Generic);
|
||||
|
||||
CMU_ClockEnable(cmuClock_PRS, true);
|
||||
status = RAIL_ConfigSleep(gRailHandle, RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
sReceiveFrame.mLength = 0;
|
||||
sReceiveFrame.mPsdu = sReceivePsdu;
|
||||
sTransmitFrame.mLength = 0;
|
||||
sTransmitFrame.mPsdu = sTransmitPsdu;
|
||||
|
||||
sRxBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL);
|
||||
assert(sRxBandConfig != NULL);
|
||||
sCurrentBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL);
|
||||
assert(sCurrentBandConfig != NULL);
|
||||
|
||||
sTxBandConfig = sRxBandConfig;
|
||||
efr32RadioSetTxPower(sTxBandConfig->mRailHandle, sTxBandConfig->mChannelConfig, sTxPowerDbm);
|
||||
efr32RadioSetTxPower(sTxPowerDbm);
|
||||
|
||||
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
|
||||
sTransmitError = OT_ERROR_NONE;
|
||||
sTransmitBusy = false;
|
||||
|
||||
otLogInfoPlat("Initialized", NULL);
|
||||
}
|
||||
@@ -290,34 +296,36 @@ void efr32RadioDeinit(void)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
RAIL_Idle(sBandConfigs[i].mRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true);
|
||||
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
|
||||
status = RAIL_ConfigEvents(gRailHandle, RAIL_EVENTS_ALL, 0);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
status = RAIL_IEEE802154_Deinit(sBandConfigs[i].mRailHandle);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
sBandConfigs[i].mRailHandle = NULL;
|
||||
}
|
||||
|
||||
sTxBandConfig = NULL;
|
||||
sRxBandConfig = NULL;
|
||||
sCurrentBandConfig = NULL;
|
||||
}
|
||||
|
||||
static otError efr32StartEnergyScan(energyScanMode aMode, uint16_t aChannel, RAIL_Time_t aAveragingTimeUs)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_SAMPLE_RSSI_PRIORITY};
|
||||
otError error = OT_ERROR_NONE;
|
||||
RAIL_Status_t status;
|
||||
otError error = OT_ERROR_NONE;
|
||||
efr32BandConfig *config = NULL;
|
||||
|
||||
otEXPECT_ACTION(sEnergyScanStatus == ENERGY_SCAN_STATUS_IDLE, error = OT_ERROR_BUSY);
|
||||
|
||||
sEnergyScanStatus = ENERGY_SCAN_STATUS_IN_PROGRESS;
|
||||
sEnergyScanMode = aMode;
|
||||
|
||||
RAIL_Idle(sRxBandConfig->mRailHandle, RAIL_IDLE, true);
|
||||
RAIL_Idle(gRailHandle, RAIL_IDLE, true);
|
||||
|
||||
status = RAIL_StartAverageRssi(sRxBandConfig->mRailHandle, aChannel, aAveragingTimeUs, &schedulerInfo);
|
||||
config = efr32RadioGetBandConfig(aChannel);
|
||||
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
if (sCurrentBandConfig != config)
|
||||
{
|
||||
efr32RailConfigLoad(config);
|
||||
sCurrentBandConfig = config;
|
||||
}
|
||||
|
||||
status = RAIL_StartAverageRssi(gRailHandle, aChannel, aAveragingTimeUs, NULL);
|
||||
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
|
||||
|
||||
exit:
|
||||
@@ -352,7 +360,7 @@ void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
status = RAIL_IEEE802154_SetPanId(sBandConfigs[i].mRailHandle, aPanId, 0);
|
||||
status = RAIL_IEEE802154_SetPanId(gRailHandle, aPanId, 0);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -368,7 +376,7 @@ void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aA
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
status = RAIL_IEEE802154_SetLongAddress(sBandConfigs[i].mRailHandle, (uint8_t *)aAddress->m8, 0);
|
||||
status = RAIL_IEEE802154_SetLongAddress(gRailHandle, (uint8_t *)aAddress->m8, 0);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -383,7 +391,7 @@ void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
status = RAIL_IEEE802154_SetShortAddress(sBandConfigs[i].mRailHandle, aAddress, 0);
|
||||
status = RAIL_IEEE802154_SetShortAddress(gRailHandle, aAddress, 0);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -427,12 +435,9 @@ otError otPlatRadioSleep(otInstance *aInstance)
|
||||
error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
otLogInfoPlat("State=OT_RADIO_STATE_SLEEP", NULL);
|
||||
sState = OT_RADIO_STATE_SLEEP;
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
RAIL_Idle(sBandConfigs[i].mRailHandle, RAIL_IDLE, true);
|
||||
}
|
||||
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); // abort packages under reception
|
||||
sState = OT_RADIO_STATE_SLEEP;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -440,10 +445,9 @@ exit:
|
||||
|
||||
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_RX_PRIORITY};
|
||||
RAIL_Status_t status;
|
||||
efr32BandConfig * config;
|
||||
otError error = OT_ERROR_NONE;
|
||||
RAIL_Status_t status;
|
||||
efr32BandConfig *config;
|
||||
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
otEXPECT_ACTION(sState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
|
||||
@@ -451,13 +455,14 @@ otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
|
||||
config = efr32RadioGetBandConfig(aChannel);
|
||||
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
if (sRxBandConfig != config)
|
||||
if (sCurrentBandConfig != config)
|
||||
{
|
||||
RAIL_Idle(sRxBandConfig->mRailHandle, RAIL_IDLE, false);
|
||||
sRxBandConfig = config;
|
||||
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
|
||||
efr32RailConfigLoad(config);
|
||||
sCurrentBandConfig = config;
|
||||
}
|
||||
|
||||
status = RAIL_StartRx(sRxBandConfig->mRailHandle, aChannel, &schedulerInfo);
|
||||
status = RAIL_StartRx(gRailHandle, aChannel, NULL);
|
||||
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
|
||||
|
||||
otLogInfoPlat("State=OT_RADIO_STATE_RECEIVE", NULL);
|
||||
@@ -470,13 +475,14 @@ exit:
|
||||
|
||||
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
|
||||
RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_NONE;
|
||||
RAIL_SchedulerInfo_t schedulerInfo = {.priority = EFR32_SCHEDULER_TX_PRIORITY};
|
||||
efr32BandConfig * config;
|
||||
RAIL_Status_t status;
|
||||
uint8_t frameLength;
|
||||
otError error = OT_ERROR_NONE;
|
||||
RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
|
||||
RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_DEFAULT;
|
||||
efr32BandConfig * config;
|
||||
RAIL_Status_t status;
|
||||
uint8_t frameLength;
|
||||
|
||||
assert(sTransmitBusy == false);
|
||||
|
||||
otEXPECT_ACTION((sState != OT_RADIO_STATE_DISABLED) && (sState != OT_RADIO_STATE_TRANSMIT),
|
||||
error = OT_ERROR_INVALID_STATE);
|
||||
@@ -488,16 +494,17 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
|
||||
sTransmitError = OT_ERROR_NONE;
|
||||
sTransmitBusy = true;
|
||||
|
||||
if (sTxBandConfig != config)
|
||||
if (sCurrentBandConfig != config)
|
||||
{
|
||||
efr32RadioSetTxPower(config->mRailHandle, config->mChannelConfig, sTxPowerDbm);
|
||||
sTxBandConfig = config;
|
||||
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
|
||||
efr32RailConfigLoad(config);
|
||||
sCurrentBandConfig = config;
|
||||
}
|
||||
|
||||
otEXPECT(aFrame->mLength >= IEEE802154_MIN_LENGTH && aFrame->mLength <= IEEE802154_MAX_LENGTH);
|
||||
frameLength = (uint8_t)aFrame->mLength;
|
||||
RAIL_WriteTxFifo(sTxBandConfig->mRailHandle, &frameLength, sizeof frameLength, true);
|
||||
RAIL_WriteTxFifo(sTxBandConfig->mRailHandle, aFrame->mPsdu, frameLength - 2, false);
|
||||
RAIL_WriteTxFifo(gRailHandle, &frameLength, sizeof frameLength, true);
|
||||
RAIL_WriteTxFifo(gRailHandle, aFrame->mPsdu, frameLength - 2, false);
|
||||
|
||||
if (aFrame->mPsdu[0] & IEEE802154_ACK_REQUEST)
|
||||
{
|
||||
@@ -506,12 +513,11 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
|
||||
|
||||
if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled)
|
||||
{
|
||||
status =
|
||||
RAIL_StartCcaCsmaTx(sTxBandConfig->mRailHandle, aFrame->mChannel, txOptions, &csmaConfig, &schedulerInfo);
|
||||
status = RAIL_StartCcaCsmaTx(gRailHandle, aFrame->mChannel, txOptions, &csmaConfig, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = RAIL_StartTx(sTxBandConfig->mRailHandle, aFrame->mChannel, txOptions, &schedulerInfo);
|
||||
status = RAIL_StartTx(gRailHandle, aFrame->mChannel, txOptions, NULL);
|
||||
}
|
||||
|
||||
if (status == RAIL_STATUS_NO_ERROR)
|
||||
@@ -537,30 +543,19 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
|
||||
|
||||
int8_t otPlatRadioGetRssi(otInstance *aInstance)
|
||||
{
|
||||
otError error;
|
||||
uint32_t start;
|
||||
int8_t rssi = OT_RADIO_RSSI_INVALID;
|
||||
|
||||
int8_t rssi = OT_RADIO_RSSI_INVALID;
|
||||
OT_UNUSED_VARIABLE(aInstance);
|
||||
|
||||
error = efr32StartEnergyScan(ENERGY_SCAN_MODE_SYNC, sReceiveFrame.mChannel, EFR32_RSSI_AVERAGING_TIME);
|
||||
otEXPECT(error == OT_ERROR_NONE);
|
||||
|
||||
start = RAIL_GetTime();
|
||||
|
||||
// waiting for the event RAIL_EVENT_RSSI_AVERAGE_DONE
|
||||
while (sEnergyScanStatus == ENERGY_SCAN_STATUS_IN_PROGRESS &&
|
||||
((RAIL_GetTime() - start) < EFR32_RSSI_AVERAGING_TIMEOUT))
|
||||
;
|
||||
|
||||
if (sEnergyScanStatus == ENERGY_SCAN_STATUS_COMPLETED)
|
||||
if ((RAIL_GetRadioState(gRailHandle) & RAIL_RF_STATE_RX))
|
||||
{
|
||||
rssi = sEnergyScanResultDbm;
|
||||
int16_t railRssi = RAIL_RSSI_INVALID;
|
||||
railRssi = RAIL_GetRssi(gRailHandle, true);
|
||||
if (railRssi != RAIL_RSSI_INVALID)
|
||||
{
|
||||
rssi = railRssi / QUARTER_DBM_IN_DBM;
|
||||
}
|
||||
}
|
||||
|
||||
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
|
||||
|
||||
exit:
|
||||
return rssi;
|
||||
}
|
||||
|
||||
@@ -588,7 +583,7 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
status = RAIL_IEEE802154_SetPromiscuousMode(sBandConfigs[i].mRailHandle, aEnable);
|
||||
status = RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, aEnable);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -601,7 +596,7 @@ void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
|
||||
sIsSrcMatchEnabled = aEnable;
|
||||
}
|
||||
|
||||
static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle)
|
||||
static void processNextRxPacket(otInstance *aInstance)
|
||||
{
|
||||
RAIL_RxPacketHandle_t packetHandle = RAIL_RX_PACKET_HANDLE_INVALID;
|
||||
RAIL_RxPacketInfo_t packetInfo;
|
||||
@@ -609,13 +604,11 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
|
||||
RAIL_Status_t status;
|
||||
uint16_t length;
|
||||
|
||||
packetHandle = RAIL_GetRxPacketInfo(aRailHandle, RAIL_RX_PACKET_HANDLE_OLDEST, &packetInfo);
|
||||
packetHandle = RAIL_GetRxPacketInfo(gRailHandle, RAIL_RX_PACKET_HANDLE_OLDEST, &packetInfo);
|
||||
otEXPECT_ACTION(packetInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS,
|
||||
packetHandle = RAIL_RX_PACKET_HANDLE_INVALID);
|
||||
|
||||
packetDetails.timeReceived.timePosition = RAIL_PACKET_TIME_INVALID;
|
||||
packetDetails.timeReceived.totalPacketBytes = 0;
|
||||
status = RAIL_GetRxPacketDetails(aRailHandle, packetHandle, &packetDetails);
|
||||
status = RAIL_GetRxPacketDetailsAlt(gRailHandle, packetHandle, &packetDetails);
|
||||
otEXPECT(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
length = packetInfo.packetBytes + 1;
|
||||
@@ -639,13 +632,13 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
|
||||
memcpy(sReceiveFrame.mPsdu + packetInfo.firstPortionBytes, packetInfo.lastPortionData,
|
||||
packetInfo.packetBytes - packetInfo.firstPortionBytes);
|
||||
|
||||
sReceiveFrame.mLength = length;
|
||||
sReceiveFrame.mInfo.mRxInfo.mRssi = packetDetails.rssi;
|
||||
sReceiveFrame.mInfo.mRxInfo.mLqi = packetDetails.lqi;
|
||||
status = RAIL_ReleaseRxPacket(gRailHandle, packetHandle);
|
||||
if (status == RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
packetHandle = RAIL_RX_PACKET_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
// TODO: grab timestamp and handle conversion to msec/usec
|
||||
// sReceiveFrame.mInfo.mRxInfo.mMsec = packetDetails.packetTime;
|
||||
// sReceiveFrame.mInfo.mRxInfo.mUsec = packetDetails.packetTime;
|
||||
sReceiveFrame.mLength = length;
|
||||
|
||||
if (packetDetails.isAck)
|
||||
{
|
||||
@@ -669,6 +662,17 @@ static void processNextRxPacket(otInstance *aInstance, RAIL_Handle_t aRailHandle
|
||||
|
||||
sReceiveError = OT_ERROR_NONE;
|
||||
|
||||
sReceiveFrame.mInfo.mRxInfo.mRssi = packetDetails.rssi;
|
||||
sReceiveFrame.mInfo.mRxInfo.mLqi = packetDetails.lqi;
|
||||
|
||||
// TODO: grab timestamp and handle conversion to msec/usec and RAIL_GetRxTimeSyncWordEndAlt
|
||||
// sReceiveFrame.mInfo.mRxInfo.mMsec = packetDetails.packetTime;
|
||||
// sReceiveFrame.mInfo.mRxInfo.mUsec = packetDetails.packetTime;
|
||||
|
||||
// TODO Set this flag only when the packet is really acknowledged with frame pending set.
|
||||
// See https://github.com/openthread/openthread/pull/3785
|
||||
sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
|
||||
if (otPlatDiagModeGet())
|
||||
@@ -692,7 +696,7 @@ exit:
|
||||
|
||||
if (packetHandle != RAIL_RX_PACKET_HANDLE_INVALID)
|
||||
{
|
||||
RAIL_ReleaseRxPacket(aRailHandle, packetHandle);
|
||||
RAIL_ReleaseRxPacket(gRailHandle, packetHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,21 +729,36 @@ static void ieee802154DataRequestCommand(RAIL_Handle_t aRailHandle)
|
||||
|
||||
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
|
||||
{
|
||||
if (aEvents &
|
||||
(RAIL_EVENT_TX_ABORTED | RAIL_EVENT_TX_BLOCKED | RAIL_EVENT_TX_UNDERFLOW | RAIL_EVENT_SCHEDULER_STATUS))
|
||||
if (aEvents & RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND)
|
||||
{
|
||||
sTransmitError = OT_ERROR_ABORT;
|
||||
sTransmitBusy = false;
|
||||
|
||||
RAIL_YieldRadio(aRailHandle);
|
||||
ieee802154DataRequestCommand(aRailHandle);
|
||||
}
|
||||
if (aEvents & RAIL_EVENTS_TX_COMPLETION)
|
||||
{
|
||||
if (aEvents & RAIL_EVENT_TX_PACKET_SENT)
|
||||
{
|
||||
if ((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0)
|
||||
{
|
||||
sTransmitError = OT_ERROR_NONE;
|
||||
sTransmitBusy = false;
|
||||
}
|
||||
}
|
||||
else if (aEvents & RAIL_EVENT_TX_CHANNEL_BUSY)
|
||||
{
|
||||
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE;
|
||||
sTransmitBusy = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sTransmitError = OT_ERROR_ABORT;
|
||||
sTransmitBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_RX_ACK_TIMEOUT)
|
||||
{
|
||||
sTransmitError = OT_ERROR_NO_ACK;
|
||||
sTransmitBusy = false;
|
||||
|
||||
RAIL_YieldRadio(aRailHandle);
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_RX_PACKET_RECEIVED)
|
||||
@@ -747,30 +766,6 @@ static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
|
||||
RAIL_HoldRxPacket(aRailHandle);
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND)
|
||||
{
|
||||
ieee802154DataRequestCommand(aRailHandle);
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_TX_PACKET_SENT)
|
||||
{
|
||||
if ((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0)
|
||||
{
|
||||
sTransmitError = OT_ERROR_NONE;
|
||||
sTransmitBusy = false;
|
||||
|
||||
RAIL_YieldRadio(aRailHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_TX_CHANNEL_BUSY)
|
||||
{
|
||||
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE;
|
||||
sTransmitBusy = false;
|
||||
|
||||
RAIL_YieldRadio(aRailHandle);
|
||||
}
|
||||
|
||||
if (aEvents & RAIL_EVENT_CAL_NEEDED)
|
||||
{
|
||||
RAIL_Status_t status;
|
||||
@@ -793,8 +788,6 @@ static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
|
||||
{
|
||||
sEnergyScanResultDbm = energyScanResultQuarterDbm / QUARTER_DBM_IN_DBM;
|
||||
}
|
||||
|
||||
RAIL_YieldRadio(aRailHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +808,6 @@ void efr32RadioProcess(otInstance *aInstance)
|
||||
}
|
||||
|
||||
sState = OT_RADIO_STATE_RECEIVE;
|
||||
|
||||
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
||||
if (otPlatDiagModeGet())
|
||||
{
|
||||
@@ -838,7 +830,7 @@ void efr32RadioProcess(otInstance *aInstance)
|
||||
otPlatRadioEnergyScanDone(aInstance, sEnergyScanResultDbm);
|
||||
}
|
||||
|
||||
processNextRxPacket(aInstance, sRxBandConfig->mRailHandle);
|
||||
processNextRxPacket(aInstance);
|
||||
}
|
||||
|
||||
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
|
||||
@@ -860,11 +852,8 @@ otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
|
||||
|
||||
RAIL_Status_t status;
|
||||
|
||||
for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++)
|
||||
{
|
||||
status = RAIL_SetTxPowerDbm(sBandConfigs[i].mRailHandle, ((RAIL_TxPower_t)aPower) * 10);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
}
|
||||
status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPower) * 10);
|
||||
assert(status == RAIL_STATUS_NO_ERROR);
|
||||
|
||||
sTxPowerDbm = aPower;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef __RAIL_CONFIG_H__
|
||||
#define __RAIL_CONFIG_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rail_types.h"
|
||||
#include "board_config.h"
|
||||
#include "rail_types.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define RADIO_CONFIG_XTAL_FREQUENCY 38400000UL
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Copyright (c) 2019, 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.
|
||||
#
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
|
||||
|
||||
# Always package (e.g. for 'make dist') these subdirectories.
|
||||
|
||||
DIST_SUBDIRS = \
|
||||
sleepy-demo-mtd \
|
||||
sleepy-demo-ftd \
|
||||
$(NULL)
|
||||
|
||||
# Always build (e.g. for 'make all') these subdirectories.
|
||||
|
||||
SUBDIRS = \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_EXECUTABLE
|
||||
SUBDIRS += sleepy-demo-mtd sleepy-demo-ftd
|
||||
endif
|
||||
|
||||
# Always pretty (e.g. for 'make pretty') these subdirectories.
|
||||
|
||||
PRETTY_SUBDIRS = \
|
||||
sleepy-demo-mtd \
|
||||
sleepy-demo-ftd \
|
||||
$(NULL)
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
# EFR32MG21 Sleepy Demo Example
|
||||
|
||||
The EFR32 Sleepy applications demonstrates Sleepy End Device behaviour using
|
||||
the EFR32's low power EM2 mode. The steps below will take you through the
|
||||
process of building and running the demo
|
||||
|
||||
For setting up the build environment refer to [examples/platforms/efr32mg21/README.md](../README.md).
|
||||
|
||||
|
||||
## 1. Build
|
||||
|
||||
```bash
|
||||
$ cd <path-to-openthread>
|
||||
$ ./bootstrap
|
||||
$ make -f examples/Makefile-efr32mg21 COMMISSIONER=1 JOINER=1 DHCP6_CLIENT=1 DHCP6_SERVER=1 BOARD=BRD4180A
|
||||
```
|
||||
|
||||
Convert the resulting executables into S-Record format and append a s37 suffix.
|
||||
|
||||
```bash
|
||||
$ cd output/efr32mg21/bin
|
||||
$ arm-none-eabi-objcopy -O srec sleepy-demo-mtd sleepy-demo-mtd.s37
|
||||
$ arm-none-eabi-objcopy -O srec sleepy-demo-ftd sleepy-demo-ftd.s37
|
||||
```
|
||||
|
||||
In Silicon Labs Simplicity Studio flash one device with the sleepy-demo-mtd.s37
|
||||
image and the other device with the sleepy-demo-ftd.s37 image.
|
||||
|
||||
For instructions on flashing firmware see [examples/platforms/efr32mg21/README.md](../README.md#flash-binaries)
|
||||
|
||||
|
||||
## 2. Starting nodes
|
||||
|
||||
For demonstration purposes the network settings are hardcoded within the source files.
|
||||
The devices start Thread and form a network within a few seconds of powering on. In a real-life
|
||||
application the devices should implement and go through a commissioning process to create
|
||||
a network and add devices.
|
||||
|
||||
When the sleepy-demo-ftd device is started in the CLI the user shall see:
|
||||
|
||||
```
|
||||
sleepy-demo-ftd started
|
||||
sleepy-demo-ftd changed to leader
|
||||
```
|
||||
|
||||
When the sleepy-demo-mtd device starts it joins the preconfigured Thread network
|
||||
before disabling Rx-On-Idle to become a Sleepy-End-Device.
|
||||
|
||||
Use the command "child table" in the FTD console and observe the R flag of the child is 0.
|
||||
|
||||
```
|
||||
> child table
|
||||
| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|S|D|N| Extended MAC |
|
||||
+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+
|
||||
| 1 | 0x8401 | 240 | 3 | 3 | 3 |0|1|0|0| 8e8582dbd78c243c |
|
||||
|
||||
Done
|
||||
```
|
||||
|
||||
|
||||
## 3. MTD behaviour on wakeup
|
||||
|
||||
MTD wakes up every 5 seconds and sends a multicast UDP message containing the
|
||||
string "mtd is awake". The FTD listens on the multicast address and will toggle
|
||||
LED 0 and display a message in the CLI showing "Message Received: mtd is awake".
|
||||
|
||||
## 4. Monitoring power consumption of the MTD
|
||||
|
||||
Open the Energy Profiler within Silicon Labs Simplicity Studio. Within the Quick Access menu
|
||||
select Start Energy Capture... and select the MTD device. When operating as a Sleepy End Device
|
||||
with no LEDs on the current should be under 20-80 microamps with occassional spikes during waking
|
||||
and polling the parent. With the LED on the MTD has a current consumption of approximately 1mA.
|
||||
|
||||
When operating as a Minial End Device with the Rx on Idle observe that the current is in the order
|
||||
of 10ma.
|
||||
|
||||
With further configuration of GPIOs and peripherals it is possible to reduce the sleepy current
|
||||
consumption further.
|
||||
|
||||
## 5. Notes on sleeping, sleepy callback and interrupts
|
||||
|
||||
To allow the EFR32 to enter sleepy mode the application must register a callback with efr32SetSleepCallback.
|
||||
The return value of callback is used to indicate that the application has no further work to do and that
|
||||
it is safe to go into a low power mode. The callback is called with interrupts disabled so should do
|
||||
the minimum required to check if it can sleep.
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
#
|
||||
# Copyright (c) 2019, 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.
|
||||
#
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
|
||||
include $(top_srcdir)/examples/platforms/Makefile.platform.am
|
||||
|
||||
override CFLAGS := $(filter-out -Wconversion,$(CFLAGS))
|
||||
override CXXFLAGS := $(filter-out -Wconversion,$(CXXFLAGS))
|
||||
|
||||
EFR32_BOARD_DIR = $(shell echo $(BOARD) | tr A-Z a-z)
|
||||
EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.6
|
||||
|
||||
$(top_builddir)/examples/platforms/efr32mg21/libopenthread-efr32mg21.a:
|
||||
(cd $(top_builddir)/examples/platforms/efr32mg21/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32mg21.a )
|
||||
|
||||
bin_PROGRAMS = \
|
||||
$(NULL)
|
||||
|
||||
CPPFLAGS_COMMON += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/src/core \
|
||||
-I$(top_srcdir)/examples/platforms \
|
||||
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
|
||||
-Wno-sign-compare \
|
||||
-DCORTEXM3 \
|
||||
-D__START=main \
|
||||
-DPHY=EMBER_PHY_RAIL \
|
||||
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
|
||||
-DCORTEXM3_EFM32_MICRO \
|
||||
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
|
||||
-D__STARTUP_CLEAR_BSS \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/examples/platforms \
|
||||
-I$(top_srcdir)/examples/platforms/efr32/$(EFR32_BOARD_DIR) \
|
||||
-I$(top_srcdir)/src/core \
|
||||
-I$(top_srcdir)/third_party/silabs/rail_config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/common \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/protocol/ieee802154 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32/rf/common/cortex \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal/efr32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/gpiointerrupt/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/ustimer/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/rtcdrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emlib/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/halconfig/inc/hal-config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/util/plugin/plugin-common/fem-control \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread/stack \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-missing-field-initializers \
|
||||
$(NULL)
|
||||
|
||||
LDADD_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
LDFLAGS_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
LIBTOOLFLAGS_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
SOURCES_COMMON += \
|
||||
main.c \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
|
||||
LDADD_COMMON += \
|
||||
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
|
||||
$(NULL)
|
||||
endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
|
||||
|
||||
if OPENTHREAD_ENABLE_EXECUTABLE
|
||||
bin_PROGRAMS += \
|
||||
sleepy-demo-ftd \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
sleepy_demo_ftd_CPPFLAGS = \
|
||||
$(CPPFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_ftd_LDADD = \
|
||||
$(top_builddir)/src/cli/libopenthread-cli-ftd.a \
|
||||
$(top_builddir)/src/core/libopenthread-ftd.a \
|
||||
$(LDADD_COMMON) \
|
||||
$(top_builddir)/src/core/libopenthread-ftd.a \
|
||||
$(LDADD_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_ftd_LDFLAGS = \
|
||||
$(LDFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_ftd_LIBTOOLFLAGS = \
|
||||
$(LIBTOOLFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_ftd_SOURCES = \
|
||||
$(SOURCES_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_LINKER_MAP
|
||||
sleepy_demo_ftd_LDFLAGS += -Wl,-Map=sleepy-demo-ftd.map
|
||||
endif
|
||||
|
||||
if OPENTHREAD_BUILD_COVERAGE
|
||||
CPPFLAGS_COMMON += \
|
||||
-DOPENTHREAD_ENABLE_COVERAGE \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = $(wildcard *.gcda *.gcno)
|
||||
endif # OPENTHREAD_BUILD_COVERAGE
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#include "bsp.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_emu.h"
|
||||
#include "gpiointerrupt.h"
|
||||
#include "hal-config-board.h"
|
||||
#include "hal_common.h"
|
||||
#include "openthread-system.h"
|
||||
#include "rtcdriver.h"
|
||||
#include <assert.h>
|
||||
#include <common/logging.hpp>
|
||||
#include <openthread-core-config.h>
|
||||
#include <string.h>
|
||||
#include <openthread/cli.h>
|
||||
#include <openthread/config.h>
|
||||
#include <openthread/dataset_ftd.h>
|
||||
#include <openthread/diag.h>
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/tasklet.h>
|
||||
#include <openthread/thread.h>
|
||||
#include <openthread/udp.h>
|
||||
#include <openthread/platform/logging.h>
|
||||
|
||||
// Constants
|
||||
#define MULTICAST_ADDR "ff03::1"
|
||||
#define MULTICAST_PORT 123
|
||||
#define RECV_PORT 234
|
||||
#define MTD_MESSAGE "mtd button"
|
||||
#define FTD_MESSAGE "ftd button"
|
||||
|
||||
// Types
|
||||
typedef struct ButtonArray
|
||||
{
|
||||
GPIO_Port_TypeDef port;
|
||||
unsigned int pin;
|
||||
} ButtonArray_t;
|
||||
|
||||
// Prototypes
|
||||
void setNetworkConfiguration(otInstance *aInstance);
|
||||
void handleNetifStateChanged(uint32_t aFlags, void *aContext);
|
||||
void gpioInit(void (*gpioCallback)(uint8_t pin));
|
||||
void buttonCallback(uint8_t pin);
|
||||
void initUdp(void);
|
||||
void applicationTick(void);
|
||||
void sFtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
|
||||
|
||||
// Variables
|
||||
static otInstance * instance;
|
||||
static otUdpSocket sFtdSocket;
|
||||
static bool sLedOn = false;
|
||||
static bool sHaveSwitchAddress = false;
|
||||
static otIp6Address sSwitchAddress;
|
||||
static bool sFtdButtonPressed = false;
|
||||
static const ButtonArray_t sButtonArray[BSP_BUTTON_COUNT] = BSP_BUTTON_INIT;
|
||||
|
||||
void otTaskletsSignalPending(otInstance *aInstance)
|
||||
{
|
||||
(void)aInstance;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
otSysInit(argc, argv);
|
||||
gpioInit(buttonCallback);
|
||||
|
||||
instance = otInstanceInitSingle();
|
||||
assert(instance);
|
||||
|
||||
otCliUartInit(instance);
|
||||
otCliOutputFormat("sleepy-demo-ftd started\r\n");
|
||||
|
||||
setNetworkConfiguration(instance);
|
||||
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
|
||||
initUdp();
|
||||
|
||||
otIp6SetEnabled(instance, true);
|
||||
otThreadSetEnabled(instance, true);
|
||||
|
||||
while (!otSysPseudoResetWasRequested())
|
||||
{
|
||||
otTaskletsProcess(instance);
|
||||
otSysProcessDrivers(instance);
|
||||
applicationTick();
|
||||
}
|
||||
|
||||
otInstanceFinalize(instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide, if required an "otPlatLog()" function
|
||||
*/
|
||||
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
|
||||
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aLogLevel);
|
||||
OT_UNUSED_VARIABLE(aLogRegion);
|
||||
OT_UNUSED_VARIABLE(aFormat);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, aFormat);
|
||||
otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Override default network settings, such as panid, so the devices can join a network
|
||||
*/
|
||||
void setNetworkConfiguration(otInstance *aInstance)
|
||||
{
|
||||
static char aNetworkName[] = "SleepyEFR32";
|
||||
otOperationalDataset aDataset;
|
||||
|
||||
memset(&aDataset, 0, sizeof(otOperationalDataset));
|
||||
|
||||
/*
|
||||
* Fields that can be configured in otOperationDataset to override defaults:
|
||||
* Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
|
||||
* Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
|
||||
*/
|
||||
aDataset.mActiveTimestamp = 1;
|
||||
aDataset.mComponents.mIsActiveTimestampPresent = true;
|
||||
|
||||
/* Set Channel to 15 */
|
||||
aDataset.mChannel = 15;
|
||||
aDataset.mComponents.mIsChannelPresent = true;
|
||||
|
||||
/* Set Pan ID to 2222 */
|
||||
aDataset.mPanId = (otPanId)0x2222;
|
||||
aDataset.mComponents.mIsPanIdPresent = true;
|
||||
|
||||
/* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
|
||||
uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
|
||||
memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
|
||||
aDataset.mComponents.mIsExtendedPanIdPresent = true;
|
||||
|
||||
/* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
|
||||
uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
|
||||
memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
|
||||
aDataset.mComponents.mIsMasterKeyPresent = true;
|
||||
|
||||
/* Set Network Name to SleepyEFR32 */
|
||||
size_t length = strlen(aNetworkName);
|
||||
assert(length <= OT_NETWORK_NAME_MAX_SIZE);
|
||||
memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
|
||||
aDataset.mComponents.mIsNetworkNamePresent = true;
|
||||
|
||||
otDatasetSetActive(aInstance, &aDataset);
|
||||
}
|
||||
|
||||
void handleNetifStateChanged(uint32_t aFlags, void *aContext)
|
||||
{
|
||||
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
|
||||
{
|
||||
otDeviceRole changedRole = otThreadGetDeviceRole(aContext);
|
||||
|
||||
switch (changedRole)
|
||||
{
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
otCliOutputFormat("sleepy-demo-ftd changed to leader\r\n");
|
||||
break;
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
otCliOutputFormat("sleepy-demo-ftd changed to router\r\n");
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_CHILD:
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_DETACHED:
|
||||
case OT_DEVICE_ROLE_DISABLED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gpioInit(void (*callback)(uint8_t pin))
|
||||
{
|
||||
// set up button GPIOs to input with pullups
|
||||
for (int i = 0; i < BSP_BUTTON_COUNT; i++)
|
||||
{
|
||||
GPIO_PinModeSet(sButtonArray[i].port, sButtonArray[i].pin, gpioModeInputPull, 1);
|
||||
}
|
||||
// set up interrupt based callback function on falling edge
|
||||
GPIOINT_Init();
|
||||
GPIOINT_CallbackRegister(sButtonArray[0].pin, callback);
|
||||
GPIOINT_CallbackRegister(sButtonArray[1].pin, callback);
|
||||
GPIO_IntConfig(sButtonArray[0].port, sButtonArray[0].pin, false, true, true);
|
||||
GPIO_IntConfig(sButtonArray[1].port, sButtonArray[1].pin, false, true, true);
|
||||
|
||||
BSP_LedsInit();
|
||||
BSP_LedClear(0);
|
||||
BSP_LedClear(1);
|
||||
}
|
||||
|
||||
void initUdp(void)
|
||||
{
|
||||
otError error;
|
||||
otSockAddr sockaddr;
|
||||
|
||||
memset(&sockaddr, 0, sizeof(sockaddr));
|
||||
|
||||
otIp6AddressFromString(MULTICAST_ADDR, &sockaddr.mAddress);
|
||||
sockaddr.mPort = MULTICAST_PORT;
|
||||
|
||||
error = otUdpOpen(instance, &sFtdSocket, sFtdReceiveCallback, NULL);
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otCliOutputFormat("FTD failed to open udp multicast\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
error = otUdpBind(&sFtdSocket, &sockaddr);
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otUdpClose(&sFtdSocket);
|
||||
otCliOutputFormat("FTD failed to bind udp multicast\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void buttonCallback(uint8_t pin)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(pin);
|
||||
sFtdButtonPressed = true;
|
||||
}
|
||||
|
||||
void applicationTick(void)
|
||||
{
|
||||
otError error = 0;
|
||||
otMessageInfo messageInfo;
|
||||
otMessage * message = NULL;
|
||||
char * payload = FTD_MESSAGE;
|
||||
|
||||
if (sFtdButtonPressed == true)
|
||||
{
|
||||
sFtdButtonPressed = false;
|
||||
|
||||
if (sHaveSwitchAddress)
|
||||
{
|
||||
memset(&messageInfo, 0, sizeof(messageInfo));
|
||||
memcpy(&messageInfo.mPeerAddr, &sSwitchAddress, sizeof messageInfo.mPeerAddr);
|
||||
messageInfo.mPeerPort = RECV_PORT;
|
||||
|
||||
message = otUdpNewMessage(instance, NULL);
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
error = otMessageAppend(message, payload, (uint16_t)strlen(payload));
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
error = otUdpSend(&sFtdSocket, message, &messageInfo);
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
otMessageFree(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sFtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
OT_UNUSED_VARIABLE(aMessage);
|
||||
OT_UNUSED_VARIABLE(aMessageInfo);
|
||||
uint8_t buf[1500];
|
||||
int length;
|
||||
|
||||
sLedOn = !sLedOn;
|
||||
|
||||
if (sLedOn)
|
||||
{
|
||||
BSP_LedSet(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
BSP_LedClear(0);
|
||||
}
|
||||
|
||||
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
|
||||
buf[length] = '\0';
|
||||
otCliOutputFormat("Message Received: %s\r\n", buf);
|
||||
|
||||
sHaveSwitchAddress = true;
|
||||
memcpy(&sSwitchAddress, &aMessageInfo->mPeerAddr, sizeof sSwitchAddress);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#
|
||||
# Copyright (c) 2019, 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.
|
||||
#
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
|
||||
include $(top_srcdir)/examples/platforms/Makefile.platform.am
|
||||
|
||||
override CFLAGS := $(filter-out -Wconversion,$(CFLAGS))
|
||||
override CXXFLAGS := $(filter-out -Wconversion,$(CXXFLAGS))
|
||||
|
||||
EFR32_BOARD_DIR = $(shell echo $(BOARD) | tr A-Z a-z)
|
||||
EFR32MG_SDK_SRCDIR = $(top_srcdir)/third_party/silabs/gecko_sdk_suite/v2.6
|
||||
|
||||
$(top_builddir)/examples/platforms/efr32mg21/libopenthread-efr32mg21.a:
|
||||
(cd $(top_builddir)/examples/platforms/efr32mg21/ && $(MAKE) $(AM_MAKEFLAGS) libopenthread-efr32mg21.a )
|
||||
|
||||
bin_PROGRAMS = \
|
||||
$(NULL)
|
||||
|
||||
CPPFLAGS_COMMON += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/src/core \
|
||||
-I$(top_srcdir)/examples/platforms \
|
||||
-DPLATFORM_HEADER=\"@top_builddir@/third_party/silabs/gecko_sdk_suite/v2.6/platform/base/hal/micro/cortexm3/compiler/gcc.h\" \
|
||||
-Wno-sign-compare \
|
||||
-DCORTEXM3 \
|
||||
-D__START=main \
|
||||
-DPHY=EMBER_PHY_RAIL \
|
||||
-DMICRO=EMBER_MICRO_CORTEXM3_EFR32 \
|
||||
-DCORTEXM3_EFM32_MICRO \
|
||||
-DPLAT=EMBER_PLATFORM_CORTEXM3 \
|
||||
-D__STARTUP_CLEAR_BSS \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/examples/platforms \
|
||||
-I$(top_srcdir)/examples/platforms/efr32/$(EFR32_BOARD_DIR) \
|
||||
-I$(top_srcdir)/src/core \
|
||||
-I$(top_srcdir)/third_party/silabs/rail_config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/common \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/protocol/ieee802154 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/chip/efr32/rf/common/cortex \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/hal/efr32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/radio/rail_lib/plugin/pa-conversions \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/common/bsp \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/hardware/kit/EFR32MG21_$(BOARD)/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/CMSIS/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/ \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/Device/SiliconLabs/EFR32MG21/Include \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/common/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/gpiointerrupt/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/uartdrv/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/ustimer/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/dmadrv/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emdrv/rtcdrv/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/emlib/inc \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/halconfig/inc/hal-config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/util/plugin/plugin-common/fem-control \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32/config \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/plugin \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal/micro/cortexm3/efm32 \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/protocol/thread/stack \
|
||||
-I$(EFR32MG_SDK_SRCDIR)/platform/base/hal \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-missing-field-initializers \
|
||||
$(NULL)
|
||||
|
||||
LDADD_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
LDFLAGS_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
LIBTOOLFLAGS_COMMON += \
|
||||
$(NULL)
|
||||
|
||||
SOURCES_COMMON += \
|
||||
main.c \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
|
||||
LDADD_COMMON += \
|
||||
$(top_builddir)/third_party/mbedtls/libmbedcrypto.a \
|
||||
$(NULL)
|
||||
endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
|
||||
|
||||
if OPENTHREAD_ENABLE_EXECUTABLE
|
||||
bin_PROGRAMS += \
|
||||
sleepy-demo-mtd \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
sleepy_demo_mtd_CPPFLAGS = \
|
||||
$(CPPFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_mtd_LDADD = \
|
||||
$(top_builddir)/src/cli/libopenthread-cli-mtd.a \
|
||||
$(top_builddir)/src/core/libopenthread-mtd.a \
|
||||
$(LDADD_COMMON) \
|
||||
$(top_builddir)/src/core/libopenthread-mtd.a \
|
||||
$(LDADD_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_mtd_LDFLAGS = \
|
||||
$(LDFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_mtd_LIBTOOLFLAGS = \
|
||||
$(LIBTOOLFLAGS_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
sleepy_demo_mtd_SOURCES = \
|
||||
$(SOURCES_COMMON) \
|
||||
$(NULL)
|
||||
|
||||
if OPENTHREAD_ENABLE_LINKER_MAP
|
||||
sleepy_demo_mtd_LDFLAGS += -Wl,-Map=sleepy-demo-mtd.map
|
||||
endif
|
||||
|
||||
if OPENTHREAD_BUILD_COVERAGE
|
||||
CPPFLAGS_COMMON += \
|
||||
-DOPENTHREAD_ENABLE_COVERAGE \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = $(wildcard *.gcda *.gcno)
|
||||
endif # OPENTHREAD_BUILD_COVERAGE
|
||||
|
||||
include $(abs_top_nlbuild_autotools_dir)/automake/post.am
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bsp.h"
|
||||
#include "em_cmu.h"
|
||||
#include "em_emu.h"
|
||||
#include "gpiointerrupt.h"
|
||||
#include "hal-config-board.h"
|
||||
#include "hal_common.h"
|
||||
#include "openthread-system.h"
|
||||
#include "platform-efr32.h"
|
||||
#include "rtcdriver.h"
|
||||
#include <common/logging.hpp>
|
||||
#include <openthread-core-config.h>
|
||||
#include <openthread/cli.h>
|
||||
#include <openthread/config.h>
|
||||
#include <openthread/dataset_ftd.h>
|
||||
#include <openthread/diag.h>
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/link.h>
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/tasklet.h>
|
||||
#include <openthread/thread.h>
|
||||
#include <openthread/udp.h>
|
||||
#include <openthread/platform/alarm-milli.h>
|
||||
#include <openthread/platform/logging.h>
|
||||
|
||||
// Constants
|
||||
#define MULTICAST_ADDR "ff03::1"
|
||||
#define MULTICAST_PORT 123
|
||||
#define RECV_PORT 234
|
||||
#define SLEEPY_POLL_PERIOD_MS 5000
|
||||
#define MTD_MESSAGE "mtd is awake"
|
||||
#define FTD_MESSAGE "ftd button"
|
||||
|
||||
// Types
|
||||
typedef struct ButtonArray
|
||||
{
|
||||
GPIO_Port_TypeDef port;
|
||||
unsigned int pin;
|
||||
} ButtonArray_t;
|
||||
|
||||
// Prototypes
|
||||
void deviceOutOfSleepCb(void);
|
||||
bool sleepCb(void);
|
||||
void setNetworkConfiguration(otInstance *aInstance);
|
||||
void handleNetifStateChanged(uint32_t aFlags, void *aContext);
|
||||
void gpioInit(void (*gpioCallback)(uint8_t pin));
|
||||
void buttonCallback(uint8_t pin);
|
||||
void initUdp(void);
|
||||
void applicationTick(void);
|
||||
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
|
||||
|
||||
// Variables
|
||||
static otInstance * instance;
|
||||
static otUdpSocket sMtdSocket;
|
||||
static otSockAddr sMulticastSockAddr;
|
||||
static const ButtonArray_t sButtonArray[BSP_BUTTON_COUNT] = BSP_BUTTON_INIT;
|
||||
static bool sButtonPressed = false;
|
||||
static bool sRxOnIdleButtonPressed = false;
|
||||
static bool sLedOn = false;
|
||||
static bool sAllowDeepSleep = false;
|
||||
static bool sTaskletsPendingSem = true;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
otLinkModeConfig config;
|
||||
|
||||
otSysInit(argc, argv);
|
||||
gpioInit(buttonCallback);
|
||||
|
||||
instance = otInstanceInitSingle();
|
||||
assert(instance);
|
||||
|
||||
otCliUartInit(instance);
|
||||
|
||||
otLinkSetPollPeriod(instance, SLEEPY_POLL_PERIOD_MS);
|
||||
setNetworkConfiguration(instance);
|
||||
otSetStateChangedCallback(instance, handleNetifStateChanged, instance);
|
||||
|
||||
config.mRxOnWhenIdle = true;
|
||||
config.mSecureDataRequests = true;
|
||||
config.mDeviceType = 0;
|
||||
config.mNetworkData = 0;
|
||||
otThreadSetLinkMode(instance, config);
|
||||
|
||||
initUdp();
|
||||
otIp6SetEnabled(instance, true);
|
||||
otThreadSetEnabled(instance, true);
|
||||
efr32SetSleepCallback(sleepCb, deviceOutOfSleepCb);
|
||||
|
||||
while (!otSysPseudoResetWasRequested())
|
||||
{
|
||||
otTaskletsProcess(instance);
|
||||
otSysProcessDrivers(instance);
|
||||
|
||||
applicationTick();
|
||||
|
||||
// Put the EFR32 into deep sleep if callback sleepCb permits.
|
||||
efr32Sleep();
|
||||
}
|
||||
|
||||
otInstanceFinalize(instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void deviceOutOfSleepCb(void)
|
||||
{
|
||||
static uint32_t udpPacketSendTimer = 0;
|
||||
|
||||
if (udpPacketSendTimer == 0)
|
||||
{
|
||||
udpPacketSendTimer = otPlatAlarmMilliGetNow();
|
||||
}
|
||||
|
||||
if ((otPlatAlarmMilliGetNow() - udpPacketSendTimer) >= 5000)
|
||||
{
|
||||
sButtonPressed = true;
|
||||
udpPacketSendTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback from efr32Sleep to indicate if it is ok to go into sleep mode.
|
||||
* This runs with interrupts disabled.
|
||||
*/
|
||||
bool sleepCb(void)
|
||||
{
|
||||
bool allow;
|
||||
allow = (sAllowDeepSleep && !sTaskletsPendingSem);
|
||||
sTaskletsPendingSem = false;
|
||||
return allow;
|
||||
}
|
||||
|
||||
void otTaskletsSignalPending(otInstance *aInstance)
|
||||
{
|
||||
(void)aInstance;
|
||||
sTaskletsPendingSem = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override default network settings, such as panid, so the devices can join a network
|
||||
*/
|
||||
void setNetworkConfiguration(otInstance *aInstance)
|
||||
{
|
||||
static char aNetworkName[] = "SleepyEFR32";
|
||||
otOperationalDataset aDataset;
|
||||
|
||||
memset(&aDataset, 0, sizeof(otOperationalDataset));
|
||||
|
||||
/*
|
||||
* Fields that can be configured in otOperationDataset to override defaults:
|
||||
* Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
|
||||
* Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
|
||||
*/
|
||||
aDataset.mActiveTimestamp = 1;
|
||||
aDataset.mComponents.mIsActiveTimestampPresent = true;
|
||||
|
||||
/* Set Channel to 15 */
|
||||
aDataset.mChannel = 15;
|
||||
aDataset.mComponents.mIsChannelPresent = true;
|
||||
|
||||
/* Set Pan ID to 2222 */
|
||||
aDataset.mPanId = (otPanId)0x2222;
|
||||
aDataset.mComponents.mIsPanIdPresent = true;
|
||||
|
||||
/* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
|
||||
uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
|
||||
memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
|
||||
aDataset.mComponents.mIsExtendedPanIdPresent = true;
|
||||
|
||||
/* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
|
||||
uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
|
||||
memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
|
||||
aDataset.mComponents.mIsMasterKeyPresent = true;
|
||||
|
||||
/* Set Network Name to SleepyEFR32 */
|
||||
size_t length = strlen(aNetworkName);
|
||||
assert(length <= OT_NETWORK_NAME_MAX_SIZE);
|
||||
memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
|
||||
aDataset.mComponents.mIsNetworkNamePresent = true;
|
||||
|
||||
otDatasetSetActive(aInstance, &aDataset);
|
||||
}
|
||||
|
||||
void handleNetifStateChanged(uint32_t aFlags, void *aContext)
|
||||
{
|
||||
otLinkModeConfig config;
|
||||
|
||||
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
|
||||
{
|
||||
otDeviceRole changedRole = otThreadGetDeviceRole(aContext);
|
||||
|
||||
switch (changedRole)
|
||||
{
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_CHILD:
|
||||
config.mRxOnWhenIdle = 0;
|
||||
config.mSecureDataRequests = true;
|
||||
config.mDeviceType = 0;
|
||||
config.mNetworkData = 0;
|
||||
otThreadSetLinkMode(instance, config);
|
||||
sAllowDeepSleep = true;
|
||||
break;
|
||||
|
||||
case OT_DEVICE_ROLE_DETACHED:
|
||||
case OT_DEVICE_ROLE_DISABLED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide, if required an "otPlatLog()" function
|
||||
*/
|
||||
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
|
||||
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aLogLevel);
|
||||
OT_UNUSED_VARIABLE(aLogRegion);
|
||||
OT_UNUSED_VARIABLE(aFormat);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, aFormat);
|
||||
otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
void gpioInit(void (*callback)(uint8_t pin))
|
||||
{
|
||||
// set up button GPIOs to input with pullups
|
||||
for (int i = 0; i < BSP_BUTTON_COUNT; i++)
|
||||
{
|
||||
GPIO_PinModeSet(sButtonArray[i].port, sButtonArray[i].pin, gpioModeInputPull, 1);
|
||||
}
|
||||
// set up interrupt based callback function on falling edge
|
||||
GPIOINT_Init();
|
||||
GPIOINT_CallbackRegister(sButtonArray[0].pin, callback);
|
||||
GPIOINT_CallbackRegister(sButtonArray[1].pin, callback);
|
||||
GPIO_IntConfig(sButtonArray[0].port, sButtonArray[0].pin, false, true, true);
|
||||
GPIO_IntConfig(sButtonArray[1].port, sButtonArray[1].pin, false, true, true);
|
||||
|
||||
BSP_LedsInit();
|
||||
BSP_LedClear(0);
|
||||
BSP_LedClear(1);
|
||||
}
|
||||
|
||||
void initUdp(void)
|
||||
{
|
||||
otError error;
|
||||
otSockAddr sockaddr;
|
||||
|
||||
memset(&sMulticastSockAddr, 0, sizeof sMulticastSockAddr);
|
||||
otIp6AddressFromString(MULTICAST_ADDR, &sMulticastSockAddr.mAddress);
|
||||
sMulticastSockAddr.mPort = MULTICAST_PORT;
|
||||
|
||||
memset(&sockaddr, 0, sizeof(sockaddr));
|
||||
sockaddr.mPort = RECV_PORT;
|
||||
|
||||
error = otUdpOpen(instance, &sMtdSocket, mtdReceiveCallback, NULL);
|
||||
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
error = otUdpBind(&sMtdSocket, &sockaddr);
|
||||
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
otUdpClose(&sMtdSocket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void buttonCallback(uint8_t pin)
|
||||
{
|
||||
if ((pin & 0x01) == 0x01)
|
||||
{
|
||||
sButtonPressed = true;
|
||||
}
|
||||
else if ((pin & 0x01) == 0x00)
|
||||
{
|
||||
sRxOnIdleButtonPressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void applicationTick(void)
|
||||
{
|
||||
otError error = 0;
|
||||
otMessageInfo messageInfo;
|
||||
otMessage * message = NULL;
|
||||
char * payload = MTD_MESSAGE;
|
||||
otLinkModeConfig config;
|
||||
|
||||
if (sRxOnIdleButtonPressed == true)
|
||||
{
|
||||
sRxOnIdleButtonPressed = false;
|
||||
sAllowDeepSleep = !sAllowDeepSleep;
|
||||
config.mRxOnWhenIdle = !sAllowDeepSleep;
|
||||
config.mSecureDataRequests = true;
|
||||
config.mDeviceType = 0;
|
||||
config.mNetworkData = 0;
|
||||
otThreadSetLinkMode(instance, config);
|
||||
}
|
||||
|
||||
if (sButtonPressed == true)
|
||||
{
|
||||
sButtonPressed = false;
|
||||
|
||||
memset(&messageInfo, 0, sizeof(messageInfo));
|
||||
memcpy(&messageInfo.mPeerAddr, &sMulticastSockAddr.mAddress, sizeof messageInfo.mPeerAddr);
|
||||
messageInfo.mPeerPort = sMulticastSockAddr.mPort;
|
||||
|
||||
message = otUdpNewMessage(instance, NULL);
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
error = otMessageAppend(message, payload, (uint16_t)strlen(payload));
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
error = otUdpSend(&sMtdSocket, message, &messageInfo);
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
otMessageFree(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mtdReceiveCallback(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aContext);
|
||||
OT_UNUSED_VARIABLE(aMessage);
|
||||
OT_UNUSED_VARIABLE(aMessageInfo);
|
||||
uint8_t buf[1500];
|
||||
int length;
|
||||
|
||||
length = otMessageRead(aMessage, otMessageGetOffset(aMessage), buf, sizeof(buf) - 1);
|
||||
buf[length] = '\0';
|
||||
|
||||
if (strcmp((char *)buf, FTD_MESSAGE) == 0)
|
||||
{
|
||||
sLedOn = !sLedOn;
|
||||
|
||||
if (sLedOn)
|
||||
{
|
||||
BSP_LedSet(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
BSP_LedClear(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,19 +34,24 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <openthread/tasklet.h>
|
||||
#include <openthread/platform/uart.h>
|
||||
|
||||
#include "common/logging.hpp"
|
||||
|
||||
#include "bsp.h"
|
||||
#include "em_chip.h"
|
||||
#include "em_core.h"
|
||||
#include "em_emu.h"
|
||||
#include "em_system.h"
|
||||
#include "hal-config.h"
|
||||
#include "hal_common.h"
|
||||
#include "rail.h"
|
||||
#include "rtcdriver.h"
|
||||
|
||||
#include "openthread-core-efr32-config.h"
|
||||
#include "platform-efr32.h"
|
||||
|
||||
#include "hal-config.h"
|
||||
|
||||
#if (HAL_FEM_ENABLE)
|
||||
#include "fem-control.h"
|
||||
#endif
|
||||
@@ -58,6 +63,8 @@
|
||||
void halInitChipSpecific(void);
|
||||
|
||||
otInstance *sInstance;
|
||||
static bool (*sCanSleepCallback)(void);
|
||||
static void (*sDeviceOutOfSleepCb)(void);
|
||||
|
||||
void otSysInit(int argc, char *argv[])
|
||||
{
|
||||
@@ -69,6 +76,7 @@ void otSysInit(int argc, char *argv[])
|
||||
halInitChipSpecific();
|
||||
|
||||
BSP_Init(BSP_INIT_BCC);
|
||||
RTCDRV_Init();
|
||||
|
||||
#if (HAL_FEM_ENABLE)
|
||||
initFem();
|
||||
@@ -97,6 +105,51 @@ void otSysDeinit(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void efr32SetSleepCallback(bool (*aCallback)(void), void (*aCallbackWake)(void))
|
||||
{
|
||||
sCanSleepCallback = aCallback;
|
||||
sDeviceOutOfSleepCb = aCallbackWake;
|
||||
}
|
||||
|
||||
void efr32Sleep(void)
|
||||
{
|
||||
bool canDeepSleep = false;
|
||||
int wakeupProcessTime = 1000;
|
||||
CORE_DECLARE_IRQ_STATE;
|
||||
|
||||
if (RAIL_Sleep(wakeupProcessTime, &canDeepSleep) == RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
if (canDeepSleep)
|
||||
{
|
||||
CORE_ENTER_ATOMIC();
|
||||
if (sCanSleepCallback != NULL && sCanSleepCallback())
|
||||
{
|
||||
EMU_EnterEM2(true);
|
||||
}
|
||||
CORE_EXIT_ATOMIC();
|
||||
// TODO OT will handle an interrupt here and it mustn't call any RAIL APIs
|
||||
|
||||
while (RAIL_Wake(0) != RAIL_STATUS_NO_ERROR)
|
||||
{
|
||||
}
|
||||
|
||||
if (sDeviceOutOfSleepCb != NULL)
|
||||
{
|
||||
sDeviceOutOfSleepCb();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CORE_ENTER_ATOMIC();
|
||||
if (sCanSleepCallback != NULL && sCanSleepCallback())
|
||||
{
|
||||
EMU_EnterEM1();
|
||||
}
|
||||
CORE_EXIT_ATOMIC();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void otSysProcessDrivers(otInstance *aInstance)
|
||||
{
|
||||
sInstance = aInstance;
|
||||
|
||||
Reference in New Issue
Block a user