mirror of https://github.com/ARMmbed/mbed-os.git
445 lines
16 KiB
C++
445 lines
16 KiB
C++
/**
|
|
/ _____) _ | |
|
|
( (____ _____ ____ _| |_ _____ ____| |__
|
|
\____ \| ___ | (_ _) ___ |/ ___) _ \
|
|
_____) ) ____| | | || |_| ____( (___| | | |
|
|
(______/|_____)_|_|_| \__)_____)\____)_| |_|
|
|
(C)2013 Semtech
|
|
___ _____ _ ___ _ _____ ___ ___ ___ ___
|
|
/ __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
|
|
\__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
|
|
|___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
|
|
embedded.connectivity.solutions===============
|
|
|
|
Description: LoRa MAC layer implementation
|
|
|
|
License: Revised BSD License, see LICENSE.TXT file include in the project
|
|
|
|
Maintainer: Miguel Luis ( Semtech ), Gregory Cristian ( Semtech ) and Daniel Jaeckle ( STACKFORCE )
|
|
|
|
Copyright (c) 2017, Arm Limited and affiliates.
|
|
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#include "LoRaMacCommand.h"
|
|
#include "LoRaMac.h"
|
|
|
|
#if defined(FEATURE_COMMON_PAL)
|
|
#include "mbed_trace.h"
|
|
#define TRACE_GROUP "LMACC"
|
|
#else
|
|
#define tr_debug(...) (void(0)) //dummies if feature common pal is not added
|
|
#define tr_info(...) (void(0)) //dummies if feature common pal is not added
|
|
#define tr_error(...) (void(0)) //dummies if feature common pal is not added
|
|
#endif //defined(FEATURE_COMMON_PAL)
|
|
|
|
/*!
|
|
* LoRaMAC max EIRP (dBm) table.
|
|
*/
|
|
static const uint8_t LoRaMacMaxEirpTable[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 };
|
|
|
|
|
|
LoRaMacCommand::LoRaMacCommand(LoRaMac& lora_mac)
|
|
: _lora_mac(lora_mac)
|
|
{
|
|
MacCommandsInNextTx = false;
|
|
MacCommandsBufferIndex = 0;
|
|
MacCommandsBufferToRepeatIndex = 0;
|
|
//uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
|
|
//uint8_t MacCommandsBufferToRepeat[LORA_MAC_COMMAND_MAX_LENGTH];
|
|
}
|
|
|
|
LoRaMacCommand::~LoRaMacCommand()
|
|
{
|
|
}
|
|
|
|
LoRaMacStatus_t LoRaMacCommand::AddMacCommand(uint8_t cmd, uint8_t p1, uint8_t p2)
|
|
{
|
|
LoRaMacStatus_t status = LORAMAC_STATUS_BUSY;
|
|
// The maximum buffer length must take MAC commands to re-send into account.
|
|
const uint8_t bufLen = LORA_MAC_COMMAND_MAX_LENGTH - MacCommandsBufferToRepeatIndex;
|
|
|
|
switch( cmd )
|
|
{
|
|
case MOTE_MAC_LINK_CHECK_REQ:
|
|
if( MacCommandsBufferIndex < bufLen )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// No payload for this command
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_LINK_ADR_ANS:
|
|
if( MacCommandsBufferIndex < ( bufLen - 1 ) )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// Margin
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_DUTY_CYCLE_ANS:
|
|
if( MacCommandsBufferIndex < bufLen )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// No payload for this answer
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_RX_PARAM_SETUP_ANS:
|
|
if( MacCommandsBufferIndex < ( bufLen - 1 ) )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// Status: Datarate ACK, Channel ACK
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
|
|
// This is a sticky MAC command answer. Setup indication
|
|
_lora_mac.SetMlmeScheduleUplinkIndication();
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_DEV_STATUS_ANS:
|
|
if( MacCommandsBufferIndex < ( bufLen - 2 ) )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// 1st byte Battery
|
|
// 2nd byte Margin
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p2;
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_NEW_CHANNEL_ANS:
|
|
if( MacCommandsBufferIndex < ( bufLen - 1 ) )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// Status: Datarate range OK, Channel frequency OK
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_RX_TIMING_SETUP_ANS:
|
|
if( MacCommandsBufferIndex < bufLen )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// No payload for this answer
|
|
// This is a sticky MAC command answer. Setup indication
|
|
_lora_mac.SetMlmeScheduleUplinkIndication();
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_TX_PARAM_SETUP_ANS:
|
|
if( MacCommandsBufferIndex < bufLen )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// No payload for this answer
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
case MOTE_MAC_DL_CHANNEL_ANS:
|
|
if( MacCommandsBufferIndex < bufLen )
|
|
{
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = cmd;
|
|
// Status: Uplink frequency exists, Channel frequency OK
|
|
MacCommandsBuffer[MacCommandsBufferIndex++] = p1;
|
|
// This is a sticky MAC command answer. Setup indication
|
|
_lora_mac.SetMlmeScheduleUplinkIndication();
|
|
status = LORAMAC_STATUS_OK;
|
|
}
|
|
break;
|
|
default:
|
|
return LORAMAC_STATUS_SERVICE_UNKNOWN;
|
|
}
|
|
if( status == LORAMAC_STATUS_OK )
|
|
{
|
|
MacCommandsInNextTx = true;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
void LoRaMacCommand::ClearCommandBuffer()
|
|
{
|
|
MacCommandsBufferIndex = 0;
|
|
}
|
|
|
|
uint8_t LoRaMacCommand::GetLength() const
|
|
{
|
|
return MacCommandsBufferIndex;
|
|
}
|
|
|
|
uint8_t *LoRaMacCommand::GetMacCommandsBuffer()
|
|
{
|
|
return MacCommandsBuffer;
|
|
}
|
|
|
|
void LoRaMacCommand::ParseMacCommandsToRepeat()
|
|
{
|
|
uint8_t i = 0;
|
|
uint8_t cmdCount = 0;
|
|
|
|
for( i = 0; i < MacCommandsBufferIndex; i++ )
|
|
{
|
|
switch( MacCommandsBuffer[i] )
|
|
{
|
|
// STICKY
|
|
case MOTE_MAC_DL_CHANNEL_ANS:
|
|
case MOTE_MAC_RX_PARAM_SETUP_ANS:
|
|
{ // 1 byte payload
|
|
MacCommandsBufferToRepeat[cmdCount++] = MacCommandsBuffer[i++];
|
|
MacCommandsBufferToRepeat[cmdCount++] = MacCommandsBuffer[i];
|
|
break;
|
|
}
|
|
case MOTE_MAC_RX_TIMING_SETUP_ANS:
|
|
{ // 0 byte payload
|
|
MacCommandsBufferToRepeat[cmdCount++] = MacCommandsBuffer[i];
|
|
break;
|
|
}
|
|
// NON-STICKY
|
|
case MOTE_MAC_DEV_STATUS_ANS:
|
|
{ // 2 bytes payload
|
|
i += 2;
|
|
break;
|
|
}
|
|
case MOTE_MAC_LINK_ADR_ANS:
|
|
case MOTE_MAC_NEW_CHANNEL_ANS:
|
|
{ // 1 byte payload
|
|
i++;
|
|
break;
|
|
}
|
|
case MOTE_MAC_TX_PARAM_SETUP_ANS:
|
|
case MOTE_MAC_DUTY_CYCLE_ANS:
|
|
case MOTE_MAC_LINK_CHECK_REQ:
|
|
{ // 0 byte payload
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( cmdCount > 0 ) {
|
|
MacCommandsInNextTx = true;
|
|
} else {
|
|
MacCommandsInNextTx = false;
|
|
}
|
|
}
|
|
|
|
|
|
void LoRaMacCommand::ClearRepeatBuffer()
|
|
{
|
|
MacCommandsBufferToRepeatIndex = 0;
|
|
}
|
|
|
|
void LoRaMacCommand::CopyRepeatCommandsToBuffer()
|
|
{
|
|
// Copy the MAC commands which must be re-send into the MAC command buffer
|
|
memcpy(&MacCommandsBuffer[MacCommandsBufferIndex], MacCommandsBufferToRepeat, MacCommandsBufferToRepeatIndex);
|
|
MacCommandsBufferIndex += MacCommandsBufferToRepeatIndex;
|
|
}
|
|
|
|
uint8_t LoRaMacCommand::GetRepeatLength() const
|
|
{
|
|
return MacCommandsBufferToRepeatIndex;
|
|
}
|
|
|
|
void LoRaMacCommand::ClearMacCommandsInNextTx()
|
|
{
|
|
MacCommandsInNextTx = false;
|
|
}
|
|
|
|
bool LoRaMacCommand::IsMacCommandsInNextTx() const
|
|
{
|
|
return MacCommandsInNextTx;
|
|
}
|
|
|
|
void LoRaMacCommand::ProcessMacCommands(uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, uint8_t snr,
|
|
MlmeConfirm_t& MlmeConfirm, LoRaMacCallback_t *LoRaMacCallbacks,
|
|
lora_mac_system_params_t &LoRaMacParams, LoRaPHY &lora_phy)
|
|
{
|
|
uint8_t status = 0;
|
|
|
|
while( macIndex < commandsSize )
|
|
{
|
|
// Decode Frame MAC commands
|
|
switch( payload[macIndex++] )
|
|
{
|
|
case SRV_MAC_LINK_CHECK_ANS:
|
|
MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
|
|
MlmeConfirm.DemodMargin = payload[macIndex++];
|
|
MlmeConfirm.NbGateways = payload[macIndex++];
|
|
break;
|
|
case SRV_MAC_LINK_ADR_REQ:
|
|
{
|
|
LinkAdrReqParams_t linkAdrReq;
|
|
int8_t linkAdrDatarate = DR_0;
|
|
int8_t linkAdrTxPower = TX_POWER_0;
|
|
uint8_t linkAdrNbRep = 0;
|
|
uint8_t linkAdrNbBytesParsed = 0;
|
|
|
|
// Fill parameter structure
|
|
linkAdrReq.Payload = &payload[macIndex - 1];
|
|
linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
|
|
linkAdrReq.AdrEnabled = LoRaMacParams.AdrCtrlOn;
|
|
linkAdrReq.UplinkDwellTime = LoRaMacParams.UplinkDwellTime;
|
|
linkAdrReq.CurrentDatarate = LoRaMacParams.ChannelsDatarate;
|
|
linkAdrReq.CurrentTxPower = LoRaMacParams.ChannelsTxPower;
|
|
linkAdrReq.CurrentNbRep = LoRaMacParams.ChannelsNbRep;
|
|
|
|
// Process the ADR requests
|
|
status = lora_phy.link_ADR_request(&linkAdrReq, &linkAdrDatarate,
|
|
&linkAdrTxPower, &linkAdrNbRep,
|
|
&linkAdrNbBytesParsed);
|
|
|
|
if( ( status & 0x07 ) == 0x07 )
|
|
{
|
|
LoRaMacParams.ChannelsDatarate = linkAdrDatarate;
|
|
LoRaMacParams.ChannelsTxPower = linkAdrTxPower;
|
|
LoRaMacParams.ChannelsNbRep = linkAdrNbRep;
|
|
}
|
|
|
|
// Add the answers to the buffer
|
|
for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
|
|
{
|
|
AddMacCommand( MOTE_MAC_LINK_ADR_ANS, status, 0 );
|
|
}
|
|
// Update MAC index
|
|
macIndex += linkAdrNbBytesParsed - 1;
|
|
}
|
|
break;
|
|
case SRV_MAC_DUTY_CYCLE_REQ:
|
|
LoRaMacParams.MaxDCycle = payload[macIndex++];
|
|
LoRaMacParams.AggregatedDCycle = 1 << LoRaMacParams.MaxDCycle;
|
|
AddMacCommand( MOTE_MAC_DUTY_CYCLE_ANS, 0, 0 );
|
|
break;
|
|
case SRV_MAC_RX_PARAM_SETUP_REQ:
|
|
{
|
|
RxParamSetupReqParams_t rxParamSetupReq;
|
|
status = 0x07;
|
|
|
|
rxParamSetupReq.DrOffset = ( payload[macIndex] >> 4 ) & 0x07;
|
|
rxParamSetupReq.Datarate = payload[macIndex] & 0x0F;
|
|
macIndex++;
|
|
|
|
rxParamSetupReq.Frequency = ( uint32_t )payload[macIndex++];
|
|
rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 8;
|
|
rxParamSetupReq.Frequency |= ( uint32_t )payload[macIndex++] << 16;
|
|
rxParamSetupReq.Frequency *= 100;
|
|
|
|
// Perform request on region
|
|
status = lora_phy.setup_rx_params(&rxParamSetupReq);
|
|
|
|
if( ( status & 0x07 ) == 0x07 )
|
|
{
|
|
LoRaMacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
|
|
LoRaMacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
|
|
LoRaMacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
|
|
}
|
|
AddMacCommand( MOTE_MAC_RX_PARAM_SETUP_ANS, status, 0 );
|
|
}
|
|
break;
|
|
case SRV_MAC_DEV_STATUS_REQ:
|
|
{
|
|
uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
|
|
if( ( LoRaMacCallbacks != NULL ) && ( LoRaMacCallbacks->GetBatteryLevel != NULL ) )
|
|
{
|
|
batteryLevel = LoRaMacCallbacks->GetBatteryLevel( );
|
|
}
|
|
AddMacCommand( MOTE_MAC_DEV_STATUS_ANS, batteryLevel, snr );
|
|
break;
|
|
}
|
|
case SRV_MAC_NEW_CHANNEL_REQ:
|
|
{
|
|
NewChannelReqParams_t newChannelReq;
|
|
ChannelParams_t chParam;
|
|
status = 0x03;
|
|
|
|
newChannelReq.ChannelId = payload[macIndex++];
|
|
newChannelReq.NewChannel = &chParam;
|
|
|
|
chParam.Frequency = ( uint32_t )payload[macIndex++];
|
|
chParam.Frequency |= ( uint32_t )payload[macIndex++] << 8;
|
|
chParam.Frequency |= ( uint32_t )payload[macIndex++] << 16;
|
|
chParam.Frequency *= 100;
|
|
chParam.Rx1Frequency = 0;
|
|
chParam.DrRange.Value = payload[macIndex++];
|
|
|
|
status = lora_phy.request_new_channel(&newChannelReq);
|
|
|
|
AddMacCommand( MOTE_MAC_NEW_CHANNEL_ANS, status, 0 );
|
|
}
|
|
break;
|
|
case SRV_MAC_RX_TIMING_SETUP_REQ:
|
|
{
|
|
uint8_t delay = payload[macIndex++] & 0x0F;
|
|
|
|
if( delay == 0 )
|
|
{
|
|
delay++;
|
|
}
|
|
LoRaMacParams.ReceiveDelay1 = delay * 1000;
|
|
LoRaMacParams.ReceiveDelay2 = LoRaMacParams.ReceiveDelay1 + 1000;
|
|
AddMacCommand( MOTE_MAC_RX_TIMING_SETUP_ANS, 0, 0 );
|
|
}
|
|
break;
|
|
case SRV_MAC_TX_PARAM_SETUP_REQ:
|
|
{
|
|
TxParamSetupReqParams_t txParamSetupReq;
|
|
uint8_t eirpDwellTime = payload[macIndex++];
|
|
|
|
txParamSetupReq.UplinkDwellTime = 0;
|
|
txParamSetupReq.DownlinkDwellTime = 0;
|
|
|
|
if( ( eirpDwellTime & 0x20 ) == 0x20 )
|
|
{
|
|
txParamSetupReq.DownlinkDwellTime = 1;
|
|
}
|
|
if( ( eirpDwellTime & 0x10 ) == 0x10 )
|
|
{
|
|
txParamSetupReq.UplinkDwellTime = 1;
|
|
}
|
|
txParamSetupReq.MaxEirp = eirpDwellTime & 0x0F;
|
|
|
|
// Check the status for correctness
|
|
if( lora_phy.setup_tx_params(&txParamSetupReq ) != -1 )
|
|
{
|
|
// Accept command
|
|
LoRaMacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
|
|
LoRaMacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
|
|
LoRaMacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
|
|
// Add command response
|
|
AddMacCommand( MOTE_MAC_TX_PARAM_SETUP_ANS, 0, 0 );
|
|
}
|
|
}
|
|
break;
|
|
case SRV_MAC_DL_CHANNEL_REQ:
|
|
{
|
|
DlChannelReqParams_t dlChannelReq;
|
|
status = 0x03;
|
|
|
|
dlChannelReq.ChannelId = payload[macIndex++];
|
|
dlChannelReq.Rx1Frequency = ( uint32_t )payload[macIndex++];
|
|
dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 8;
|
|
dlChannelReq.Rx1Frequency |= ( uint32_t )payload[macIndex++] << 16;
|
|
dlChannelReq.Rx1Frequency *= 100;
|
|
|
|
status = lora_phy.dl_channel_request(&dlChannelReq);
|
|
|
|
AddMacCommand( MOTE_MAC_DL_CHANNEL_ANS, status, 0 );
|
|
}
|
|
break;
|
|
default:
|
|
// Unknown command. ABORT MAC commands processing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LoRaMacCommand::IsStickyMacCommandPending()
|
|
{
|
|
if( MacCommandsBufferToRepeatIndex > 0 )
|
|
{
|
|
// Sticky MAC commands pending
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|