1 /*!
2  * \file      RegionIN865.c
3  *
4  * \brief     Region implementation for IN865
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2017 Semtech
16  *
17  *               ___ _____ _   ___ _  _____ ___  ___  ___ ___
18  *              / __|_   _/_\ / __| |/ / __/ _ \| _ \/ __| __|
19  *              \__ \ | |/ _ \ (__| ' <| _| (_) |   / (__| _|
20  *              |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
21  *              embedded.connectivity.solutions===============
22  *
23  * \endcode
24  *
25  * \author    Miguel Luis ( Semtech )
26  *
27  * \author    Gregory Cristian ( Semtech )
28  *
29  * \author    Daniel Jaeckle ( STACKFORCE )
30 */
31 #include "radio.h"
32 #include "RegionCommon.h"
33 #include "RegionIN865.h"
34 
35 // Definitions
36 #define CHANNELS_MASK_SIZE              1
37 
38 /*
39  * Non-volatile module context.
40  */
41 static RegionNvmDataGroup1_t* RegionNvmGroup1;
42 static RegionNvmDataGroup2_t* RegionNvmGroup2;
43 static Band_t* RegionBands;
44 
45 
VerifyRfFreq(uint32_t freq)46 static bool VerifyRfFreq( uint32_t freq )
47 {
48     // Check radio driver support
49     if( Radio.CheckRfFrequency( freq ) == false )
50     {
51         return false;
52     }
53 
54     if( ( freq < 865000000 ) || ( freq > 867000000 ) )
55     {
56         return false;
57     }
58     return true;
59 }
60 
GetTimeOnAir(int8_t datarate,uint16_t pktLen)61 static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen )
62 {
63     int8_t phyDr = DataratesIN865[datarate];
64     uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsIN865 );
65     TimerTime_t timeOnAir = 0;
66 
67     if( datarate == DR_7 )
68     { // High Speed FSK channel
69         timeOnAir = Radio.TimeOnAir( MODEM_FSK, bandwidth, phyDr * 1000, 0, 5, false, pktLen, true );
70     }
71     else
72     {
73         timeOnAir = Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true );
74     }
75     return timeOnAir;
76 }
77 
RegionIN865GetPhyParam(GetPhyParams_t * getPhy)78 PhyParam_t RegionIN865GetPhyParam( GetPhyParams_t* getPhy )
79 {
80     PhyParam_t phyParam = { 0 };
81 
82     switch( getPhy->Attribute )
83     {
84         case PHY_MIN_RX_DR:
85         {
86             phyParam.Value = IN865_RX_MIN_DATARATE;
87             break;
88         }
89         case PHY_MIN_TX_DR:
90         {
91             phyParam.Value = IN865_TX_MIN_DATARATE;
92             break;
93         }
94         case PHY_DEF_TX_DR:
95         {
96             phyParam.Value = IN865_DEFAULT_DATARATE;
97             break;
98         }
99         case PHY_NEXT_LOWER_TX_DR:
100         {
101             RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams =
102             {
103                 .CurrentDr = getPhy->Datarate,
104                 .MaxDr = ( int8_t )IN865_TX_MAX_DATARATE,
105                 .MinDr = ( int8_t )IN865_TX_MIN_DATARATE,
106                 .NbChannels = IN865_MAX_NB_CHANNELS,
107                 .ChannelsMask = RegionNvmGroup2->ChannelsMask,
108                 .Channels = RegionNvmGroup2->Channels,
109             };
110             phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams );
111             break;
112         }
113         case PHY_MAX_TX_POWER:
114         {
115             phyParam.Value = IN865_MAX_TX_POWER;
116             break;
117         }
118         case PHY_DEF_TX_POWER:
119         {
120             phyParam.Value = IN865_DEFAULT_TX_POWER;
121             break;
122         }
123         case PHY_DEF_ADR_ACK_LIMIT:
124         {
125             phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT;
126             break;
127         }
128         case PHY_DEF_ADR_ACK_DELAY:
129         {
130             phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY;
131             break;
132         }
133         case PHY_MAX_PAYLOAD:
134         {
135             phyParam.Value = MaxPayloadOfDatarateIN865[getPhy->Datarate];
136             break;
137         }
138         case PHY_DUTY_CYCLE:
139         {
140             phyParam.Value = IN865_DUTY_CYCLE_ENABLED;
141             break;
142         }
143         case PHY_MAX_RX_WINDOW:
144         {
145             phyParam.Value = IN865_MAX_RX_WINDOW;
146             break;
147         }
148         case PHY_RECEIVE_DELAY1:
149         {
150             phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1;
151             break;
152         }
153         case PHY_RECEIVE_DELAY2:
154         {
155             phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2;
156             break;
157         }
158         case PHY_JOIN_ACCEPT_DELAY1:
159         {
160             phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1;
161             break;
162         }
163         case PHY_JOIN_ACCEPT_DELAY2:
164         {
165             phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2;
166             break;
167         }
168         case PHY_RETRANSMIT_TIMEOUT:
169         {
170             phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) );
171             break;
172         }
173         case PHY_DEF_DR1_OFFSET:
174         {
175             phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET;
176             break;
177         }
178         case PHY_DEF_RX2_FREQUENCY:
179         {
180             phyParam.Value = IN865_RX_WND_2_FREQ;
181             break;
182         }
183         case PHY_DEF_RX2_DR:
184         {
185             phyParam.Value = IN865_RX_WND_2_DR;
186             break;
187         }
188         case PHY_CHANNELS_MASK:
189         {
190             phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask;
191             break;
192         }
193         case PHY_CHANNELS_DEFAULT_MASK:
194         {
195             phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask;
196             break;
197         }
198         case PHY_MAX_NB_CHANNELS:
199         {
200             phyParam.Value = IN865_MAX_NB_CHANNELS;
201             break;
202         }
203         case PHY_CHANNELS:
204         {
205             phyParam.Channels = RegionNvmGroup2->Channels;
206             break;
207         }
208         case PHY_DEF_UPLINK_DWELL_TIME:
209         {
210             phyParam.Value = IN865_DEFAULT_UPLINK_DWELL_TIME;
211             break;
212         }
213         case PHY_DEF_DOWNLINK_DWELL_TIME:
214         {
215             phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME;
216             break;
217         }
218         case PHY_DEF_MAX_EIRP:
219         {
220             phyParam.fValue = IN865_DEFAULT_MAX_EIRP;
221             break;
222         }
223         case PHY_DEF_ANTENNA_GAIN:
224         {
225             phyParam.fValue = IN865_DEFAULT_ANTENNA_GAIN;
226             break;
227         }
228         case PHY_BEACON_CHANNEL_FREQ:
229         {
230             phyParam.Value = IN865_BEACON_CHANNEL_FREQ;
231             break;
232         }
233         case PHY_BEACON_FORMAT:
234         {
235             phyParam.BeaconFormat.BeaconSize = IN865_BEACON_SIZE;
236             phyParam.BeaconFormat.Rfu1Size = IN865_RFU1_SIZE;
237             phyParam.BeaconFormat.Rfu2Size = IN865_RFU2_SIZE;
238             break;
239         }
240         case PHY_BEACON_CHANNEL_DR:
241         {
242             phyParam.Value = IN865_BEACON_CHANNEL_DR;
243             break;
244         }
245         case PHY_PING_SLOT_CHANNEL_FREQ:
246         {
247             phyParam.Value = IN865_PING_SLOT_CHANNEL_FREQ;
248             break;
249         }
250         case PHY_PING_SLOT_CHANNEL_DR:
251         {
252             phyParam.Value = IN865_PING_SLOT_CHANNEL_DR;
253             break;
254         }
255         case PHY_SF_FROM_DR:
256         {
257             phyParam.Value = DataratesIN865[getPhy->Datarate];
258             break;
259         }
260         case PHY_BW_FROM_DR:
261         {
262             phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsIN865 );
263             break;
264         }
265         default:
266         {
267             break;
268         }
269     }
270 
271     return phyParam;
272 }
273 
RegionIN865SetBandTxDone(SetBandTxDoneParams_t * txDone)274 void RegionIN865SetBandTxDone( SetBandTxDoneParams_t* txDone )
275 {
276     RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band],
277                                txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp );
278 }
279 
RegionIN865InitDefaults(InitDefaultsParams_t * params)280 void RegionIN865InitDefaults( InitDefaultsParams_t* params )
281 {
282     Band_t bands[IN865_MAX_NB_BANDS] =
283     {
284         IN865_BAND0
285     };
286 
287     switch( params->Type )
288     {
289         case INIT_TYPE_DEFAULTS:
290         {
291             if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) )
292             {
293                 return;
294             }
295 
296             RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1;
297             RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2;
298             RegionBands = (Band_t*) params->Bands;
299 
300             // Initialize bands
301             memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * IN865_MAX_NB_BANDS );
302 
303             // Default channels
304             RegionNvmGroup2->Channels[0] = ( ChannelParams_t ) IN865_LC1;
305             RegionNvmGroup2->Channels[1] = ( ChannelParams_t ) IN865_LC2;
306             RegionNvmGroup2->Channels[2] = ( ChannelParams_t ) IN865_LC3;
307 
308             // Initialize the channels default mask
309             RegionNvmGroup2->ChannelsDefaultMask[0] = LC( 1 ) + LC( 2 ) + LC( 3 );
310 
311             // Default ChannelsMask
312             RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
313             break;
314         }
315         case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS:
316         {
317             // Reset Channels Rx1Frequency to default 0
318             RegionNvmGroup2->Channels[0].Rx1Frequency = 0;
319             RegionNvmGroup2->Channels[1].Rx1Frequency = 0;
320             RegionNvmGroup2->Channels[2].Rx1Frequency = 0;
321             // Default ChannelsMask
322             RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
323             break;
324         }
325         case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS:
326         {
327             // Restore channels default mask
328             RegionNvmGroup2->ChannelsMask[0] |= RegionNvmGroup2->ChannelsDefaultMask[0];
329             break;
330         }
331         default:
332         {
333             break;
334         }
335     }
336 }
337 
RegionIN865Verify(VerifyParams_t * verify,PhyAttribute_t phyAttribute)338 bool RegionIN865Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
339 {
340     switch( phyAttribute )
341     {
342         case PHY_FREQUENCY:
343         {
344             return VerifyRfFreq( verify->Frequency );
345         }
346         case PHY_TX_DR:
347         {
348             if( verify->DatarateParams.Datarate == DR_6 )
349             {// DR_6 is not supported by this region
350                 return false;
351             }
352             else
353             {
354                 return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE );
355             }
356         }
357         case PHY_DEF_TX_DR:
358         {
359             return RegionCommonValueInRange( verify->DatarateParams.Datarate, DR_0, DR_5 );
360         }
361         case PHY_RX_DR:
362         {
363             if( verify->DatarateParams.Datarate == DR_6 )
364             {// DR_6 is not supported by this region
365                 return false;
366             }
367             else
368             {
369                 return RegionCommonValueInRange( verify->DatarateParams.Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE );
370             }
371         }
372         case PHY_DEF_TX_POWER:
373         case PHY_TX_POWER:
374         {
375             // Remark: switched min and max!
376             return RegionCommonValueInRange( verify->TxPower, IN865_MAX_TX_POWER, IN865_MIN_TX_POWER );
377         }
378         case PHY_DUTY_CYCLE:
379         {
380             return IN865_DUTY_CYCLE_ENABLED;
381         }
382         default:
383             return false;
384     }
385 }
386 
RegionIN865ApplyCFList(ApplyCFListParams_t * applyCFList)387 void RegionIN865ApplyCFList( ApplyCFListParams_t* applyCFList )
388 {
389     ChannelParams_t newChannel;
390     ChannelAddParams_t channelAdd;
391     ChannelRemoveParams_t channelRemove;
392 
393     // Setup default datarate range
394     newChannel.DrRange.Value = ( DR_5 << 4 ) | DR_0;
395 
396     // Size of the optional CF list
397     if( applyCFList->Size != 16 )
398     {
399         return;
400     }
401 
402     // Last byte CFListType must be 0 to indicate the CFList contains a list of frequencies
403     if( applyCFList->Payload[15] != 0 )
404     {
405         return;
406     }
407 
408     // Last byte is RFU, don't take it into account
409     for( uint8_t i = 0, chanIdx = IN865_NUMB_DEFAULT_CHANNELS; chanIdx < IN865_MAX_NB_CHANNELS; i+=3, chanIdx++ )
410     {
411         if( chanIdx < ( IN865_NUMB_CHANNELS_CF_LIST + IN865_NUMB_DEFAULT_CHANNELS ) )
412         {
413             // Channel frequency
414             newChannel.Frequency = (uint32_t) applyCFList->Payload[i];
415             newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 1] << 8 );
416             newChannel.Frequency |= ( (uint32_t) applyCFList->Payload[i + 2] << 16 );
417             newChannel.Frequency *= 100;
418 
419             // Initialize alternative frequency to 0
420             newChannel.Rx1Frequency = 0;
421         }
422         else
423         {
424             newChannel.Frequency = 0;
425             newChannel.DrRange.Value = 0;
426             newChannel.Rx1Frequency = 0;
427         }
428 
429         if( newChannel.Frequency != 0 )
430         {
431             channelAdd.NewChannel = &newChannel;
432             channelAdd.ChannelId = chanIdx;
433 
434             // Try to add all channels
435             RegionIN865ChannelAdd( &channelAdd );
436         }
437         else
438         {
439             channelRemove.ChannelId = chanIdx;
440 
441             RegionIN865ChannelsRemove( &channelRemove );
442         }
443     }
444 }
445 
RegionIN865ChanMaskSet(ChanMaskSetParams_t * chanMaskSet)446 bool RegionIN865ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
447 {
448     switch( chanMaskSet->ChannelsMaskType )
449     {
450         case CHANNELS_MASK:
451         {
452             RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, 1 );
453             break;
454         }
455         case CHANNELS_DEFAULT_MASK:
456         {
457             RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, 1 );
458             break;
459         }
460         default:
461             return false;
462     }
463     return true;
464 }
465 
RegionIN865ComputeRxWindowParameters(int8_t datarate,uint8_t minRxSymbols,uint32_t rxError,RxConfigParams_t * rxConfigParams)466 void RegionIN865ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
467 {
468     uint32_t tSymbolInUs = 0;
469 
470     // Get the datarate, perform a boundary check
471     rxConfigParams->Datarate = MIN( datarate, IN865_RX_MAX_DATARATE );
472     rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsIN865 );
473 
474     if( rxConfigParams->Datarate == DR_7 )
475     { // FSK
476         tSymbolInUs = RegionCommonComputeSymbolTimeFsk( DataratesIN865[rxConfigParams->Datarate] );
477     }
478     else
479     { // LoRa
480         tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesIN865[rxConfigParams->Datarate], BandwidthsIN865[rxConfigParams->Datarate] );
481     }
482 
483     RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
484 }
485 
RegionIN865RxConfig(RxConfigParams_t * rxConfig,int8_t * datarate)486 bool RegionIN865RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
487 {
488     RadioModems_t modem;
489     int8_t dr = rxConfig->Datarate;
490     int8_t phyDr = 0;
491     uint32_t frequency = rxConfig->Frequency;
492 
493     if( Radio.GetStatus( ) != RF_IDLE )
494     {
495         return false;
496     }
497 
498     if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
499     {
500         // Apply window 1 frequency
501         frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Frequency;
502         // Apply the alternative RX 1 window frequency, if it is available
503         if( RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency != 0 )
504         {
505             frequency = RegionNvmGroup2->Channels[rxConfig->Channel].Rx1Frequency;
506         }
507     }
508 
509     // Read the physical datarate from the datarates table
510     phyDr = DataratesIN865[dr];
511 
512     Radio.SetChannel( frequency );
513 
514     // Radio configuration
515     if( dr == DR_7 )
516     {
517         modem = MODEM_FSK;
518         Radio.SetRxConfig( modem, 50000, phyDr * 1000, 0, 83333, 5, rxConfig->WindowTimeout, false, 0, true, 0, 0, false, rxConfig->RxContinuous );
519     }
520     else
521     {
522         modem = MODEM_LORA;
523         Radio.SetRxConfig( modem, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
524     }
525 
526     Radio.SetMaxPayloadLength( modem, MaxPayloadOfDatarateIN865[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE );
527 
528     *datarate = (uint8_t) dr;
529     return true;
530 }
531 
RegionIN865TxConfig(TxConfigParams_t * txConfig,int8_t * txPower,TimerTime_t * txTimeOnAir)532 bool RegionIN865TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
533 {
534     RadioModems_t modem;
535     int8_t phyDr = DataratesIN865[txConfig->Datarate];
536     int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower );
537     uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsIN865 );
538     int8_t phyTxPower = 0;
539 
540     // Calculate physical TX power
541     phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
542 
543     // Setup the radio frequency
544     Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency );
545 
546     if( txConfig->Datarate == DR_7 )
547     { // High Speed FSK channel
548         modem = MODEM_FSK;
549         Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 );
550     }
551     else
552     {
553         modem = MODEM_LORA;
554         Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
555     }
556 
557     // Update time-on-air
558     *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen );
559 
560     // Setup maximum payload lenght of the radio driver
561     Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
562 
563     *txPower = txPowerLimited;
564     return true;
565 }
566 
RegionIN865LinkAdrReq(LinkAdrReqParams_t * linkAdrReq,int8_t * drOut,int8_t * txPowOut,uint8_t * nbRepOut,uint8_t * nbBytesParsed)567 uint8_t RegionIN865LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
568 {
569     uint8_t status = 0x07;
570     RegionCommonLinkAdrParams_t linkAdrParams = { 0 };
571     uint8_t nextIndex = 0;
572     uint8_t bytesProcessed = 0;
573     uint16_t chMask = 0;
574     GetPhyParams_t getPhy;
575     PhyParam_t phyParam;
576     RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
577 
578     while( bytesProcessed < linkAdrReq->PayloadSize )
579     {
580         // Get ADR request parameters
581         nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
582 
583         if( nextIndex == 0 )
584             break; // break loop, since no more request has been found
585 
586         // Update bytes processed
587         bytesProcessed += nextIndex;
588 
589         // Revert status, as we only check the last ADR request for the channel mask KO
590         status = 0x07;
591 
592         // Setup temporary channels mask
593         chMask = linkAdrParams.ChMask;
594 
595         // Verify channels mask
596         if( ( linkAdrParams.ChMaskCtrl == 0 ) && ( chMask == 0 ) )
597         {
598             status &= 0xFE; // Channel mask KO
599         }
600         else if( ( ( linkAdrParams.ChMaskCtrl >= 1 ) && ( linkAdrParams.ChMaskCtrl <= 5 )) ||
601                 ( linkAdrParams.ChMaskCtrl >= 7 ) )
602         {
603             // RFU
604             status &= 0xFE; // Channel mask KO
605         }
606         else
607         {
608             for( uint8_t i = 0; i < IN865_MAX_NB_CHANNELS; i++ )
609             {
610                 if( linkAdrParams.ChMaskCtrl == 6 )
611                 {
612                     if( RegionNvmGroup2->Channels[i].Frequency != 0 )
613                     {
614                         chMask |= 1 << i;
615                     }
616                 }
617                 else
618                 {
619                     if( ( ( chMask & ( 1 << i ) ) != 0 ) &&
620                         ( RegionNvmGroup2->Channels[i].Frequency == 0 ) )
621                     {// Trying to enable an undefined channel
622                         status &= 0xFE; // Channel mask KO
623                     }
624                 }
625             }
626         }
627     }
628 
629     if( linkAdrParams.Datarate != DR_6 )
630     {
631         // Get the minimum possible datarate
632         getPhy.Attribute = PHY_MIN_TX_DR;
633         getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
634         phyParam = RegionIN865GetPhyParam( &getPhy );
635 
636         linkAdrVerifyParams.Status = status;
637         linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
638         linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
639         linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
640         linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
641         linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
642         linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
643         linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
644         linkAdrVerifyParams.NbChannels = IN865_MAX_NB_CHANNELS;
645         linkAdrVerifyParams.ChannelsMask = &chMask;
646         linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
647         linkAdrVerifyParams.MaxDatarate = IN865_TX_MAX_DATARATE;
648         linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels;
649         linkAdrVerifyParams.MinTxPower = IN865_MIN_TX_POWER;
650         linkAdrVerifyParams.MaxTxPower = IN865_MAX_TX_POWER;
651         linkAdrVerifyParams.Version = linkAdrReq->Version;
652 
653         // Verify the parameters and update, if necessary
654         status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
655     }
656     else
657     {// DR_6 is not supported by this region
658         status &= 0xFD; // Datarate KO
659     }
660 
661     // Update channelsMask if everything is correct
662     if( status == 0x07 )
663     {
664         // Set the channels mask to a default value
665         memset1( ( uint8_t* ) RegionNvmGroup2->ChannelsMask, 0, sizeof( RegionNvmGroup2->ChannelsMask ) );
666         // Update the channels mask
667         RegionNvmGroup2->ChannelsMask[0] = chMask;
668     }
669 
670     // Update status variables
671     *drOut = linkAdrParams.Datarate;
672     *txPowOut = linkAdrParams.TxPower;
673     *nbRepOut = linkAdrParams.NbRep;
674     *nbBytesParsed = bytesProcessed;
675 
676     return status;
677 }
678 
RegionIN865RxParamSetupReq(RxParamSetupReqParams_t * rxParamSetupReq)679 uint8_t RegionIN865RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
680 {
681     uint8_t status = 0x07;
682 
683     // Verify radio frequency
684     if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
685     {
686         status &= 0xFE; // Channel frequency KO
687     }
688 
689     // Verify datarate
690     if( ( RegionCommonValueInRange( rxParamSetupReq->Datarate, IN865_RX_MIN_DATARATE, IN865_RX_MAX_DATARATE ) == false ) ||
691         // DR_6 is not supported by this region
692         ( rxParamSetupReq->Datarate == DR_6 ) )
693     {
694         status &= 0xFD; // Datarate KO
695     }
696 
697     // Verify datarate offset
698     if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, IN865_MIN_RX1_DR_OFFSET, IN865_MAX_RX1_DR_OFFSET ) == false )
699     {
700         status &= 0xFB; // Rx1DrOffset range KO
701     }
702 
703     return status;
704 }
705 
RegionIN865NewChannelReq(NewChannelReqParams_t * newChannelReq)706 int8_t RegionIN865NewChannelReq( NewChannelReqParams_t* newChannelReq )
707 {
708     uint8_t status = 0x03;
709     ChannelAddParams_t channelAdd;
710     ChannelRemoveParams_t channelRemove;
711 
712     if( newChannelReq->NewChannel->Frequency == 0 )
713     {
714         channelRemove.ChannelId = newChannelReq->ChannelId;
715 
716         // Remove
717         if( RegionIN865ChannelsRemove( &channelRemove ) == false )
718         {
719             status &= 0xFC;
720         }
721     }
722     else
723     {
724         channelAdd.NewChannel = newChannelReq->NewChannel;
725         channelAdd.ChannelId = newChannelReq->ChannelId;
726 
727         switch( RegionIN865ChannelAdd( &channelAdd ) )
728         {
729             case LORAMAC_STATUS_OK:
730             {
731                 break;
732             }
733             case LORAMAC_STATUS_FREQUENCY_INVALID:
734             {
735                 status &= 0xFE;
736                 break;
737             }
738             case LORAMAC_STATUS_DATARATE_INVALID:
739             {
740                 status &= 0xFD;
741                 break;
742             }
743             case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
744             {
745                 status &= 0xFC;
746                 break;
747             }
748             default:
749             {
750                 status &= 0xFC;
751                 break;
752             }
753         }
754     }
755 
756     return status;
757 }
758 
RegionIN865TxParamSetupReq(TxParamSetupReqParams_t * txParamSetupReq)759 int8_t RegionIN865TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
760 {
761     // Do not accept the request
762     return -1;
763 }
764 
RegionIN865DlChannelReq(DlChannelReqParams_t * dlChannelReq)765 int8_t RegionIN865DlChannelReq( DlChannelReqParams_t* dlChannelReq )
766 {
767     uint8_t status = 0x03;
768 
769     // Verify if the frequency is supported
770     if( VerifyRfFreq( dlChannelReq->Rx1Frequency ) == false )
771     {
772         status &= 0xFE;
773     }
774 
775     // Verify if an uplink frequency exists
776     if( RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Frequency == 0 )
777     {
778         status &= 0xFD;
779     }
780 
781     // Apply Rx1 frequency, if the status is OK
782     if( status == 0x03 )
783     {
784         RegionNvmGroup2->Channels[dlChannelReq->ChannelId].Rx1Frequency = dlChannelReq->Rx1Frequency;
785     }
786 
787     return status;
788 }
789 
RegionIN865AlternateDr(int8_t currentDr,AlternateDrType_t type)790 int8_t RegionIN865AlternateDr( int8_t currentDr, AlternateDrType_t type )
791 {
792     return currentDr;
793 }
794 
RegionIN865NextChannel(NextChanParams_t * nextChanParams,uint8_t * channel,TimerTime_t * time,TimerTime_t * aggregatedTimeOff)795 LoRaMacStatus_t RegionIN865NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
796 {
797     uint8_t nbEnabledChannels = 0;
798     uint8_t nbRestrictedChannels = 0;
799     uint8_t enabledChannels[IN865_MAX_NB_CHANNELS] = { 0 };
800     RegionCommonIdentifyChannelsParam_t identifyChannelsParam;
801     RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams;
802     LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND;
803     uint16_t joinChannels = IN865_JOIN_CHANNELS;
804 
805     if( RegionCommonCountChannels( RegionNvmGroup2->ChannelsMask, 0, 1 ) == 0 )
806     { // Reactivate default channels
807         RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
808     }
809 
810     // Search how many channels are enabled
811     countChannelsParams.Joined = nextChanParams->Joined;
812     countChannelsParams.Datarate = nextChanParams->Datarate;
813     countChannelsParams.ChannelsMask = RegionNvmGroup2->ChannelsMask;
814     countChannelsParams.Channels = RegionNvmGroup2->Channels;
815     countChannelsParams.Bands = RegionBands;
816     countChannelsParams.MaxNbChannels = IN865_MAX_NB_CHANNELS;
817     countChannelsParams.JoinChannels = &joinChannels;
818 
819     identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff;
820     identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx;
821     identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled;
822     identifyChannelsParam.MaxBands = IN865_MAX_NB_BANDS;
823 
824     identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp;
825     identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest;
826     identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen );
827 
828     identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams;
829 
830     status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels,
831                                            &nbEnabledChannels, &nbRestrictedChannels, time );
832 
833     if( status == LORAMAC_STATUS_OK )
834     {
835         // We found a valid channel
836         *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
837     }
838     else if( status == LORAMAC_STATUS_NO_CHANNEL_FOUND )
839     {
840         // Datarate not supported by any channel, restore defaults
841         RegionNvmGroup2->ChannelsMask[0] |= LC( 1 ) + LC( 2 ) + LC( 3 );
842     }
843     return status;
844 }
845 
RegionIN865ChannelAdd(ChannelAddParams_t * channelAdd)846 LoRaMacStatus_t RegionIN865ChannelAdd( ChannelAddParams_t* channelAdd )
847 {
848     bool drInvalid = false;
849     bool freqInvalid = false;
850     uint8_t id = channelAdd->ChannelId;
851 
852     if( id < IN865_NUMB_DEFAULT_CHANNELS )
853     {
854         return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
855     }
856 
857     if( id >= IN865_MAX_NB_CHANNELS )
858     {
859         return LORAMAC_STATUS_PARAMETER_INVALID;
860     }
861 
862     // Validate the datarate range
863     if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Min, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
864     {
865         drInvalid = true;
866     }
867     if( RegionCommonValueInRange( channelAdd->NewChannel->DrRange.Fields.Max, IN865_TX_MIN_DATARATE, IN865_TX_MAX_DATARATE ) == false )
868     {
869         drInvalid = true;
870     }
871     if( channelAdd->NewChannel->DrRange.Fields.Min > channelAdd->NewChannel->DrRange.Fields.Max )
872     {
873         drInvalid = true;
874     }
875 
876     // Check frequency
877     if( freqInvalid == false )
878     {
879         if( VerifyRfFreq( channelAdd->NewChannel->Frequency ) == false )
880         {
881             freqInvalid = true;
882         }
883     }
884 
885     // Check status
886     if( ( drInvalid == true ) && ( freqInvalid == true ) )
887     {
888         return LORAMAC_STATUS_FREQ_AND_DR_INVALID;
889     }
890     if( drInvalid == true )
891     {
892         return LORAMAC_STATUS_DATARATE_INVALID;
893     }
894     if( freqInvalid == true )
895     {
896         return LORAMAC_STATUS_FREQUENCY_INVALID;
897     }
898 
899     memcpy1( ( uint8_t* ) &(RegionNvmGroup2->Channels[id]), ( uint8_t* ) channelAdd->NewChannel, sizeof( RegionNvmGroup2->Channels[id] ) );
900     RegionNvmGroup2->Channels[id].Band = 0;
901     RegionNvmGroup2->ChannelsMask[0] |= ( 1 << id );
902     return LORAMAC_STATUS_OK;
903 }
904 
RegionIN865ChannelsRemove(ChannelRemoveParams_t * channelRemove)905 bool RegionIN865ChannelsRemove( ChannelRemoveParams_t* channelRemove  )
906 {
907     uint8_t id = channelRemove->ChannelId;
908 
909     if( id < IN865_NUMB_DEFAULT_CHANNELS )
910     {
911         return false;
912     }
913 
914     // Remove the channel from the list of channels
915     RegionNvmGroup2->Channels[id] = ( ChannelParams_t ){ 0, 0, { 0 }, 0 };
916 
917     return RegionCommonChanDisable( RegionNvmGroup2->ChannelsMask, id, IN865_MAX_NB_CHANNELS );
918 }
919 
RegionIN865ApplyDrOffset(uint8_t downlinkDwellTime,int8_t dr,int8_t drOffset)920 uint8_t RegionIN865ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
921 {
922     int8_t datarate = EffectiveRx1DrOffsetIN865[dr][drOffset];
923 
924     if( ( datarate < 0 ) || ( dr == DR_6 ) )
925     {
926         datarate = DR_0;
927     }
928     return datarate;
929 }
930 
RegionIN865RxBeaconSetup(RxBeaconSetup_t * rxBeaconSetup,uint8_t * outDr)931 void RegionIN865RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
932 {
933     RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
934 
935     regionCommonRxBeaconSetup.Datarates = DataratesIN865;
936     regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
937     regionCommonRxBeaconSetup.BeaconSize = IN865_BEACON_SIZE;
938     regionCommonRxBeaconSetup.BeaconDatarate = IN865_BEACON_CHANNEL_DR;
939     regionCommonRxBeaconSetup.BeaconChannelBW = IN865_BEACON_CHANNEL_BW;
940     regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
941     regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
942 
943     RegionCommonRxBeaconSetup( &regionCommonRxBeaconSetup );
944 
945     // Store downlink datarate
946     *outDr = IN865_BEACON_CHANNEL_DR;
947 }
948