mbed-os/features/lorawan/lorastack/mac/LoRaMacCommand.cpp

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;
}