1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/lupyuen-LoRaMac-node-nuttx

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
Клонировать/Скачать
LoRaMac.c 170 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
Miguel Luis Отправлено 4 лет назад 243864e
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849
/*!
* \file LoRaMac.c
*
* \brief LoRa MAC layer implementation
*
* \copyright Revised BSD License, see section \ref LICENSE.
*
* \code
* ______ _
* / _____) _ | |
* ( (____ _____ ____ _| |_ _____ ____| |__
* \____ \| ___ | (_ _) ___ |/ ___) _ \
* _____) ) ____| | | || |_| ____( (___| | | |
* (______/|_____)_|_|_| \__)_____)\____)_| |_|
* (C)2013-2017 Semtech
*
* ___ _____ _ ___ _ _____ ___ ___ ___ ___
* / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
* \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
* |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
* embedded.connectivity.solutions===============
*
* \endcode
*
* \author Miguel Luis ( Semtech )
*
* \author Gregory Cristian ( Semtech )
*
* \author Daniel Jaeckle ( STACKFORCE )
*
* \author Johannes Bruder ( STACKFORCE )
*/
#include "utilities.h"
#include "region/Region.h"
#include "LoRaMacClassB.h"
#include "LoRaMacCrypto.h"
#include "secure-element.h"
#include "LoRaMacTest.h"
#include "LoRaMacTypes.h"
#include "LoRaMacConfirmQueue.h"
#include "LoRaMacHeaderTypes.h"
#include "LoRaMacMessageTypes.h"
#include "LoRaMacParser.h"
#include "LoRaMacCommands.h"
#include "LoRaMacAdr.h"
#include "LoRaMacSerializer.h"
#include "radio.h"
#include "LoRaMac.h"
#ifndef LORAMAC_VERSION
/*!
* LoRaWAN version definition.
*/
#define LORAMAC_VERSION 0x01000400
#endif
/*!
* Maximum PHY layer payload size
*/
#define LORAMAC_PHY_MAXPAYLOAD 255
/*!
* Maximum length of the fOpts field
*/
#define LORA_MAC_COMMAND_MAX_FOPTS_LENGTH 15
/*!
* LoRaMac duty cycle for the back-off procedure during the first hour.
*/
#define BACKOFF_DC_1_HOUR 100
/*!
* LoRaMac duty cycle for the back-off procedure during the next 10 hours.
*/
#define BACKOFF_DC_10_HOURS 1000
/*!
* LoRaMac duty cycle for the back-off procedure during the next 24 hours.
*/
#define BACKOFF_DC_24_HOURS 10000
/*!
* Maximum value for the ADR ack counter
*/
#define ADR_ACK_COUNTER_MAX 0xFFFFFFFF
/*!
* LoRaMac internal states
*/
enum eLoRaMacState
{
LORAMAC_IDLE = 0x00000000,
LORAMAC_STOPPED = 0x00000001,
LORAMAC_TX_RUNNING = 0x00000002,
LORAMAC_RX = 0x00000004,
LORAMAC_ACK_RETRY = 0x00000010,
LORAMAC_TX_DELAYED = 0x00000020,
LORAMAC_TX_CONFIG = 0x00000040,
LORAMAC_RX_ABORT = 0x00000080,
};
/*
* Request permission state
*/
typedef enum eLoRaMacRequestHandling
{
LORAMAC_REQUEST_HANDLING_OFF = 0,
LORAMAC_REQUEST_HANDLING_ON = !LORAMAC_REQUEST_HANDLING_OFF
}LoRaMacRequestHandling_t;
typedef struct sLoRaMacCtx
{
/*
* Length of packet in PktBuffer
*/
uint16_t PktBufferLen;
/*
* Buffer containing the data to be sent or received.
*/
uint8_t PktBuffer[LORAMAC_PHY_MAXPAYLOAD];
/*!
* Current processed transmit message
*/
LoRaMacMessage_t TxMsg;
/*!
* Buffer containing the data received by the application.
*/
uint8_t AppData[LORAMAC_PHY_MAXPAYLOAD];
/*
* Size of buffer containing the application data.
*/
uint8_t AppDataSize;
/*
* Buffer containing the upper layer data.
*/
uint8_t RxPayload[LORAMAC_PHY_MAXPAYLOAD];
SysTime_t LastTxSysTime;
/*
* LoRaMac internal state
*/
uint32_t MacState;
/*
* LoRaMac upper layer event functions
*/
LoRaMacPrimitives_t* MacPrimitives;
/*
* LoRaMac upper layer callback functions
*/
LoRaMacCallback_t* MacCallbacks;
/*
* Radio events function pointer
*/
RadioEvents_t RadioEvents;
/*
* LoRaMac duty cycle delayed Tx timer
*/
TimerEvent_t TxDelayedTimer;
/*
* LoRaMac reception windows timers
*/
TimerEvent_t RxWindowTimer1;
TimerEvent_t RxWindowTimer2;
/*
* LoRaMac reception windows delay
* \remark normal frame: RxWindowXDelay = ReceiveDelayX - RADIO_WAKEUP_TIME
* join frame : RxWindowXDelay = JoinAcceptDelayX - RADIO_WAKEUP_TIME
*/
uint32_t RxWindow1Delay;
uint32_t RxWindow2Delay;
/*
* LoRaMac Rx windows configuration
*/
RxConfigParams_t RxWindow1Config;
RxConfigParams_t RxWindow2Config;
RxConfigParams_t RxWindowCConfig;
/*
* Limit of uplinks without any donwlink response before the ADRACKReq bit will be set.
*/
uint16_t AdrAckLimit;
/*
* Limit of uplinks without any donwlink response after a the first frame with set ADRACKReq bit
* before the trying to regain the connectivity.
*/
uint16_t AdrAckDelay;
/*
* Acknowledge timeout timer. Used for packet retransmissions.
*/
TimerEvent_t RetransmitTimeoutTimer;
/*
* Uplink messages repetitions counter
*/
uint8_t ChannelsNbTransCounter;
/*
* Indicates if the AckTimeout timer has expired or not
*/
bool RetransmitTimeoutRetry;
/*
* If the node has sent a FRAME_TYPE_DATA_CONFIRMED_UP this variable indicates
* if the nodes needs to manage the server acknowledgement.
*/
bool NodeAckRequested;
/*
* Current channel index
*/
uint8_t Channel;
/*
* Last transmission time on air
*/
TimerTime_t TxTimeOnAir;
/*
* Structure to hold an MCPS indication data.
*/
McpsIndication_t McpsIndication;
/*
* Structure to hold MCPS confirm data.
*/
McpsConfirm_t McpsConfirm;
/*
* Structure to hold MLME confirm data.
*/
MlmeConfirm_t MlmeConfirm;
/*
* Structure to hold MLME indication data.
*/
MlmeIndication_t MlmeIndication;
/*
* Holds the current rx window slot
*/
LoRaMacRxSlot_t RxSlot;
/*
* LoRaMac tx/rx operation state
*/
LoRaMacFlags_t MacFlags;
/*
* Data structure indicating if a request is allowed or not.
*/
LoRaMacRequestHandling_t AllowRequests;
/*
* Duty cycle wait time
*/
TimerTime_t DutyCycleWaitTime;
/*
* Start time of the response timeout
*/
TimerTime_t ResponseTimeoutStartTime;
/*
* Buffer containing the MAC layer commands
*/
uint8_t MacCommandsBuffer[LORA_MAC_COMMAND_MAX_LENGTH];
}LoRaMacCtx_t;
/*
* Module context.
*/
static LoRaMacCtx_t MacCtx;
static LoRaMacNvmData_t Nvm;
/*!
* Defines the LoRaMac radio events status
*/
typedef union uLoRaMacRadioEvents
{
uint32_t Value;
struct sEvents
{
uint32_t RxTimeout : 1;
uint32_t RxError : 1;
uint32_t TxTimeout : 1;
uint32_t RxDone : 1;
uint32_t TxDone : 1;
}Events;
}LoRaMacRadioEvents_t;
/*!
* LoRaMac radio events status
*/
LoRaMacRadioEvents_t LoRaMacRadioEvents = { .Value = 0 };
/*!
* \brief Function to be executed on Radio Tx Done event
*/
static void OnRadioTxDone( void );
/*!
* \brief This function prepares the MAC to abort the execution of function
* OnRadioRxDone in case of a reception error.
*/
static void PrepareRxDoneAbort( void );
/*!
* \brief Function to be executed on Radio Rx Done event
*/
static void OnRadioRxDone( uint8_t* payload, uint16_t size, int16_t rssi, int8_t snr );
/*!
* \brief Function executed on Radio Tx Timeout event
*/
static void OnRadioTxTimeout( void );
/*!
* \brief Function executed on Radio Rx error event
*/
static void OnRadioRxError( void );
/*!
* \brief Function executed on Radio Rx Timeout event
*/
static void OnRadioRxTimeout( void );
/*!
* \brief Function executed on duty cycle delayed Tx timer event
*/
static void OnTxDelayedTimerEvent( void* context );
/*!
* \brief Function executed on first Rx window timer event
*/
static void OnRxWindow1TimerEvent( void* context );
/*!
* \brief Function executed on second Rx window timer event
*/
static void OnRxWindow2TimerEvent( void* context );
/*!
* \brief Function executed on AckTimeout timer event
*/
static void OnRetransmitTimeoutTimerEvent( void* context );
/*!
* \brief Configures the events to trigger an MLME-Indication with
* a MLME type of MLME_SCHEDULE_UPLINK.
*/
static void SetMlmeScheduleUplinkIndication( void );
/*!
* Computes next 32 bit downlink counter value and determines the frame counter ID.
*
* \param[IN] addrID - Address identifier
* \param[IN] fType - Frame type
* \param[IN] macMsg - Data message object, holding the current 16 bit transmitted frame counter
* \param[IN] lrWanVersion - LoRaWAN version
* \param[OUT] fCntID - Frame counter identifier
* \param[OUT] currentDown - Current downlink counter value
*
* \retval - Status of the operation
*/
static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
FCntIdentifier_t* fCntID, uint32_t* currentDown );
/*!
* \brief Switches the device class
*
* \param [IN] deviceClass Device class to switch to
*/
static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass );
/*!
* \brief Gets the maximum application payload length in the absence of the optional FOpt field.
*
* \param [IN] datarate Current datarate
*
* \retval Max length
*/
static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate );
/*!
* \brief Validates if the payload fits into the frame, taking the datarate
* into account.
*
* \details Refer to chapter 4.3.2 of the LoRaWAN specification, v1.0
*
* \param lenN Length of the application payload. The length depends on the
* datarate and is region specific
*
* \param datarate Current datarate
*
* \param fOptsLen Length of the fOpts field
*
* \retval [false: payload does not fit into the frame, true: payload fits into
* the frame]
*/
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen );
/*!
* \brief Decodes MAC commands in the fOpts field and in the payload
*
* \param [IN] payload A pointer to the payload
* \param [IN] macIndex The index of the payload where the MAC commands start
* \param [IN] commandsSize The size of the MAC commands
* \param [IN] snr The SNR value of the frame
* \param [IN] rxSlot The RX slot where the frame was received
*/
static void ProcessMacCommands( uint8_t* payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot );
/*!
* \brief LoRaMAC layer generic send frame
*
* \param [IN] macHdr MAC header field
* \param [IN] fPort MAC payload port
* \param [IN] fBuffer MAC data buffer to be sent
* \param [IN] fBufferSize MAC data buffer size
* \retval status Status of the operation.
*/
LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
/*!
* \brief LoRaMAC layer send join/rejoin request
*
* \param [IN] joinReqType Type of join-request or rejoin
*
* \retval status Status of the operation.
*/
LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType );
/*!
* \brief LoRaMAC layer frame buffer initialization
*
* \param [IN] macHdr MAC header field
* \param [IN] fCtrl MAC frame control field
* \param [IN] fOpts MAC commands buffer
* \param [IN] fPort MAC payload port
* \param [IN] fBuffer MAC data buffer to be sent
* \param [IN] fBufferSize MAC data buffer size
* \retval status Status of the operation.
*/
LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize );
/*
* \brief Schedules the frame according to the duty cycle
*
* \param [IN] allowDelayedTx When set to true, the a frame will be delayed,
* the duty cycle restriction is active
* \retval Status of the operation
*/
static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx );
/*
* \brief Secures the current processed frame ( TxMsg )
* \param[IN] txDr Data rate used for the transmission
* \param[IN] txCh Index of the channel used for the transmission
* \retval status Status of the operation
*/
static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh );
/*
* \brief Calculates the aggregated back off time.
*/
static void CalculateBackOff( void );
/*
* \brief Function to remove pending MAC commands
*
* \param [IN] rxSlot The RX slot on which the frame was received
* \param [IN] fCtrl The frame control field of the received frame
* \param [IN] request The request type
*/
static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request );
/*!
* \brief LoRaMAC layer prepared frame buffer transmission with channel specification
*
* \remark PrepareFrame must be called at least once before calling this
* function.
*
* \param [IN] channel Channel to transmit on
* \retval status Status of the operation.
*/
LoRaMacStatus_t SendFrameOnChannel( uint8_t channel );
/*!
* \brief Sets the radio in continuous transmission mode
*
* \remark Uses the radio parameters set on the previous transmission.
*
* \param [IN] timeout Time in seconds while the radio is kept in continuous wave mode
* \param [IN] frequency RF frequency to be set.
* \param [IN] power RF output power to be set.
* \retval status Status of the operation.
*/
LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power );
/*!
* \brief Resets MAC specific parameters to default
*/
static void ResetMacParameters( void );
/*!
* \brief Initializes and opens the reception window
*
* \param [IN] rxTimer Window timer to be topped.
* \param [IN] rxConfig Window parameters to be setup
*/
static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig );
/*!
* \brief Opens up a continuous RX C window. This is used for
* class c devices.
*/
static void OpenContinuousRxCWindow( void );
/*!
* \brief Returns a pointer to the internal contexts structure.
*
* \retval void Points to a structure containing all contexts
*/
static LoRaMacNvmData_t* GetNvmData( void );
/*!
* \brief Restoring of internal module contexts
*
* \details This function allows to restore module contexts by a given pointer.
*
*
* \retval LoRaMacStatus_t Status of the operation. Possible returns are:
* returns are:
* \ref LORAMAC_STATUS_OK,
* \ref LORAMAC_STATUS_PARAMETER_INVALID,
*/
static LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* contexts );
/*!
* \brief Determines the frame type
*
* \param [IN] macMsg Data message object
*
* \param [OUT] fType Frame type
*
* \retval LoRaMacStatus_t Status of the operation. Possible returns are:
* returns are:
* \ref LORAMAC_STATUS_OK,
* \ref LORAMAC_STATUS_PARAMETER_INVALID,
*/
LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType );
/*!
* \brief Verifies, if the retransmission counter has reached the limit
*
* \param [IN] counter Current retransmission counter
* \param [IN] limit Retransmission counter limit
*
* \retval Returns true if the number of retransmissions have reached the limit.
*/
static bool CheckRetrans( uint8_t counter, uint8_t limit );
/*!
* \brief Checks if the retransmission should be stopped in case of a unconfirmed uplink
*
* \retval Returns true if it should be stopped.
*/
static bool CheckRetransUnconfirmedUplink( void );
/*!
* \brief Checks if the retransmission should be stopped in case of a confirmed uplink
*
* \retval Returns true it should be stopped.
*/
static bool CheckRetransConfirmedUplink( void );
/*!
* \brief Increases the ADR ack counter. Takes the maximum
* value into account.
*
* \param [IN] counter Current counter value.
*
* \retval Returns the next counter value.
*/
static uint32_t IncreaseAdrAckCounter( uint32_t counter );
/*!
* \brief Stops the uplink retransmission
*
* \retval Returns true if successful.
*/
static bool StopRetransmission( void );
/*!
* \brief Calls the callback to indicate that a context changed
*/
static void CallNvmDataChangeCallback( uint16_t notifyFlags );
/*!
* \brief Verifies if a request is pending currently
*
* \retval 1: Request pending, 0: request not pending
*/
static uint8_t IsRequestPending( void );
/*!
* \brief Enabled the possibility to perform requests
*
* \param [IN] requestState Request permission state
*/
static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState );
/*!
* \brief This function verifies if a RX abort occurred
*/
static void LoRaMacCheckForRxAbort( void );
/*!
* \brief This function verifies if a beacon acquisition MLME
* request was pending
*
* \retval 1: Request pending, 0: no request pending
*/
static uint8_t LoRaMacCheckForBeaconAcquisition( void );
/*!
* \brief Returns true, if the device must apply the minium datarate
*
* \param [IN] adr ADR status bit
*
* \param [IN] activation Activation type of the device
*
* \param [IN] datarateChanged Set to true, if the datarate was changed
* with the LinkAdrReq.
*/
static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged );
/*!
* \brief This function handles join request
*/
static void LoRaMacHandleMlmeRequest( void );
/*!
* \brief This function handles mcps request
*/
static void LoRaMacHandleMcpsRequest( void );
/*!
* \brief This function handles callback events for requests
*/
static void LoRaMacHandleRequestEvents( void );
/*!
* \brief This function handles callback events for indications
*/
static void LoRaMacHandleIndicationEvents( void );
/*!
* \brief This function handles callback events for NVM updates
*
* \param [IN] nvmData Data structure containing NVM data.
*/
static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData );
/*!
* Structure used to store the radio Tx event data
*/
struct
{
TimerTime_t CurTime;
}TxDoneParams;
/*!
* Structure used to store the radio Rx event data
*/
struct
{
TimerTime_t LastRxDone;
uint8_t *Payload;
uint16_t Size;
int16_t Rssi;
int8_t Snr;
}RxDoneParams;
static void OnRadioTxDone( void )
{
TxDoneParams.CurTime = TimerGetCurrentTime( );
MacCtx.LastTxSysTime = SysTimeGet( );
LoRaMacRadioEvents.Events.TxDone = 1;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static void OnRadioRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
RxDoneParams.LastRxDone = TimerGetCurrentTime( );
RxDoneParams.Payload = payload;
RxDoneParams.Size = size;
RxDoneParams.Rssi = rssi;
RxDoneParams.Snr = snr;
LoRaMacRadioEvents.Events.RxDone = 1;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static void OnRadioTxTimeout( void )
{
LoRaMacRadioEvents.Events.TxTimeout = 1;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static void OnRadioRxError( void )
{
LoRaMacRadioEvents.Events.RxError = 1;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static void OnRadioRxTimeout( void )
{
LoRaMacRadioEvents.Events.RxTimeout = 1;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static void UpdateRxSlotIdleState( void )
{
if( Nvm.MacGroup2.DeviceClass != CLASS_C )
{
MacCtx.RxSlot = RX_SLOT_NONE;
}
else
{
MacCtx.RxSlot = RX_SLOT_WIN_CLASS_C;
}
}
static void ProcessRadioTxDone( void )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
SetBandTxDoneParams_t txDone;
if( Nvm.MacGroup2.DeviceClass != CLASS_C )
{
Radio.Sleep( );
}
// Setup timers
TimerSetValue( &MacCtx.RxWindowTimer1, MacCtx.RxWindow1Delay );
TimerStart( &MacCtx.RxWindowTimer1 );
TimerSetValue( &MacCtx.RxWindowTimer2, MacCtx.RxWindow2Delay );
TimerStart( &MacCtx.RxWindowTimer2 );
if( MacCtx.NodeAckRequested == true )
{
getPhy.Attribute = PHY_RETRANSMIT_TIMEOUT;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
TimerSetValue( &MacCtx.RetransmitTimeoutTimer, MacCtx.RxWindow2Delay + phyParam.Value );
TimerStart( &MacCtx.RetransmitTimeoutTimer );
}
else
{
// Transmission successful, setup status directly
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
}
// Update Aggregated last tx done time
Nvm.MacGroup1.LastTxDoneTime = TxDoneParams.CurTime;
// Update last tx done time for the current channel
txDone.Channel = MacCtx.Channel;
txDone.LastTxDoneTime = TxDoneParams.CurTime;
txDone.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime );
txDone.LastTxAirTime = MacCtx.TxTimeOnAir;
txDone.Joined = true;
if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
{
txDone.Joined = false;
}
RegionSetBandTxDone( Nvm.MacGroup2.Region, &txDone );
}
static void PrepareRxDoneAbort( void )
{
MacCtx.MacState |= LORAMAC_RX_ABORT;
if( MacCtx.NodeAckRequested == true )
{
OnRetransmitTimeoutTimerEvent( NULL );
}
MacCtx.MacFlags.Bits.McpsInd = 1;
MacCtx.MacFlags.Bits.MacDone = 1;
UpdateRxSlotIdleState( );
}
static void ProcessRadioRxDone( void )
{
LoRaMacHeader_t macHdr;
ApplyCFListParams_t applyCFList;
GetPhyParams_t getPhy;
PhyParam_t phyParam;
LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
LoRaMacMessageData_t macMsgData;
LoRaMacMessageJoinAccept_t macMsgJoinAccept;
uint8_t *payload = RxDoneParams.Payload;
uint16_t size = RxDoneParams.Size;
int16_t rssi = RxDoneParams.Rssi;
int8_t snr = RxDoneParams.Snr;
uint8_t pktHeaderLen = 0;
uint32_t downLinkCounter = 0;
uint32_t address = Nvm.MacGroup2.DevAddr;
uint8_t multicast = 0;
AddressIdentifier_t addrID = UNICAST_DEV_ADDR;
FCntIdentifier_t fCntID;
MacCtx.McpsConfirm.AckReceived = false;
MacCtx.McpsIndication.Rssi = rssi;
MacCtx.McpsIndication.Snr = snr;
MacCtx.McpsIndication.RxSlot = MacCtx.RxSlot;
MacCtx.McpsIndication.Port = 0;
MacCtx.McpsIndication.Multicast = 0;
MacCtx.McpsIndication.FramePending = 0;
MacCtx.McpsIndication.Buffer = NULL;
MacCtx.McpsIndication.BufferSize = 0;
MacCtx.McpsIndication.RxData = false;
MacCtx.McpsIndication.AckReceived = false;
MacCtx.McpsIndication.DownLinkCounter = 0;
MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
MacCtx.McpsIndication.DevAddress = 0;
MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
MacCtx.McpsIndication.ResponseTimeout = 0;
Radio.Sleep( );
TimerStop( &MacCtx.RxWindowTimer2 );
// This function must be called even if we are not in class b mode yet.
if( LoRaMacClassBRxBeacon( payload, size ) == true )
{
MacCtx.MlmeIndication.BeaconInfo.Rssi = rssi;
MacCtx.MlmeIndication.BeaconInfo.Snr = snr;
return;
}
// Check if we expect a ping or a multicast slot.
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
if( LoRaMacClassBIsPingExpected( ) == true )
{
LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBPingSlotTimerEvent( NULL );
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
}
else if( LoRaMacClassBIsMulticastExpected( ) == true )
{
LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBMulticastSlotTimerEvent( NULL );
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
}
}
macHdr.Value = payload[pktHeaderLen++];
switch( macHdr.Bits.MType )
{
case FRAME_TYPE_JOIN_ACCEPT:
// Check if the received frame size is valid
if( size < LORAMAC_JOIN_ACCEPT_FRAME_MIN_SIZE )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
macMsgJoinAccept.Buffer = payload;
macMsgJoinAccept.BufSize = size;
// Abort in case if the device isn't joined yet and no rejoin request is ongoing.
if( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
macCryptoStatus = LoRaMacCryptoHandleJoinAccept( JOIN_REQ, SecureElementGetJoinEui( ), &macMsgJoinAccept );
if( LORAMAC_CRYPTO_SUCCESS == macCryptoStatus )
{
// Network ID
Nvm.MacGroup2.NetID = ( uint32_t ) macMsgJoinAccept.NetID[0];
Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[1] << 8 );
Nvm.MacGroup2.NetID |= ( ( uint32_t ) macMsgJoinAccept.NetID[2] << 16 );
// Device Address
Nvm.MacGroup2.DevAddr = macMsgJoinAccept.DevAddr;
// DLSettings
Nvm.MacGroup2.MacParams.Rx1DrOffset = macMsgJoinAccept.DLSettings.Bits.RX1DRoffset;
Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
Nvm.MacGroup2.MacParams.RxCChannel.Datarate = macMsgJoinAccept.DLSettings.Bits.RX2DataRate;
// RxDelay
Nvm.MacGroup2.MacParams.ReceiveDelay1 = macMsgJoinAccept.RxDelay;
if( Nvm.MacGroup2.MacParams.ReceiveDelay1 == 0 )
{
Nvm.MacGroup2.MacParams.ReceiveDelay1 = 1;
}
Nvm.MacGroup2.MacParams.ReceiveDelay1 *= 1000;
Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000;
Nvm.MacGroup2.Version.Fields.Minor = 0;
// Apply CF list
applyCFList.Payload = macMsgJoinAccept.CFList;
// Size of the regular payload is 12. Plus 1 byte MHDR and 4 bytes MIC
applyCFList.Size = size - 17;
// Apply the last tx channel
applyCFList.JoinChannel = MacCtx.Channel;
RegionApplyCFList( Nvm.MacGroup2.Region, &applyCFList );
Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_OTAA;
// MLME handling
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_JOIN );
}
}
else
{
// MLME handling
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL, MLME_JOIN );
}
}
break;
case FRAME_TYPE_DATA_CONFIRMED_DOWN:
MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
// Intentional fall through
case FRAME_TYPE_DATA_UNCONFIRMED_DOWN:
// Check if the received payload size is valid
getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
getPhy.Datarate = MacCtx.McpsIndication.RxDatarate;
getPhy.Attribute = PHY_MAX_PAYLOAD;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
if( ( MAX( 0, ( int16_t )( ( int16_t ) size - ( int16_t ) LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE ) ) > ( int16_t )phyParam.Value ) ||
( size < LORAMAC_FRAME_PAYLOAD_MIN_SIZE ) )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
macMsgData.Buffer = payload;
macMsgData.BufSize = size;
macMsgData.FRMPayload = MacCtx.RxPayload;
macMsgData.FRMPayloadSize = LORAMAC_PHY_MAXPAYLOAD;
if( LORAMAC_PARSER_SUCCESS != LoRaMacParserData( &macMsgData ) )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
// Handle Class B
// Check if we expect a ping or a multicast slot.
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
if( LoRaMacClassBIsPingExpected( ) == true )
{
LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBPingSlotTimerEvent( NULL );
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_PING_SLOT;
LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending );
}
else if( LoRaMacClassBIsMulticastExpected( ) == true )
{
LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBMulticastSlotTimerEvent( NULL );
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT;
LoRaMacClassBSetFPendingBit( macMsgData.FHDR.DevAddr, ( uint8_t ) macMsgData.FHDR.FCtrl.Bits.FPending );
}
}
// Store device address
MacCtx.McpsIndication.DevAddress = macMsgData.FHDR.DevAddr;
FType_t fType;
if( LORAMAC_STATUS_OK != DetermineFrameType( &macMsgData, &fType ) )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
//Check if it is a multicast message
multicast = 0;
downLinkCounter = 0;
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
{
if( ( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address == macMsgData.FHDR.DevAddr ) &&
( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true ) )
{
multicast = 1;
addrID = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.GroupID;
downLinkCounter = *( Nvm.MacGroup2.MulticastChannelList[i].DownLinkCounter );
address = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address;
if( Nvm.MacGroup2.DeviceClass == CLASS_C )
{
MacCtx.McpsIndication.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
}
break;
}
}
// Filter messages according to multicast downlink exceptions
if( ( multicast == 1 ) && ( ( fType != FRAME_TYPE_D ) ||
( macMsgData.FHDR.FCtrl.Bits.Ack != 0 ) ||
( macMsgData.FHDR.FCtrl.Bits.AdrAckReq != 0 ) ) )
{
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
return;
}
// Get downlink frame counter value
macCryptoStatus = GetFCntDown( addrID, fType, &macMsgData, Nvm.MacGroup2.Version, &fCntID, &downLinkCounter );
if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
{
if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_FCNT_DUPLICATED )
{
// Catch the case of repeated downlink frame counter
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED;
}
else
{
// Other errors
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
}
MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
PrepareRxDoneAbort( );
return;
}
macCryptoStatus = LoRaMacCryptoUnsecureMessage( addrID, address, fCntID, downLinkCounter, &macMsgData );
if( macCryptoStatus != LORAMAC_CRYPTO_SUCCESS )
{
if( macCryptoStatus == LORAMAC_CRYPTO_FAIL_ADDRESS )
{
// We are not the destination of this frame.
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL;
}
else
{
// MIC calculation fail
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_MIC_FAIL;
}
PrepareRxDoneAbort( );
return;
}
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
MacCtx.McpsIndication.Multicast = multicast;
MacCtx.McpsIndication.FramePending = macMsgData.FHDR.FCtrl.Bits.FPending;
MacCtx.McpsIndication.Buffer = NULL;
MacCtx.McpsIndication.BufferSize = 0;
MacCtx.McpsIndication.DownLinkCounter = downLinkCounter;
MacCtx.McpsIndication.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_OK;
MacCtx.McpsConfirm.AckReceived = macMsgData.FHDR.FCtrl.Bits.Ack;
// Reset ADR ACK Counter only, when RX1 or RX2 slot
if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
{
Nvm.MacGroup1.AdrAckCounter = 0;
Nvm.MacGroup2.DownlinkReceived = true;
}
// MCPS Indication and ack requested handling
if( multicast == 1 )
{
MacCtx.McpsIndication.McpsIndication = MCPS_MULTICAST;
}
else
{
if( macHdr.Bits.MType == FRAME_TYPE_DATA_CONFIRMED_DOWN )
{
Nvm.MacGroup1.SrvAckRequested = true;
if( Nvm.MacGroup2.Version.Fields.Minor == 0 )
{
Nvm.MacGroup1.LastRxMic = macMsgData.MIC;
}
MacCtx.McpsIndication.McpsIndication = MCPS_CONFIRMED;
// Handle response timeout for class c and class b downlinks
if( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) )
{
// Calculate timeout
MacCtx.McpsIndication.ResponseTimeout = REGION_COMMON_CLASS_B_C_RESP_TIMEOUT;
MacCtx.ResponseTimeoutStartTime = RxDoneParams.LastRxDone;
}
}
else
{
Nvm.MacGroup1.SrvAckRequested = false;
MacCtx.McpsIndication.McpsIndication = MCPS_UNCONFIRMED;
}
}
RemoveMacCommands( MacCtx.McpsIndication.RxSlot, macMsgData.FHDR.FCtrl, MacCtx.McpsConfirm.McpsRequest );
switch( fType )
{
case FRAME_TYPE_A:
{ /* +----------+------+-------+--------------+
* | FOptsLen | Fopt | FPort | FRMPayload |
* +----------+------+-------+--------------+
* | > 0 | X | > 0 | X |
* +----------+------+-------+--------------+
*/
// Decode MAC commands in FOpts field
ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
MacCtx.McpsIndication.Port = macMsgData.FPort;
MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
MacCtx.McpsIndication.RxData = true;
break;
}
case FRAME_TYPE_B:
{ /* +----------+------+-------+--------------+
* | FOptsLen | Fopt | FPort | FRMPayload |
* +----------+------+-------+--------------+
* | > 0 | X | - | - |
* +----------+------+-------+--------------+
*/
// Decode MAC commands in FOpts field
ProcessMacCommands( macMsgData.FHDR.FOpts, 0, macMsgData.FHDR.FCtrl.Bits.FOptsLen, snr, MacCtx.McpsIndication.RxSlot );
MacCtx.McpsIndication.Port = macMsgData.FPort;
break;
}
case FRAME_TYPE_C:
{ /* +----------+------+-------+--------------+
* | FOptsLen | Fopt | FPort | FRMPayload |
* +----------+------+-------+--------------+
* | = 0 | - | = 0 | MAC commands |
* +----------+------+-------+--------------+
*/
// Decode MAC commands in FRMPayload
ProcessMacCommands( macMsgData.FRMPayload, 0, macMsgData.FRMPayloadSize, snr, MacCtx.McpsIndication.RxSlot );
MacCtx.McpsIndication.Port = macMsgData.FPort;
break;
}
case FRAME_TYPE_D:
{ /* +----------+------+-------+--------------+
* | FOptsLen | Fopt | FPort | FRMPayload |
* +----------+------+-------+--------------+
* | = 0 | - | > 0 | X |
* +----------+------+-------+--------------+
*/
// No MAC commands just application payload
MacCtx.McpsIndication.Port = macMsgData.FPort;
MacCtx.McpsIndication.Buffer = macMsgData.FRMPayload;
MacCtx.McpsIndication.BufferSize = macMsgData.FRMPayloadSize;
MacCtx.McpsIndication.RxData = true;
break;
}
default:
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
break;
}
if( ( macMsgData.FPort == LORAMAC_CERT_FPORT ) && ( Nvm.MacGroup2.IsCertPortOn == false ) )
{ // Do not notify the upper layer of data reception on FPort LORAMAC_CERT_FPORT if the port
// handling is disabled.
MacCtx.McpsIndication.Port = macMsgData.FPort;
MacCtx.McpsIndication.Buffer = NULL;
MacCtx.McpsIndication.BufferSize = 0;
MacCtx.McpsIndication.RxData = false;
}
// Provide always an indication, skip the callback to the user application,
// in case of a confirmed downlink retransmission.
MacCtx.MacFlags.Bits.McpsInd = 1;
break;
case FRAME_TYPE_PROPRIETARY:
memcpy1( MacCtx.RxPayload, &payload[pktHeaderLen], size - pktHeaderLen );
MacCtx.McpsIndication.McpsIndication = MCPS_PROPRIETARY;
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
MacCtx.McpsIndication.Buffer = MacCtx.RxPayload;
MacCtx.McpsIndication.BufferSize = size - pktHeaderLen;
MacCtx.MacFlags.Bits.McpsInd = 1;
break;
default:
MacCtx.McpsIndication.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
PrepareRxDoneAbort( );
break;
}
// Verify if we need to disable the RetransmitTimeoutTimer
if( MacCtx.NodeAckRequested == true )
{
if( MacCtx.McpsConfirm.AckReceived == true )
{
OnRetransmitTimeoutTimerEvent( NULL );
}
}
MacCtx.MacFlags.Bits.MacDone = 1;
UpdateRxSlotIdleState( );
}
static void ProcessRadioTxTimeout( void )
{
if( Nvm.MacGroup2.DeviceClass != CLASS_C )
{
Radio.Sleep( );
}
UpdateRxSlotIdleState( );
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT;
LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT );
if( MacCtx.NodeAckRequested == true )
{
MacCtx.RetransmitTimeoutRetry = true;
}
MacCtx.MacFlags.Bits.MacDone = 1;
}
static void HandleRadioRxErrorTimeout( LoRaMacEventInfoStatus_t rx1EventInfoStatus, LoRaMacEventInfoStatus_t rx2EventInfoStatus )
{
bool classBRx = false;
if( Nvm.MacGroup2.DeviceClass != CLASS_C )
{
Radio.Sleep( );
}
if( LoRaMacClassBIsBeaconExpected( ) == true )
{
LoRaMacClassBSetBeaconState( BEACON_STATE_TIMEOUT );
LoRaMacClassBBeaconTimerEvent( NULL );
classBRx = true;
}
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
if( LoRaMacClassBIsPingExpected( ) == true )
{
LoRaMacClassBSetPingSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBPingSlotTimerEvent( NULL );
classBRx = true;
}
if( LoRaMacClassBIsMulticastExpected( ) == true )
{
LoRaMacClassBSetMulticastSlotState( PINGSLOT_STATE_CALC_PING_OFFSET );
LoRaMacClassBMulticastSlotTimerEvent( NULL );
classBRx = true;
}
}
if( classBRx == false )
{
if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
{
if( MacCtx.NodeAckRequested == true )
{
MacCtx.McpsConfirm.Status = rx1EventInfoStatus;
}
LoRaMacConfirmQueueSetStatusCmn( rx1EventInfoStatus );
if( TimerGetElapsedTime( Nvm.MacGroup1.LastTxDoneTime ) >= MacCtx.RxWindow2Delay )
{
TimerStop( &MacCtx.RxWindowTimer2 );
MacCtx.MacFlags.Bits.MacDone = 1;
}
}
else
{
if( MacCtx.NodeAckRequested == true )
{
MacCtx.McpsConfirm.Status = rx2EventInfoStatus;
}
LoRaMacConfirmQueueSetStatusCmn( rx2EventInfoStatus );
MacCtx.MacFlags.Bits.MacDone = 1;
}
}
UpdateRxSlotIdleState( );
}
static void ProcessRadioRxError( void )
{
HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_ERROR, LORAMAC_EVENT_INFO_STATUS_RX2_ERROR );
}
static void ProcessRadioRxTimeout( void )
{
HandleRadioRxErrorTimeout( LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT, LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT );
}
static void LoRaMacHandleIrqEvents( void )
{
LoRaMacRadioEvents_t events;
CRITICAL_SECTION_BEGIN( );
events = LoRaMacRadioEvents;
LoRaMacRadioEvents.Value = 0;
CRITICAL_SECTION_END( );
if( events.Value != 0 )
{
if( events.Events.TxDone == 1 )
{
ProcessRadioTxDone( );
}
if( events.Events.RxDone == 1 )
{
ProcessRadioRxDone( );
}
if( events.Events.TxTimeout == 1 )
{
ProcessRadioTxTimeout( );
}
if( events.Events.RxError == 1 )
{
ProcessRadioRxError( );
}
if( events.Events.RxTimeout == 1 )
{
ProcessRadioRxTimeout( );
}
}
}
bool LoRaMacIsBusy( void )
{
if( ( MacCtx.MacState == LORAMAC_IDLE ) &&
( MacCtx.AllowRequests == LORAMAC_REQUEST_HANDLING_ON ) )
{
return false;
}
return true;
}
static void LoRaMacEnableRequests( LoRaMacRequestHandling_t requestState )
{
MacCtx.AllowRequests = requestState;
}
static void LoRaMacHandleRequestEvents( void )
{
// Handle events
LoRaMacFlags_t reqEvents = MacCtx.MacFlags;
if( MacCtx.MacState == LORAMAC_IDLE )
{
// Update event bits
if( MacCtx.MacFlags.Bits.McpsReq == 1 )
{
MacCtx.MacFlags.Bits.McpsReq = 0;
}
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
{
MacCtx.MacFlags.Bits.MlmeReq = 0;
}
// Allow requests again
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
// Handle callbacks
if( reqEvents.Bits.McpsReq == 1 )
{
MacCtx.MacPrimitives->MacMcpsConfirm( &MacCtx.McpsConfirm );
}
if( reqEvents.Bits.MlmeReq == 1 )
{
LoRaMacConfirmQueueHandleCb( &MacCtx.MlmeConfirm );
if( LoRaMacConfirmQueueGetCnt( ) > 0 )
{
MacCtx.MacFlags.Bits.MlmeReq = 1;
}
}
// Start beaconing again
LoRaMacClassBResumeBeaconing( );
// Procedure done. Reset variables.
MacCtx.MacFlags.Bits.MacDone = 0;
}
}
static void LoRaMacHandleScheduleUplinkEvent( void )
{
// Handle events
if( MacCtx.MacState == LORAMAC_IDLE )
{
// Verify if sticky MAC commands are pending or not
bool isStickyMacCommandPending = false;
LoRaMacCommandsStickyCmdsPending( &isStickyMacCommandPending );
if( isStickyMacCommandPending == true )
{// Setup MLME indication
SetMlmeScheduleUplinkIndication( );
}
}
}
static void LoRaMacHandleIndicationEvents( void )
{
// Handle MLME indication
if( MacCtx.MacFlags.Bits.MlmeInd == 1 )
{
MacCtx.MacFlags.Bits.MlmeInd = 0;
MacCtx.MacPrimitives->MacMlmeIndication( &MacCtx.MlmeIndication );
}
if( MacCtx.MacFlags.Bits.MlmeSchedUplinkInd == 1 )
{
MlmeIndication_t schduleUplinkIndication;
schduleUplinkIndication.MlmeIndication = MLME_SCHEDULE_UPLINK;
schduleUplinkIndication.Status = LORAMAC_EVENT_INFO_STATUS_OK;
MacCtx.MacPrimitives->MacMlmeIndication( &schduleUplinkIndication );
MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 0;
}
// Handle MCPS indication
if( MacCtx.MacFlags.Bits.McpsInd == 1 )
{
MacCtx.MacFlags.Bits.McpsInd = 0;
MacCtx.MacPrimitives->MacMcpsIndication( &MacCtx.McpsIndication );
}
}
static void LoRaMacHandleMcpsRequest( void )
{
// Handle MCPS uplinks
if( MacCtx.MacFlags.Bits.McpsReq == 1 )
{
bool stopRetransmission = false;
bool waitForRetransmission = false;
if( ( MacCtx.McpsConfirm.McpsRequest == MCPS_UNCONFIRMED ) ||
( MacCtx.McpsConfirm.McpsRequest == MCPS_PROPRIETARY ) )
{
stopRetransmission = CheckRetransUnconfirmedUplink( );
}
else if( MacCtx.McpsConfirm.McpsRequest == MCPS_CONFIRMED )
{
if( MacCtx.RetransmitTimeoutRetry == true )
{
stopRetransmission = CheckRetransConfirmedUplink( );
}
else
{
waitForRetransmission = true;
}
}
if( stopRetransmission == true )
{// Stop retransmission
TimerStop( &MacCtx.TxDelayedTimer );
MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
StopRetransmission( );
}
else if( waitForRetransmission == false )
{// Arrange further retransmission
MacCtx.MacFlags.Bits.MacDone = 0;
// Reset the state of the AckTimeout
MacCtx.RetransmitTimeoutRetry = false;
// Sends the same frame again
OnTxDelayedTimerEvent( NULL );
}
}
}
static void LoRaMacHandleMlmeRequest( void )
{
// Handle join request
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_JOIN ) == true )
{
if( LoRaMacConfirmQueueGetStatus( MLME_JOIN ) == LORAMAC_EVENT_INFO_STATUS_OK )
{// Node joined successfully
MacCtx.ChannelsNbTransCounter = 0;
}
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
}
else if( LoRaMacConfirmQueueIsCmdActive( MLME_TXCW ) == true )
{
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
}
}
}
static uint8_t LoRaMacCheckForBeaconAcquisition( void )
{
if( ( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_ACQUISITION ) == true ) &&
( MacCtx.MacFlags.Bits.McpsReq == 0 ) )
{
if( MacCtx.MacFlags.Bits.MlmeReq == 1 )
{
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
return 0x01;
}
}
return 0x00;
}
static bool CheckForMinimumAbpDatarate( bool adr, ActivationType_t activation, bool datarateChanged )
{
if( ( adr == true ) &&
( activation == ACTIVATION_TYPE_ABP ) &&
( datarateChanged == false ) )
{
return true;
}
return false;
}
static void LoRaMacCheckForRxAbort( void )
{
// A error occurs during receiving
if( ( MacCtx.MacState & LORAMAC_RX_ABORT ) == LORAMAC_RX_ABORT )
{
MacCtx.MacState &= ~LORAMAC_RX_ABORT;
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
}
}
static void LoRaMacHandleNvm( LoRaMacNvmData_t* nvmData )
{
uint32_t crc = 0;
uint16_t notifyFlags = LORAMAC_NVM_NOTIFY_FLAG_NONE;
if( MacCtx.MacState != LORAMAC_IDLE )
{
return;
}
// Crypto
crc = Crc32( ( uint8_t* ) &nvmData->Crypto, sizeof( nvmData->Crypto ) -
sizeof( nvmData->Crypto.Crc32 ) );
if( crc != nvmData->Crypto.Crc32 )
{
nvmData->Crypto.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CRYPTO;
}
// MacGroup1
crc = Crc32( ( uint8_t* ) &nvmData->MacGroup1, sizeof( nvmData->MacGroup1 ) -
sizeof( nvmData->MacGroup1.Crc32 ) );
if( crc != nvmData->MacGroup1.Crc32 )
{
nvmData->MacGroup1.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP1;
}
// MacGroup2
crc = Crc32( ( uint8_t* ) &nvmData->MacGroup2, sizeof( nvmData->MacGroup2 ) -
sizeof( nvmData->MacGroup2.Crc32 ) );
if( crc != nvmData->MacGroup2.Crc32 )
{
nvmData->MacGroup2.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_MAC_GROUP2;
}
// Secure Element
crc = Crc32( ( uint8_t* ) &nvmData->SecureElement, sizeof( nvmData->SecureElement ) -
sizeof( nvmData->SecureElement.Crc32 ) );
if( crc != nvmData->SecureElement.Crc32 )
{
nvmData->SecureElement.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_SECURE_ELEMENT;
}
// Region
crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup1, sizeof( nvmData->RegionGroup1 ) -
sizeof( nvmData->RegionGroup1.Crc32 ) );
if( crc != nvmData->RegionGroup1.Crc32 )
{
nvmData->RegionGroup1.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP1;
}
crc = Crc32( ( uint8_t* ) &nvmData->RegionGroup2, sizeof( nvmData->RegionGroup2 ) -
sizeof( nvmData->RegionGroup2.Crc32 ) );
if( crc != nvmData->RegionGroup2.Crc32 )
{
nvmData->RegionGroup2.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_REGION_GROUP2;
}
// ClassB
crc = Crc32( ( uint8_t* ) &nvmData->ClassB, sizeof( nvmData->ClassB ) -
sizeof( nvmData->ClassB.Crc32 ) );
if( crc != nvmData->ClassB.Crc32 )
{
nvmData->ClassB.Crc32 = crc;
notifyFlags |= LORAMAC_NVM_NOTIFY_FLAG_CLASS_B;
}
CallNvmDataChangeCallback( notifyFlags );
}
void LoRaMacProcess( void )
{
uint8_t noTx = false;
LoRaMacHandleIrqEvents( );
LoRaMacClassBProcess( );
// MAC proceeded a state and is ready to check
if( MacCtx.MacFlags.Bits.MacDone == 1 )
{
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_OFF );
LoRaMacCheckForRxAbort( );
// An error occurs during transmitting
if( IsRequestPending( ) > 0 )
{
noTx |= LoRaMacCheckForBeaconAcquisition( );
}
if( noTx == 0x00 )
{
LoRaMacHandleMlmeRequest( );
LoRaMacHandleMcpsRequest( );
}
LoRaMacHandleRequestEvents( );
LoRaMacHandleScheduleUplinkEvent( );
LoRaMacHandleNvm( &Nvm );
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
}
LoRaMacHandleIndicationEvents( );
if( MacCtx.RxSlot == RX_SLOT_WIN_CLASS_C )
{
OpenContinuousRxCWindow( );
}
}
static void OnTxDelayedTimerEvent( void* context )
{
TimerStop( &MacCtx.TxDelayedTimer );
MacCtx.MacState &= ~LORAMAC_TX_DELAYED;
if( MacCtx.ResponseTimeoutStartTime != 0 )
{
TimerTime_t elapsedTime = TimerGetElapsedTime( MacCtx.ResponseTimeoutStartTime );
if( elapsedTime > REGION_COMMON_CLASS_B_C_RESP_TIMEOUT )
{
// Skip retransmission
return;
}
}
// Schedule frame, allow delayed frame transmissions
switch( ScheduleTx( true ) )
{
case LORAMAC_STATUS_OK:
case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED:
{
break;
}
default:
{
// Stop retransmission attempt
MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate;
MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter;
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR;
LoRaMacConfirmQueueSetStatusCmn( LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR );
StopRetransmission( );
break;
}
}
}
static void OnRxWindow1TimerEvent( void* context )
{
MacCtx.RxWindow1Config.Channel = MacCtx.Channel;
MacCtx.RxWindow1Config.DrOffset = Nvm.MacGroup2.MacParams.Rx1DrOffset;
MacCtx.RxWindow1Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
MacCtx.RxWindow1Config.RxContinuous = false;
MacCtx.RxWindow1Config.RxSlot = RX_SLOT_WIN_1;
MacCtx.RxWindow1Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
RxWindowSetup( &MacCtx.RxWindowTimer1, &MacCtx.RxWindow1Config );
}
static void OnRxWindow2TimerEvent( void* context )
{
// Check if we are processing Rx1 window.
// If yes, we don't setup the Rx2 window.
if( MacCtx.RxSlot == RX_SLOT_WIN_1 )
{
return;
}
MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency;
MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
MacCtx.RxWindow2Config.RxContinuous = false;
MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
RxWindowSetup( &MacCtx.RxWindowTimer2, &MacCtx.RxWindow2Config );
}
static void OnRetransmitTimeoutTimerEvent( void* context )
{
TimerStop( &MacCtx.RetransmitTimeoutTimer );
if( MacCtx.NodeAckRequested == true )
{
MacCtx.RetransmitTimeoutRetry = true;
}
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->MacProcessNotify != NULL ) )
{
MacCtx.MacCallbacks->MacProcessNotify( );
}
}
static LoRaMacCryptoStatus_t GetFCntDown( AddressIdentifier_t addrID, FType_t fType, LoRaMacMessageData_t* macMsg, Version_t lrWanVersion,
FCntIdentifier_t* fCntID, uint32_t* currentDown )
{
if( ( macMsg == NULL ) || ( fCntID == NULL ) ||
( currentDown == NULL ) )
{
return LORAMAC_CRYPTO_ERROR_NPE;
}
// Determine the frame counter identifier and choose counter from FCntList
switch( addrID )
{
case UNICAST_DEV_ADDR:
if( lrWanVersion.Fields.Minor == 1 )
{
if( ( fType == FRAME_TYPE_A ) || ( fType == FRAME_TYPE_D ) )
{
*fCntID = A_FCNT_DOWN;
}
else
{
*fCntID = N_FCNT_DOWN;
}
}
else
{ // For LoRaWAN 1.0.X
*fCntID = FCNT_DOWN;
}
break;
case MULTICAST_0_ADDR:
*fCntID = MC_FCNT_DOWN_0;
break;
case MULTICAST_1_ADDR:
*fCntID = MC_FCNT_DOWN_1;
break;
case MULTICAST_2_ADDR:
*fCntID = MC_FCNT_DOWN_2;
break;
case MULTICAST_3_ADDR:
*fCntID = MC_FCNT_DOWN_3;
break;
default:
return LORAMAC_CRYPTO_FAIL_FCNT_ID;
}
return LoRaMacCryptoGetFCntDown( *fCntID, macMsg->FHDR.FCnt, currentDown );
}
static LoRaMacStatus_t SwitchClass( DeviceClass_t deviceClass )
{
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
switch( Nvm.MacGroup2.DeviceClass )
{
case CLASS_A:
{
if( deviceClass == CLASS_A )
{
// Revert back RxC parameters
Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParams.Rx2Channel;
}
if( deviceClass == CLASS_B )
{
status = LoRaMacClassBSwitchClass( deviceClass );
if( status == LORAMAC_STATUS_OK )
{
Nvm.MacGroup2.DeviceClass = deviceClass;
}
}
if( deviceClass == CLASS_C )
{
Nvm.MacGroup2.DeviceClass = deviceClass;
MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
for( int8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
{
if( Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.IsEnabled == true )
// TODO: Check multicast channel device class.
{
Nvm.MacGroup2.MacParams.RxCChannel.Frequency = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.ClassC.Frequency;
Nvm.MacGroup2.MacParams.RxCChannel.Datarate = Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.RxParams.ClassC.Datarate;
MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency;
MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C_MULTICAST;
MacCtx.RxWindowCConfig.RxContinuous = true;
break;
}
}
// Set the NodeAckRequested indicator to default
MacCtx.NodeAckRequested = false;
// Set the radio into sleep mode in case we are still in RX mode
Radio.Sleep( );
OpenContinuousRxCWindow( );
status = LORAMAC_STATUS_OK;
}
break;
}
case CLASS_B:
{
status = LoRaMacClassBSwitchClass( deviceClass );
if( status == LORAMAC_STATUS_OK )
{
Nvm.MacGroup2.DeviceClass = deviceClass;
}
break;
}
case CLASS_C:
{
if( deviceClass == CLASS_A )
{
Nvm.MacGroup2.DeviceClass = deviceClass;
// Set the radio into sleep to setup a defined state
Radio.Sleep( );
status = LORAMAC_STATUS_OK;
}
break;
}
}
return status;
}
static uint8_t GetMaxAppPayloadWithoutFOptsLength( int8_t datarate )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
// Setup PHY request
getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
getPhy.Datarate = datarate;
getPhy.Attribute = PHY_MAX_PAYLOAD;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
return phyParam.Value;
}
static bool ValidatePayloadLength( uint8_t lenN, int8_t datarate, uint8_t fOptsLen )
{
uint16_t maxN = 0;
uint16_t payloadSize = 0;
maxN = GetMaxAppPayloadWithoutFOptsLength( datarate );
// Calculate the resulting payload size
payloadSize = ( lenN + fOptsLen );
// Validation of the application payload size
if( ( payloadSize <= maxN ) && ( payloadSize <= LORAMAC_PHY_MAXPAYLOAD ) )
{
return true;
}
return false;
}
static void SetMlmeScheduleUplinkIndication( void )
{
MacCtx.MacFlags.Bits.MlmeSchedUplinkInd = 1;
}
static void ProcessMacCommands( uint8_t *payload, uint8_t macIndex, uint8_t commandsSize, int8_t snr, LoRaMacRxSlot_t rxSlot )
{
uint8_t status = 0;
bool adrBlockFound = false;
uint8_t macCmdPayload[2] = { 0x00, 0x00 };
if( ( rxSlot != RX_SLOT_WIN_1 ) && ( rxSlot != RX_SLOT_WIN_2 ) )
{
// Do only parse MAC commands for Class A RX windows
return;
}
while( macIndex < commandsSize )
{
// Make sure to parse only complete MAC commands
if( ( LoRaMacCommandsGetCmdSize( payload[macIndex] ) + macIndex ) > commandsSize )
{
return;
}
// Decode Frame MAC commands
switch( payload[macIndex++] )
{
case SRV_MAC_LINK_CHECK_ANS:
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_LINK_CHECK ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_LINK_CHECK );
MacCtx.MlmeConfirm.DemodMargin = payload[macIndex++];
MacCtx.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;
// The end node is allowed to process one block of LinkAdrRequests.
// It must ignore subsequent blocks
if( adrBlockFound == false )
{
adrBlockFound = true;
do
{
// Fill parameter structure
linkAdrReq.Payload = &payload[macIndex - 1];
linkAdrReq.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn;
linkAdrReq.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
linkAdrReq.CurrentDatarate = Nvm.MacGroup1.ChannelsDatarate;
linkAdrReq.CurrentTxPower = Nvm.MacGroup1.ChannelsTxPower;
linkAdrReq.CurrentNbRep = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
linkAdrReq.Version = Nvm.MacGroup2.Version;
// There is a fundamental difference in reporting the status
// of the LinkAdrRequests when ADR is on or off. When ADR is on, every
// LinkAdrAns contains the same value. This does not hold when ADR is off,
// where every LinkAdrAns requires an individual status.
if( Nvm.MacGroup2.AdrCtrlOn == true )
{
// When ADR is on, the function RegionLinkAdrReq will take care
// about the parsing and interpretation of the LinkAdrRequest block and
// it provides one status which shall be applied to every LinkAdrAns
linkAdrReq.PayloadSize = commandsSize - ( macIndex - 1 );
}
else
{
// When ADR is off, this function will loop over the individual LinkAdrRequests
// and will call RegionLinkAdrReq for each individually, as every request
// requires an individual answer.
// When ADR is off, the function RegionLinkAdrReq ignores the new values for
// ChannelsDatarate, ChannelsTxPower and ChannelsNbTrans.
linkAdrReq.PayloadSize = 5;
}
// Process the ADR requests
status = RegionLinkAdrReq( Nvm.MacGroup2.Region, &linkAdrReq, &linkAdrDatarate,
&linkAdrTxPower, &linkAdrNbRep, &linkAdrNbBytesParsed );
if( ( status & 0x07 ) == 0x07 )
{
// Set the status that the datarate has been increased
if( linkAdrDatarate > Nvm.MacGroup1.ChannelsDatarate )
{
Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = true;
}
Nvm.MacGroup1.ChannelsDatarate = linkAdrDatarate;
Nvm.MacGroup1.ChannelsTxPower = linkAdrTxPower;
Nvm.MacGroup2.MacParams.ChannelsNbTrans = linkAdrNbRep;
}
// Add the answers to the buffer
for( uint8_t i = 0; i < ( linkAdrNbBytesParsed / 5 ); i++ )
{
LoRaMacCommandsAddCmd( MOTE_MAC_LINK_ADR_ANS, &status, 1 );
}
// Update MAC index
macIndex += linkAdrNbBytesParsed - 1;
// Check to prevent invalid access
if( macIndex >= commandsSize )
break;
} while( payload[macIndex++] == SRV_MAC_LINK_ADR_REQ );
if( macIndex < commandsSize )
{
// Decrease the index such that it points to the next MAC command
macIndex--;
}
}
else
{
// Increase the index by the MAC command size (without command)
macIndex += 4;
}
break;
}
case SRV_MAC_DUTY_CYCLE_REQ:
{
Nvm.MacGroup2.MaxDCycle = payload[macIndex++] & 0x0F;
Nvm.MacGroup2.AggregatedDCycle = 1 << Nvm.MacGroup2.MaxDCycle;
LoRaMacCommandsAddCmd( MOTE_MAC_DUTY_CYCLE_ANS, macCmdPayload, 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 = RegionRxParamSetupReq( Nvm.MacGroup2.Region, &rxParamSetupReq );
if( ( status & 0x07 ) == 0x07 )
{
Nvm.MacGroup2.MacParams.Rx2Channel.Datarate = rxParamSetupReq.Datarate;
Nvm.MacGroup2.MacParams.RxCChannel.Datarate = rxParamSetupReq.Datarate;
Nvm.MacGroup2.MacParams.Rx2Channel.Frequency = rxParamSetupReq.Frequency;
Nvm.MacGroup2.MacParams.RxCChannel.Frequency = rxParamSetupReq.Frequency;
Nvm.MacGroup2.MacParams.Rx1DrOffset = rxParamSetupReq.DrOffset;
}
macCmdPayload[0] = status;
LoRaMacCommandsAddCmd( MOTE_MAC_RX_PARAM_SETUP_ANS, macCmdPayload, 1 );
// Setup indication to inform the application
SetMlmeScheduleUplinkIndication( );
break;
}
case SRV_MAC_DEV_STATUS_REQ:
{
uint8_t batteryLevel = BAT_LEVEL_NO_MEASURE;
if( ( MacCtx.MacCallbacks != NULL ) && ( MacCtx.MacCallbacks->GetBatteryLevel != NULL ) )
{
batteryLevel = MacCtx.MacCallbacks->GetBatteryLevel( );
}
macCmdPayload[0] = batteryLevel;
macCmdPayload[1] = ( uint8_t )( snr & 0x3F );
LoRaMacCommandsAddCmd( MOTE_MAC_DEV_STATUS_ANS, macCmdPayload, 2 );
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 = ( uint8_t )RegionNewChannelReq( Nvm.MacGroup2.Region, &newChannelReq );
if( ( int8_t )status >= 0 )
{
macCmdPayload[0] = status;
LoRaMacCommandsAddCmd( MOTE_MAC_NEW_CHANNEL_ANS, macCmdPayload, 1 );
}
break;
}
case SRV_MAC_RX_TIMING_SETUP_REQ:
{
uint8_t delay = payload[macIndex++] & 0x0F;
if( delay == 0 )
{
delay++;
}
Nvm.MacGroup2.MacParams.ReceiveDelay1 = delay * 1000;
Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay1 + 1000;
LoRaMacCommandsAddCmd( MOTE_MAC_RX_TIMING_SETUP_ANS, macCmdPayload, 0 );
// Setup indication to inform the application
SetMlmeScheduleUplinkIndication( );
break;
}
case SRV_MAC_TX_PARAM_SETUP_REQ:
{
TxParamSetupReqParams_t txParamSetupReq;
GetPhyParams_t getPhy;
PhyParam_t phyParam;
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( RegionTxParamSetupReq( Nvm.MacGroup2.Region, &txParamSetupReq ) != -1 )
{
// Accept command
Nvm.MacGroup2.MacParams.UplinkDwellTime = txParamSetupReq.UplinkDwellTime;
Nvm.MacGroup2.MacParams.DownlinkDwellTime = txParamSetupReq.DownlinkDwellTime;
Nvm.MacGroup2.MacParams.MaxEirp = LoRaMacMaxEirpTable[txParamSetupReq.MaxEirp];
// Update the datarate in case of the new configuration limits it
getPhy.Attribute = PHY_MIN_TX_DR;
getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup1.ChannelsDatarate = MAX( Nvm.MacGroup1.ChannelsDatarate, ( int8_t )phyParam.Value );
// Add command response
LoRaMacCommandsAddCmd( MOTE_MAC_TX_PARAM_SETUP_ANS, macCmdPayload, 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 = ( uint8_t )RegionDlChannelReq( Nvm.MacGroup2.Region, &dlChannelReq );
if( ( int8_t )status >= 0 )
{
macCmdPayload[0] = status;
LoRaMacCommandsAddCmd( MOTE_MAC_DL_CHANNEL_ANS, macCmdPayload, 1 );
// Setup indication to inform the application
SetMlmeScheduleUplinkIndication( );
}
break;
}
case SRV_MAC_DEVICE_TIME_ANS:
{
// The mote time can be updated only when the time is received in classA
// receive windows only.
if( LoRaMacConfirmQueueIsCmdActive( MLME_DEVICE_TIME ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_DEVICE_TIME );
SysTime_t gpsEpochTime = { 0 };
SysTime_t sysTime = { 0 };
SysTime_t sysTimeCurrent = { 0 };
gpsEpochTime.Seconds = ( uint32_t )payload[macIndex++];
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 8;
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 16;
gpsEpochTime.Seconds |= ( uint32_t )payload[macIndex++] << 24;
gpsEpochTime.SubSeconds = payload[macIndex++];
// Convert the fractional second received in ms
// round( pow( 0.5, 8.0 ) * 1000 ) = 3.90625
gpsEpochTime.SubSeconds = ( int16_t )( ( ( int32_t )gpsEpochTime.SubSeconds * 1000 ) >> 8 );
// Copy received GPS Epoch time into system time
sysTime = gpsEpochTime;
// Add Unix to Gps epoch offset. The system time is based on Unix time.
sysTime.Seconds += UNIX_GPS_EPOCH_OFFSET;
// Compensate time difference between Tx Done time and now
sysTimeCurrent = SysTimeGet( );
sysTime = SysTimeAdd( sysTimeCurrent, SysTimeSub( sysTime, MacCtx.LastTxSysTime ) );
// Apply the new system time.
SysTimeSet( sysTime );
LoRaMacClassBDeviceTimeAns( );
MacCtx.McpsIndication.DeviceTimeAnsReceived = true;
}
else
{
// Incase of other receive windows the Device Time Answer is not received.
MacCtx.McpsIndication.DeviceTimeAnsReceived = false;
}
break;
}
case SRV_MAC_PING_SLOT_INFO_ANS:
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_PING_SLOT_INFO ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_PING_SLOT_INFO );
// According to the specification, it is not allowed to process this answer in
// a ping or multicast slot
if( ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_PING_SLOT ) && ( MacCtx.RxSlot != RX_SLOT_WIN_CLASS_B_MULTICAST_SLOT ) )
{
LoRaMacClassBPingSlotInfoAns( );
}
}
break;
}
case SRV_MAC_PING_SLOT_CHANNEL_REQ:
{
uint8_t status = 0x03;
uint32_t frequency = 0;
uint8_t datarate;
frequency = ( uint32_t )payload[macIndex++];
frequency |= ( uint32_t )payload[macIndex++] << 8;
frequency |= ( uint32_t )payload[macIndex++] << 16;
frequency *= 100;
datarate = payload[macIndex++] & 0x0F;
status = LoRaMacClassBPingSlotChannelReq( datarate, frequency );
macCmdPayload[0] = status;
LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_CHANNEL_ANS, macCmdPayload, 1 );
// Setup indication to inform the application
SetMlmeScheduleUplinkIndication( );
break;
}
case SRV_MAC_BEACON_TIMING_ANS:
{
if( LoRaMacConfirmQueueIsCmdActive( MLME_BEACON_TIMING ) == true )
{
LoRaMacConfirmQueueSetStatus( LORAMAC_EVENT_INFO_STATUS_OK, MLME_BEACON_TIMING );
uint16_t beaconTimingDelay = 0;
uint8_t beaconTimingChannel = 0;
beaconTimingDelay = ( uint16_t )payload[macIndex++];
beaconTimingDelay |= ( uint16_t )payload[macIndex++] << 8;
beaconTimingChannel = payload[macIndex++];
LoRaMacClassBBeaconTimingAns( beaconTimingDelay, beaconTimingChannel, RxDoneParams.LastRxDone );
}
break;
}
case SRV_MAC_BEACON_FREQ_REQ:
{
uint32_t frequency = 0;
frequency = ( uint32_t )payload[macIndex++];
frequency |= ( uint32_t )payload[macIndex++] << 8;
frequency |= ( uint32_t )payload[macIndex++] << 16;
frequency *= 100;
if( LoRaMacClassBBeaconFreqReq( frequency ) == true )
{
macCmdPayload[0] = 1;
}
else
{
macCmdPayload[0] = 0;
}
LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_FREQ_ANS, macCmdPayload, 1 );
}
break;
default:
// Unknown command. ABORT MAC commands processing
return;
}
}
}
LoRaMacStatus_t Send( LoRaMacHeader_t* macHdr, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
{
LoRaMacFrameCtrl_t fCtrl;
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
int8_t datarate = Nvm.MacGroup1.ChannelsDatarate;
int8_t txPower = Nvm.MacGroup1.ChannelsTxPower;
uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter;
CalcNextAdrParams_t adrNext;
// Check if we are joined
if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
{
return LORAMAC_STATUS_NO_NETWORK_JOINED;
}
if( Nvm.MacGroup2.MaxDCycle == 0 )
{
Nvm.MacGroup1.AggregatedTimeOff = 0;
}
fCtrl.Value = 0;
fCtrl.Bits.FOptsLen = 0;
fCtrl.Bits.Adr = Nvm.MacGroup2.AdrCtrlOn;
// Check class b
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
fCtrl.Bits.FPending = 1;
}
else
{
fCtrl.Bits.FPending = 0;
}
// Check server ack
if( Nvm.MacGroup1.SrvAckRequested == true )
{
fCtrl.Bits.Ack = 1;
}
// ADR next request
adrNext.UpdateChanMask = true;
adrNext.AdrEnabled = fCtrl.Bits.Adr;
adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter;
adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate;
adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower;
adrNext.NbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
adrNext.Region = Nvm.MacGroup2.Region;
fCtrl.Bits.AdrAckReq = LoRaMacAdrCalcNext( &adrNext, &Nvm.MacGroup1.ChannelsDatarate,
&Nvm.MacGroup1.ChannelsTxPower,
&Nvm.MacGroup2.MacParams.ChannelsNbTrans, &adrAckCounter );
// Prepare the frame
status = PrepareFrame( macHdr, &fCtrl, fPort, fBuffer, fBufferSize );
// Validate status
if( ( status == LORAMAC_STATUS_OK ) || ( status == LORAMAC_STATUS_SKIPPED_APP_DATA ) )
{
// Schedule frame, do not allow delayed transmissions
status = ScheduleTx( false );
}
// Post processing
if( status != LORAMAC_STATUS_OK )
{
// Bad case - restore
// Store local variables
Nvm.MacGroup1.ChannelsDatarate = datarate;
Nvm.MacGroup1.ChannelsTxPower = txPower;
}
else
{
// Good case
Nvm.MacGroup1.SrvAckRequested = false;
Nvm.MacGroup1.AdrAckCounter = adrAckCounter;
// Remove all none sticky MAC commands
if( LoRaMacCommandsRemoveNoneStickyCmds( ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
}
return status;
}
LoRaMacStatus_t SendReJoinReq( JoinReqIdentifier_t joinReqType )
{
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
LoRaMacHeader_t macHdr;
macHdr.Value = 0;
bool allowDelayedTx = true;
// Setup join/rejoin message
switch( joinReqType )
{
case JOIN_REQ:
{
SwitchClass( CLASS_A );
MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_JOIN_REQUEST;
MacCtx.TxMsg.Message.JoinReq.Buffer = MacCtx.PktBuffer;
MacCtx.TxMsg.Message.JoinReq.BufSize = LORAMAC_PHY_MAXPAYLOAD;
macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ;
MacCtx.TxMsg.Message.JoinReq.MHDR.Value = macHdr.Value;
memcpy1( MacCtx.TxMsg.Message.JoinReq.JoinEUI, SecureElementGetJoinEui( ), LORAMAC_JOIN_EUI_FIELD_SIZE );
memcpy1( MacCtx.TxMsg.Message.JoinReq.DevEUI, SecureElementGetDevEui( ), LORAMAC_DEV_EUI_FIELD_SIZE );
allowDelayedTx = false;
break;
}
default:
status = LORAMAC_STATUS_SERVICE_UNKNOWN;
break;
}
// Schedule frame
status = ScheduleTx( allowDelayedTx );
return status;
}
static LoRaMacStatus_t CheckForClassBCollision( void )
{
if( LoRaMacClassBIsBeaconExpected( ) == true )
{
return LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME;
}
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
if( LoRaMacClassBIsPingExpected( ) == true )
{
return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
}
else if( LoRaMacClassBIsMulticastExpected( ) == true )
{
return LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME;
}
}
return LORAMAC_STATUS_OK;
}
static void ComputeRxWindowParameters( void )
{
// Compute Rx1 windows parameters
RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
RegionApplyDrOffset( Nvm.MacGroup2.Region,
Nvm.MacGroup2.MacParams.DownlinkDwellTime,
Nvm.MacGroup1.ChannelsDatarate,
Nvm.MacGroup2.MacParams.Rx1DrOffset ),
Nvm.MacGroup2.MacParams.MinRxSymbols,
Nvm.MacGroup2.MacParams.SystemMaxRxError,
&MacCtx.RxWindow1Config );
// Compute Rx2 windows parameters
RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
Nvm.MacGroup2.MacParams.Rx2Channel.Datarate,
Nvm.MacGroup2.MacParams.MinRxSymbols,
Nvm.MacGroup2.MacParams.SystemMaxRxError,
&MacCtx.RxWindow2Config );
// Default setup, in case the device joined
MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.ReceiveDelay1 + MacCtx.RxWindow1Config.WindowOffset;
MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.ReceiveDelay2 + MacCtx.RxWindow2Config.WindowOffset;
if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
{
MacCtx.RxWindow1Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay1 + MacCtx.RxWindow1Config.WindowOffset;
MacCtx.RxWindow2Delay = Nvm.MacGroup2.MacParams.JoinAcceptDelay2 + MacCtx.RxWindow2Config.WindowOffset;
}
}
static LoRaMacStatus_t VerifyTxFrame( void )
{
size_t macCmdsSize = 0;
if( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE )
{
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
if( ValidatePayloadLength( MacCtx.AppDataSize, Nvm.MacGroup1.ChannelsDatarate, macCmdsSize ) == false )
{
return LORAMAC_STATUS_LENGTH_ERROR;
}
}
return LORAMAC_STATUS_OK;
}
static LoRaMacStatus_t SerializeTxFrame( void )
{
LoRaMacSerializerStatus_t serializeStatus;
switch( MacCtx.TxMsg.Type )
{
case LORAMAC_MSG_TYPE_JOIN_REQUEST:
serializeStatus = LoRaMacSerializerJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
break;
case LORAMAC_MSG_TYPE_DATA:
serializeStatus = LoRaMacSerializerData( &MacCtx.TxMsg.Message.Data );
if( LORAMAC_SERIALIZER_SUCCESS != serializeStatus )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
break;
case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
case LORAMAC_MSG_TYPE_UNDEF:
default:
return LORAMAC_STATUS_PARAMETER_INVALID;
}
return LORAMAC_STATUS_OK;
}
static LoRaMacStatus_t ScheduleTx( bool allowDelayedTx )
{
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
NextChanParams_t nextChan;
// Check class b collisions
status = CheckForClassBCollision( );
if( status != LORAMAC_STATUS_OK )
{
return status;
}
// Update back-off
CalculateBackOff( );
// Serialize frame
status = SerializeTxFrame( );
if( status != LORAMAC_STATUS_OK )
{
return status;
}
nextChan.AggrTimeOff = Nvm.MacGroup1.AggregatedTimeOff;
nextChan.Datarate = Nvm.MacGroup1.ChannelsDatarate;
nextChan.DutyCycleEnabled = Nvm.MacGroup2.DutyCycleOn;
nextChan.ElapsedTimeSinceStartUp = SysTimeSub( SysTimeGetMcuTime( ), Nvm.MacGroup2.InitializationTime );
nextChan.LastAggrTx = Nvm.MacGroup1.LastTxDoneTime;
nextChan.LastTxIsJoinRequest = false;
nextChan.Joined = true;
nextChan.PktLen = MacCtx.PktBufferLen;
// Setup the parameters based on the join status
if( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_NONE )
{
nextChan.LastTxIsJoinRequest = true;
nextChan.Joined = false;
}
// Select channel
status = RegionNextChannel( Nvm.MacGroup2.Region, &nextChan, &MacCtx.Channel, &MacCtx.DutyCycleWaitTime, &Nvm.MacGroup1.AggregatedTimeOff );
if( status != LORAMAC_STATUS_OK )
{
if( ( status == LORAMAC_STATUS_DUTYCYCLE_RESTRICTED ) &&
( allowDelayedTx == true ) )
{
// Allow delayed transmissions. We have to allow it in case
// the MAC must retransmit a frame with the frame repetitions
if( MacCtx.DutyCycleWaitTime != 0 )
{// Send later - prepare timer
MacCtx.MacState |= LORAMAC_TX_DELAYED;
TimerSetValue( &MacCtx.TxDelayedTimer, MacCtx.DutyCycleWaitTime );
TimerStart( &MacCtx.TxDelayedTimer );
}
return LORAMAC_STATUS_OK;
}
else
{// State where the MAC cannot send a frame
return status;
}
}
// Compute window parameters, offsets, rx symbols, system errors etc.
ComputeRxWindowParameters( );
// Verify TX frame
status = VerifyTxFrame( );
if( status != LORAMAC_STATUS_OK )
{
return status;
}
// Try to send now
return SendFrameOnChannel( MacCtx.Channel );
}
static LoRaMacStatus_t SecureFrame( uint8_t txDr, uint8_t txCh )
{
LoRaMacCryptoStatus_t macCryptoStatus = LORAMAC_CRYPTO_ERROR;
uint32_t fCntUp = 0;
switch( MacCtx.TxMsg.Type )
{
case LORAMAC_MSG_TYPE_JOIN_REQUEST:
macCryptoStatus = LoRaMacCryptoPrepareJoinRequest( &MacCtx.TxMsg.Message.JoinReq );
if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.JoinReq.BufSize;
break;
case LORAMAC_MSG_TYPE_DATA:
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
{
return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
}
if( MacCtx.ChannelsNbTransCounter >= 1 )
{
fCntUp -= 1;
}
macCryptoStatus = LoRaMacCryptoSecureMessage( fCntUp, txDr, txCh, &MacCtx.TxMsg.Message.Data );
if( LORAMAC_CRYPTO_SUCCESS != macCryptoStatus )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
MacCtx.PktBufferLen = MacCtx.TxMsg.Message.Data.BufSize;
break;
case LORAMAC_MSG_TYPE_JOIN_ACCEPT:
case LORAMAC_MSG_TYPE_UNDEF:
default:
return LORAMAC_STATUS_PARAMETER_INVALID;
}
return LORAMAC_STATUS_OK;
}
static void CalculateBackOff( void )
{
// Make sure that the calculation of the backoff time for the aggregated time off will only be done in
// case the value is zero. It will be set to zero in the function RegionNextChannel.
if( Nvm.MacGroup1.AggregatedTimeOff == 0 )
{
// Update aggregated time-off. This must be an assignment and no incremental
// update as we do only calculate the time-off based on the last transmission
Nvm.MacGroup1.AggregatedTimeOff = ( MacCtx.TxTimeOnAir * Nvm.MacGroup2.AggregatedDCycle - MacCtx.TxTimeOnAir );
}
}
static void RemoveMacCommands( LoRaMacRxSlot_t rxSlot, LoRaMacFrameCtrl_t fCtrl, Mcps_t request )
{
if( rxSlot == RX_SLOT_WIN_1 || rxSlot == RX_SLOT_WIN_2 )
{
// Remove all sticky MAC commands answers since we can assume
// that they have been received by the server.
if( request == MCPS_CONFIRMED )
{
if( fCtrl.Bits.Ack == 1 )
{ // For confirmed uplinks only if we have received an ACK.
LoRaMacCommandsRemoveStickyAnsCmds( );
}
}
else
{
LoRaMacCommandsRemoveStickyAnsCmds( );
}
}
}
static void ResetMacParameters( void )
{
LoRaMacClassBCallback_t classBCallbacks;
LoRaMacClassBParams_t classBParams;
Nvm.MacGroup2.NetworkActivation = ACTIVATION_TYPE_NONE;
// ADR counter
Nvm.MacGroup1.AdrAckCounter = 0;
MacCtx.ChannelsNbTransCounter = 0;
MacCtx.RetransmitTimeoutRetry = false;
Nvm.MacGroup2.MaxDCycle = 0;
Nvm.MacGroup2.AggregatedDCycle = 1;
Nvm.MacGroup1.ChannelsTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
Nvm.MacGroup1.ChannelsDatarate = Nvm.MacGroup2.ChannelsDatarateDefault;
Nvm.MacGroup2.MacParams.Rx1DrOffset = Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset;
Nvm.MacGroup2.MacParams.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel;
Nvm.MacGroup2.MacParams.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel;
Nvm.MacGroup2.MacParams.UplinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime;
Nvm.MacGroup2.MacParams.DownlinkDwellTime = Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime;
Nvm.MacGroup2.MacParams.MaxEirp = Nvm.MacGroup2.MacParamsDefaults.MaxEirp;
Nvm.MacGroup2.MacParams.AntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain;
MacCtx.NodeAckRequested = false;
Nvm.MacGroup1.SrvAckRequested = false;
Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false;
Nvm.MacGroup2.DownlinkReceived = false;
// Reset to application defaults
InitDefaultsParams_t params;
params.Type = INIT_TYPE_RESET_TO_DEFAULT_CHANNELS;
params.NvmGroup1 = &Nvm.RegionGroup1;
params.NvmGroup2 = &Nvm.RegionGroup2;
RegionInitDefaults( Nvm.MacGroup2.Region, &params );
// Initialize channel index.
MacCtx.Channel = 0;
// Initialize Rx2 config parameters.
MacCtx.RxWindow2Config.Channel = MacCtx.Channel;
MacCtx.RxWindow2Config.Frequency = Nvm.MacGroup2.MacParams.Rx2Channel.Frequency;
MacCtx.RxWindow2Config.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
MacCtx.RxWindow2Config.RxContinuous = false;
MacCtx.RxWindow2Config.RxSlot = RX_SLOT_WIN_2;
MacCtx.RxWindow2Config.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
// Initialize RxC config parameters.
MacCtx.RxWindowCConfig = MacCtx.RxWindow2Config;
MacCtx.RxWindowCConfig.RxContinuous = true;
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
// Initialize class b
// Apply callback
classBCallbacks.GetTemperatureLevel = NULL;
classBCallbacks.MacProcessNotify = NULL;
if( MacCtx.MacCallbacks != NULL )
{
classBCallbacks.GetTemperatureLevel = MacCtx.MacCallbacks->GetTemperatureLevel;
classBCallbacks.MacProcessNotify = MacCtx.MacCallbacks->MacProcessNotify;
}
// Must all be static. Don't use local references.
classBParams.MlmeIndication = &MacCtx.MlmeIndication;
classBParams.McpsIndication = &MacCtx.McpsIndication;
classBParams.MlmeConfirm = &MacCtx.MlmeConfirm;
classBParams.LoRaMacFlags = &MacCtx.MacFlags;
classBParams.LoRaMacDevAddr = &Nvm.MacGroup2.DevAddr;
classBParams.LoRaMacRegion = &Nvm.MacGroup2.Region;
classBParams.LoRaMacParams = &Nvm.MacGroup2.MacParams;
classBParams.MulticastChannels = &Nvm.MacGroup2.MulticastChannelList[0];
LoRaMacClassBInit( &classBParams, &classBCallbacks, &Nvm.ClassB );
}
/*!
* \brief Initializes and opens the reception window
*
* \param [IN] rxTimer Window timer to be topped.
* \param [IN] rxConfig Window parameters to be setup
*/
static void RxWindowSetup( TimerEvent_t* rxTimer, RxConfigParams_t* rxConfig )
{
TimerStop( rxTimer );
// Ensure the radio is Idle
Radio.Standby( );
if( RegionRxConfig( Nvm.MacGroup2.Region, rxConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
{
Radio.Rx( Nvm.MacGroup2.MacParams.MaxRxWindow );
MacCtx.RxSlot = rxConfig->RxSlot;
}
}
static void OpenContinuousRxCWindow( void )
{
// Compute RxC windows parameters
RegionComputeRxWindowParameters( Nvm.MacGroup2.Region,
Nvm.MacGroup2.MacParams.RxCChannel.Datarate,
Nvm.MacGroup2.MacParams.MinRxSymbols,
Nvm.MacGroup2.MacParams.SystemMaxRxError,
&MacCtx.RxWindowCConfig );
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
MacCtx.RxWindowCConfig.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
// Setup continuous listening
MacCtx.RxWindowCConfig.RxContinuous = true;
// At this point the Radio should be idle.
// Thus, there is no need to set the radio in standby mode.
if( RegionRxConfig( Nvm.MacGroup2.Region, &MacCtx.RxWindowCConfig, ( int8_t* )&MacCtx.McpsIndication.RxDatarate ) == true )
{
Radio.Rx( 0 ); // Continuous mode
MacCtx.RxSlot = MacCtx.RxWindowCConfig.RxSlot;
}
}
LoRaMacStatus_t PrepareFrame( LoRaMacHeader_t* macHdr, LoRaMacFrameCtrl_t* fCtrl, uint8_t fPort, void* fBuffer, uint16_t fBufferSize )
{
MacCtx.PktBufferLen = 0;
MacCtx.NodeAckRequested = false;
uint32_t fCntUp = 0;
size_t macCmdsSize = 0;
uint8_t availableSize = 0;
if( fBuffer == NULL )
{
fBufferSize = 0;
}
memcpy1( MacCtx.AppData, ( uint8_t* ) fBuffer, fBufferSize );
MacCtx.AppDataSize = fBufferSize;
MacCtx.PktBuffer[0] = macHdr->Value;
switch( macHdr->Bits.MType )
{
case FRAME_TYPE_DATA_CONFIRMED_UP:
MacCtx.NodeAckRequested = true;
// Intentional fall through
case FRAME_TYPE_DATA_UNCONFIRMED_UP:
MacCtx.TxMsg.Type = LORAMAC_MSG_TYPE_DATA;
MacCtx.TxMsg.Message.Data.Buffer = MacCtx.PktBuffer;
MacCtx.TxMsg.Message.Data.BufSize = LORAMAC_PHY_MAXPAYLOAD;
MacCtx.TxMsg.Message.Data.MHDR.Value = macHdr->Value;
MacCtx.TxMsg.Message.Data.FPort = fPort;
MacCtx.TxMsg.Message.Data.FHDR.DevAddr = Nvm.MacGroup2.DevAddr;
MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
MacCtx.TxMsg.Message.Data.FRMPayloadSize = MacCtx.AppDataSize;
MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.AppData;
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoGetFCntUp( &fCntUp ) )
{
return LORAMAC_STATUS_FCNT_HANDLER_ERROR;
}
MacCtx.TxMsg.Message.Data.FHDR.FCnt = ( uint16_t )fCntUp;
// Reset confirm parameters
MacCtx.McpsConfirm.NbTrans = 0;
MacCtx.McpsConfirm.AckReceived = false;
MacCtx.McpsConfirm.UpLinkCounter = fCntUp;
// Handle the MAC commands if there are any available
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
if( macCmdsSize > 0 )
{
availableSize = GetMaxAppPayloadWithoutFOptsLength( Nvm.MacGroup1.ChannelsDatarate );
// There is application payload available and the MAC commands fit into FOpts field.
if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize <= LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
{
if( LoRaMacCommandsSerializeCmds( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH, &macCmdsSize, MacCtx.TxMsg.Message.Data.FHDR.FOpts ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
fCtrl->Bits.FOptsLen = macCmdsSize;
// Update FCtrl field with new value of FOptionsLength
MacCtx.TxMsg.Message.Data.FHDR.FCtrl.Value = fCtrl->Value;
}
// There is application payload available but the MAC commands does NOT fit into FOpts field.
else if( ( MacCtx.AppDataSize > 0 ) && ( macCmdsSize > LORA_MAC_COMMAND_MAX_FOPTS_LENGTH ) )
{
if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
return LORAMAC_STATUS_SKIPPED_APP_DATA;
}
// No application payload available therefore add all mac commands to the FRMPayload.
else
{
if( LoRaMacCommandsSerializeCmds( availableSize, &macCmdsSize, MacCtx.MacCommandsBuffer ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
// Force FPort to be zero
MacCtx.TxMsg.Message.Data.FPort = 0;
MacCtx.TxMsg.Message.Data.FRMPayload = MacCtx.MacCommandsBuffer;
MacCtx.TxMsg.Message.Data.FRMPayloadSize = macCmdsSize;
}
}
break;
case FRAME_TYPE_PROPRIETARY:
if( ( fBuffer != NULL ) && ( MacCtx.AppDataSize > 0 ) )
{
memcpy1( MacCtx.PktBuffer + LORAMAC_MHDR_FIELD_SIZE, ( uint8_t* ) fBuffer, MacCtx.AppDataSize );
MacCtx.PktBufferLen = LORAMAC_MHDR_FIELD_SIZE + MacCtx.AppDataSize;
}
break;
default:
return LORAMAC_STATUS_SERVICE_UNKNOWN;
}
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t SendFrameOnChannel( uint8_t channel )
{
LoRaMacStatus_t status = LORAMAC_STATUS_PARAMETER_INVALID;
TxConfigParams_t txConfig;
int8_t txPower = 0;
txConfig.Channel = channel;
txConfig.Datarate = Nvm.MacGroup1.ChannelsDatarate;
txConfig.TxPower = Nvm.MacGroup1.ChannelsTxPower;
txConfig.MaxEirp = Nvm.MacGroup2.MacParams.MaxEirp;
txConfig.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain;
txConfig.PktLen = MacCtx.PktBufferLen;
RegionTxConfig( Nvm.MacGroup2.Region, &txConfig, &txPower, &MacCtx.TxTimeOnAir );
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
MacCtx.McpsConfirm.Datarate = Nvm.MacGroup1.ChannelsDatarate;
MacCtx.McpsConfirm.TxPower = txPower;
MacCtx.McpsConfirm.Channel = channel;
// Store the time on air
MacCtx.McpsConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
MacCtx.MlmeConfirm.TxTimeOnAir = MacCtx.TxTimeOnAir;
if( LoRaMacClassBIsBeaconModeActive( ) == true )
{
// Currently, the Time-On-Air can only be computed when the radio is configured with
// the TX configuration
TimerTime_t collisionTime = LoRaMacClassBIsUplinkCollision( MacCtx.TxTimeOnAir );
if( collisionTime > 0 )
{
return LORAMAC_STATUS_BUSY_UPLINK_COLLISION;
}
}
if( Nvm.MacGroup2.DeviceClass == CLASS_B )
{
// Stop slots for class b
LoRaMacClassBStopRxSlots( );
}
LoRaMacClassBHaltBeaconing( );
// Secure frame
status = SecureFrame( Nvm.MacGroup1.ChannelsDatarate, MacCtx.Channel );
if( status != LORAMAC_STATUS_OK )
{
return status;
}
MacCtx.MacState |= LORAMAC_TX_RUNNING;
MacCtx.ChannelsNbTransCounter++;
MacCtx.McpsConfirm.NbTrans = MacCtx.ChannelsNbTransCounter;
MacCtx.ResponseTimeoutStartTime = 0;
// Send now
Radio.Send( MacCtx.PktBuffer, MacCtx.PktBufferLen );
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t SetTxContinuousWave( uint16_t timeout, uint32_t frequency, uint8_t power )
{
Radio.SetTxContinuousWave( frequency, power, timeout );
MacCtx.MacState |= LORAMAC_TX_RUNNING;
return LORAMAC_STATUS_OK;
}
LoRaMacNvmData_t* GetNvmData( void )
{
return &Nvm;
}
LoRaMacStatus_t RestoreNvmData( LoRaMacNvmData_t* nvm )
{
uint32_t crc = 0;
// Status and parameter validation
if( nvm == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( MacCtx.MacState != LORAMAC_STOPPED )
{
return LORAMAC_STATUS_BUSY;
}
// Crypto
crc = Crc32( ( uint8_t* ) &nvm->Crypto, sizeof( nvm->Crypto ) -
sizeof( nvm->Crypto.Crc32 ) );
if( crc == nvm->Crypto.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.Crypto, ( uint8_t* ) &nvm->Crypto,
sizeof( Nvm.Crypto ) );
}
// MacGroup1
crc = Crc32( ( uint8_t* ) &nvm->MacGroup1, sizeof( nvm->MacGroup1 ) -
sizeof( nvm->MacGroup1.Crc32 ) );
if( crc == nvm->MacGroup1.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.MacGroup1, ( uint8_t* ) &nvm->MacGroup1,
sizeof( Nvm.MacGroup1 ) );
}
// MacGroup2
crc = Crc32( ( uint8_t* ) &nvm->MacGroup2, sizeof( nvm->MacGroup2 ) -
sizeof( nvm->MacGroup2.Crc32 ) );
if( crc == nvm->MacGroup2.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.MacGroup2, ( uint8_t* ) &nvm->MacGroup2,
sizeof( Nvm.MacGroup2 ) );
// Initialize RxC config parameters.
MacCtx.RxWindowCConfig.Channel = MacCtx.Channel;
MacCtx.RxWindowCConfig.Frequency = Nvm.MacGroup2.MacParams.RxCChannel.Frequency;
MacCtx.RxWindowCConfig.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
MacCtx.RxWindowCConfig.RxContinuous = true;
MacCtx.RxWindowCConfig.RxSlot = RX_SLOT_WIN_CLASS_C;
}
// Secure Element
crc = Crc32( ( uint8_t* ) &nvm->SecureElement, sizeof( nvm->SecureElement ) -
sizeof( nvm->SecureElement.Crc32 ) );
if( crc == nvm->SecureElement.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.SecureElement,( uint8_t* ) &nvm->SecureElement,
sizeof( Nvm.SecureElement ) );
}
// Region
crc = Crc32( ( uint8_t* ) &nvm->RegionGroup1, sizeof( nvm->RegionGroup1 ) -
sizeof( nvm->RegionGroup1.Crc32 ) );
if( crc == nvm->RegionGroup1.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.RegionGroup1,( uint8_t* ) &nvm->RegionGroup1,
sizeof( Nvm.RegionGroup1 ) );
}
crc = Crc32( ( uint8_t* ) &nvm->ClassB, sizeof( nvm->ClassB ) -
sizeof( nvm->ClassB.Crc32 ) );
if( crc == nvm->ClassB.Crc32 )
{
memcpy1( ( uint8_t* ) &Nvm.ClassB,( uint8_t* ) &nvm->ClassB,
sizeof( Nvm.ClassB ) );
}
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t DetermineFrameType( LoRaMacMessageData_t* macMsg, FType_t* fType )
{
if( ( macMsg == NULL ) || ( fType == NULL ) )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
/* The LoRaWAN specification allows several possible configurations how data up/down frames are built up.
* In sake of clearness the following naming is applied. Please keep in mind that this is
* implementation specific since there is no definition in the LoRaWAN specification included.
*
* X -> Field is available
* - -> Field is not available
*
* +-------+ +----------+------+-------+--------------+
* | FType | | FOptsLen | Fopt | FPort | FRMPayload |
* +-------+ +----------+------+-------+--------------+
* | A | | > 0 | X | > 0 | X |
* +-------+ +----------+------+-------+--------------+
* | B | | >= 0 | X/- | - | - |
* +-------+ +----------+------+-------+--------------+
* | C | | = 0 | - | = 0 | MAC commands |
* +-------+ +----------+------+-------+--------------+
* | D | | = 0 | - | > 0 | X |
* +-------+ +----------+------+-------+--------------+
*/
if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen > 0 ) && ( macMsg->FPort > 0 ) )
{
*fType = FRAME_TYPE_A;
}
else if( macMsg->FRMPayloadSize == 0 )
{
*fType = FRAME_TYPE_B;
}
else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort == 0 ) )
{
*fType = FRAME_TYPE_C;
}
else if( ( macMsg->FHDR.FCtrl.Bits.FOptsLen == 0 ) && ( macMsg->FPort > 0 ) )
{
*fType = FRAME_TYPE_D;
}
else
{
// Should never happen.
return LORAMAC_STATUS_ERROR;
}
return LORAMAC_STATUS_OK;
}
static bool CheckRetrans( uint8_t counter, uint8_t limit )
{
if( counter >= limit )
{
return true;
}
return false;
}
static bool CheckRetransUnconfirmedUplink( void )
{
// Verify, if the max number of retransmissions have been reached
if( CheckRetrans( MacCtx.ChannelsNbTransCounter,
Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true )
{
return true;
}
if( MacCtx.MacFlags.Bits.McpsInd == 1 )
{
// Stop the retransmissions, if a valid downlink is received
// a class A RX window. This holds also for class B and C.
if( ( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_1 ) ||
( MacCtx.McpsIndication.RxSlot == RX_SLOT_WIN_2 ) )
{
return true;
}
}
return false;
}
static bool CheckRetransConfirmedUplink( void )
{
// Verify, if the max number of retransmissions have been reached
if( CheckRetrans( MacCtx.ChannelsNbTransCounter,
Nvm.MacGroup2.MacParams.ChannelsNbTrans ) == true )
{
return true;
}
if( MacCtx.MacFlags.Bits.McpsInd == 1 )
{
if( MacCtx.McpsConfirm.AckReceived == true )
{
return true;
}
}
return false;
}
static uint32_t IncreaseAdrAckCounter( uint32_t counter )
{
if( counter < ADR_ACK_COUNTER_MAX )
{
counter++;
}
return counter;
}
static bool StopRetransmission( void )
{
if( ( MacCtx.MacFlags.Bits.McpsInd == 0 ) ||
( ( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_1 ) &&
( MacCtx.McpsIndication.RxSlot != RX_SLOT_WIN_2 ) ) )
{ // Maximum repetitions without downlink. Increase ADR Ack counter.
// Only process the case when the MAC did not receive a downlink.
if( Nvm.MacGroup2.AdrCtrlOn == true )
{
Nvm.MacGroup1.AdrAckCounter = IncreaseAdrAckCounter( Nvm.MacGroup1.AdrAckCounter );
}
}
MacCtx.ChannelsNbTransCounter = 0;
MacCtx.NodeAckRequested = false;
MacCtx.RetransmitTimeoutRetry = false;
MacCtx.MacState &= ~LORAMAC_TX_RUNNING;
return true;
}
static void CallNvmDataChangeCallback( uint16_t notifyFlags )
{
if( ( MacCtx.MacCallbacks != NULL ) &&
( MacCtx.MacCallbacks->NvmDataChange != NULL ) )
{
MacCtx.MacCallbacks->NvmDataChange ( notifyFlags );
}
}
static uint8_t IsRequestPending( void )
{
if( ( MacCtx.MacFlags.Bits.MlmeReq == 1 ) ||
( MacCtx.MacFlags.Bits.McpsReq == 1 ) )
{
return 1;
}
return 0;
}
LoRaMacStatus_t LoRaMacInitialization( LoRaMacPrimitives_t* primitives, LoRaMacCallback_t* callbacks, LoRaMacRegion_t region )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
if( ( primitives == NULL ) ||
( callbacks == NULL ) )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( ( primitives->MacMcpsConfirm == NULL ) ||
( primitives->MacMcpsIndication == NULL ) ||
( primitives->MacMlmeConfirm == NULL ) ||
( primitives->MacMlmeIndication == NULL ) )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
// Verify if the region is supported
if( RegionIsActive( region ) == false )
{
return LORAMAC_STATUS_REGION_NOT_SUPPORTED;
}
// Confirm queue reset
LoRaMacConfirmQueueInit( primitives );
// Initialize the module context with zeros
memset1( ( uint8_t* ) &Nvm, 0x00, sizeof( LoRaMacNvmData_t ) );
memset1( ( uint8_t* ) &MacCtx, 0x00, sizeof( LoRaMacCtx_t ) );
// Set non zero variables to its default value
Nvm.MacGroup2.Region = region;
Nvm.MacGroup2.DeviceClass = CLASS_A;
// Setup version
Nvm.MacGroup2.Version.Value = LORAMAC_VERSION;
// Reset to defaults
getPhy.Attribute = PHY_DUTY_CYCLE;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.DutyCycleOn = ( bool ) phyParam.Value;
getPhy.Attribute = PHY_DEF_TX_POWER;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.ChannelsTxPowerDefault = phyParam.Value;
getPhy.Attribute = PHY_DEF_TX_DR;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.ChannelsDatarateDefault = phyParam.Value;
getPhy.Attribute = PHY_MAX_RX_WINDOW;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow = phyParam.Value;
getPhy.Attribute = PHY_RECEIVE_DELAY1;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1 = phyParam.Value;
getPhy.Attribute = PHY_RECEIVE_DELAY2;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2 = phyParam.Value;
getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY1;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1 = phyParam.Value;
getPhy.Attribute = PHY_JOIN_ACCEPT_DELAY2;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2 = phyParam.Value;
getPhy.Attribute = PHY_DEF_DR1_OFFSET;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.Rx1DrOffset = phyParam.Value;
getPhy.Attribute = PHY_DEF_RX2_FREQUENCY;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Frequency = phyParam.Value;
Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Frequency = phyParam.Value;
getPhy.Attribute = PHY_DEF_RX2_DR;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.Rx2Channel.Datarate = phyParam.Value;
Nvm.MacGroup2.MacParamsDefaults.RxCChannel.Datarate = phyParam.Value;
getPhy.Attribute = PHY_DEF_UPLINK_DWELL_TIME;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.UplinkDwellTime = phyParam.Value;
getPhy.Attribute = PHY_DEF_DOWNLINK_DWELL_TIME;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.DownlinkDwellTime = phyParam.Value;
getPhy.Attribute = PHY_DEF_MAX_EIRP;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.MaxEirp = phyParam.fValue;
getPhy.Attribute = PHY_DEF_ANTENNA_GAIN;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
Nvm.MacGroup2.MacParamsDefaults.AntennaGain = phyParam.fValue;
getPhy.Attribute = PHY_DEF_ADR_ACK_LIMIT;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
MacCtx.AdrAckLimit = phyParam.Value;
getPhy.Attribute = PHY_DEF_ADR_ACK_DELAY;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
MacCtx.AdrAckDelay = phyParam.Value;
// Init parameters which are not set in function ResetMacParameters
Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans = 1;
Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = 10;
Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = 6;
Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError;
Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols;
Nvm.MacGroup2.MacParams.MaxRxWindow = Nvm.MacGroup2.MacParamsDefaults.MaxRxWindow;
Nvm.MacGroup2.MacParams.ReceiveDelay1 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay1;
Nvm.MacGroup2.MacParams.ReceiveDelay2 = Nvm.MacGroup2.MacParamsDefaults.ReceiveDelay2;
Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay1;
Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = Nvm.MacGroup2.MacParamsDefaults.JoinAcceptDelay2;
Nvm.MacGroup2.MacParams.ChannelsNbTrans = Nvm.MacGroup2.MacParamsDefaults.ChannelsNbTrans;
// FPort 224 is enabled by default.
Nvm.MacGroup2.IsCertPortOn = true;
InitDefaultsParams_t params;
params.Type = INIT_TYPE_DEFAULTS;
params.NvmGroup1 = &Nvm.RegionGroup1;
params.NvmGroup2 = &Nvm.RegionGroup2;
RegionInitDefaults( Nvm.MacGroup2.Region, &params );
ResetMacParameters( );
Nvm.MacGroup2.PublicNetwork = true;
MacCtx.MacPrimitives = primitives;
MacCtx.MacCallbacks = callbacks;
MacCtx.MacFlags.Value = 0;
MacCtx.MacState = LORAMAC_STOPPED;
// Reset duty cycle times
Nvm.MacGroup1.LastTxDoneTime = 0;
Nvm.MacGroup1.AggregatedTimeOff = 0;
// Initialize timers
TimerInit( &MacCtx.TxDelayedTimer, OnTxDelayedTimerEvent );
TimerInit( &MacCtx.RxWindowTimer1, OnRxWindow1TimerEvent );
TimerInit( &MacCtx.RxWindowTimer2, OnRxWindow2TimerEvent );
TimerInit( &MacCtx.RetransmitTimeoutTimer, OnRetransmitTimeoutTimerEvent );
// Store the current initialization time
Nvm.MacGroup2.InitializationTime = SysTimeGetMcuTime( );
// Initialize Radio driver
MacCtx.RadioEvents.TxDone = OnRadioTxDone;
MacCtx.RadioEvents.RxDone = OnRadioRxDone;
MacCtx.RadioEvents.RxError = OnRadioRxError;
MacCtx.RadioEvents.TxTimeout = OnRadioTxTimeout;
MacCtx.RadioEvents.RxTimeout = OnRadioRxTimeout;
Radio.Init( &MacCtx.RadioEvents );
// Initialize the Secure Element driver
if( SecureElementInit( &Nvm.SecureElement ) != SECURE_ELEMENT_SUCCESS )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
// Initialize Crypto module
if( LoRaMacCryptoInit( &Nvm.Crypto ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
// Initialize MAC commands module
if( LoRaMacCommandsInit( ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
// Set multicast downlink counter reference
if( LoRaMacCryptoSetMulticastReference( Nvm.MacGroup2.MulticastChannelList ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
// Random seed initialization
srand1( Radio.Random( ) );
Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork );
Radio.Sleep( );
LoRaMacEnableRequests( LORAMAC_REQUEST_HANDLING_ON );
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacStart( void )
{
MacCtx.MacState = LORAMAC_IDLE;
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacStop( void )
{
if( LoRaMacIsBusy( ) == false )
{
MacCtx.MacState = LORAMAC_STOPPED;
return LORAMAC_STATUS_OK;
}
else if( MacCtx.MacState == LORAMAC_STOPPED )
{
return LORAMAC_STATUS_OK;
}
return LORAMAC_STATUS_BUSY;
}
LoRaMacStatus_t LoRaMacQueryTxPossible( uint8_t size, LoRaMacTxInfo_t* txInfo )
{
CalcNextAdrParams_t adrNext;
uint32_t adrAckCounter = Nvm.MacGroup1.AdrAckCounter;
int8_t datarate = Nvm.MacGroup2.ChannelsDatarateDefault;
int8_t txPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
uint8_t nbTrans = MacCtx.ChannelsNbTransCounter;
size_t macCmdsSize = 0;
if( txInfo == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
// Setup ADR request
adrNext.UpdateChanMask = false;
adrNext.AdrEnabled = Nvm.MacGroup2.AdrCtrlOn;
adrNext.AdrAckCounter = Nvm.MacGroup1.AdrAckCounter;
adrNext.AdrAckLimit = MacCtx.AdrAckLimit;
adrNext.AdrAckDelay = MacCtx.AdrAckDelay;
adrNext.Datarate = Nvm.MacGroup1.ChannelsDatarate;
adrNext.TxPower = Nvm.MacGroup1.ChannelsTxPower;
adrNext.NbTrans = MacCtx.ChannelsNbTransCounter;
adrNext.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
adrNext.Region = Nvm.MacGroup2.Region;
// We call the function for information purposes only. We don't want to
// apply the datarate, the tx power and the ADR ack counter.
LoRaMacAdrCalcNext( &adrNext, &datarate, &txPower, &nbTrans, &adrAckCounter );
txInfo->CurrentPossiblePayloadSize = GetMaxAppPayloadWithoutFOptsLength( datarate );
if( LoRaMacCommandsGetSizeSerializedCmds( &macCmdsSize ) != LORAMAC_COMMANDS_SUCCESS )
{
return LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
// Verify if the MAC commands fit into the FOpts and into the maximum payload.
if( ( LORA_MAC_COMMAND_MAX_FOPTS_LENGTH >= macCmdsSize ) && ( txInfo->CurrentPossiblePayloadSize >= macCmdsSize ) )
{
txInfo->MaxPossibleApplicationDataSize = txInfo->CurrentPossiblePayloadSize - macCmdsSize;
// Verify if the application data together with MAC command fit into the maximum payload.
if( txInfo->CurrentPossiblePayloadSize >= ( macCmdsSize + size ) )
{
return LORAMAC_STATUS_OK;
}
else
{
return LORAMAC_STATUS_LENGTH_ERROR;
}
}
else
{
txInfo->MaxPossibleApplicationDataSize = 0;
return LORAMAC_STATUS_LENGTH_ERROR;
}
}
LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t* mibGet )
{
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
GetPhyParams_t getPhy;
PhyParam_t phyParam;
if( mibGet == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
switch( mibGet->Type )
{
case MIB_DEVICE_CLASS:
{
mibGet->Param.Class = Nvm.MacGroup2.DeviceClass;
break;
}
case MIB_NETWORK_ACTIVATION:
{
mibGet->Param.NetworkActivation = Nvm.MacGroup2.NetworkActivation;
break;
}
case MIB_DEV_EUI:
{
mibGet->Param.DevEui = SecureElementGetDevEui( );
break;
}
case MIB_JOIN_EUI:
{
mibGet->Param.JoinEui = SecureElementGetJoinEui( );
break;
}
case MIB_SE_PIN:
{
mibGet->Param.JoinEui = SecureElementGetPin( );
break;
}
case MIB_ADR:
{
mibGet->Param.AdrEnable = Nvm.MacGroup2.AdrCtrlOn;
break;
}
case MIB_NET_ID:
{
mibGet->Param.NetID = Nvm.MacGroup2.NetID;
break;
}
case MIB_DEV_ADDR:
{
mibGet->Param.DevAddr = Nvm.MacGroup2.DevAddr;
break;
}
case MIB_PUBLIC_NETWORK:
{
mibGet->Param.EnablePublicNetwork = Nvm.MacGroup2.PublicNetwork;
break;
}
case MIB_CHANNELS:
{
getPhy.Attribute = PHY_CHANNELS;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
mibGet->Param.ChannelList = phyParam.Channels;
break;
}
case MIB_RX2_CHANNEL:
{
mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParams.Rx2Channel;
break;
}
case MIB_RX2_DEFAULT_CHANNEL:
{
mibGet->Param.Rx2Channel = Nvm.MacGroup2.MacParamsDefaults.Rx2Channel;
break;
}
case MIB_RXC_CHANNEL:
{
mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParams.RxCChannel;
break;
}
case MIB_RXC_DEFAULT_CHANNEL:
{
mibGet->Param.RxCChannel = Nvm.MacGroup2.MacParamsDefaults.RxCChannel;
break;
}
case MIB_CHANNELS_DEFAULT_MASK:
{
getPhy.Attribute = PHY_CHANNELS_DEFAULT_MASK;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
mibGet->Param.ChannelsDefaultMask = phyParam.ChannelsMask;
break;
}
case MIB_CHANNELS_MASK:
{
getPhy.Attribute = PHY_CHANNELS_MASK;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
mibGet->Param.ChannelsMask = phyParam.ChannelsMask;
break;
}
case MIB_CHANNELS_NB_TRANS:
{
mibGet->Param.ChannelsNbTrans = Nvm.MacGroup2.MacParams.ChannelsNbTrans;
break;
}
case MIB_MAX_RX_WINDOW_DURATION:
{
mibGet->Param.MaxRxWindow = Nvm.MacGroup2.MacParams.MaxRxWindow;
break;
}
case MIB_RECEIVE_DELAY_1:
{
mibGet->Param.ReceiveDelay1 = Nvm.MacGroup2.MacParams.ReceiveDelay1;
break;
}
case MIB_RECEIVE_DELAY_2:
{
mibGet->Param.ReceiveDelay2 = Nvm.MacGroup2.MacParams.ReceiveDelay2;
break;
}
case MIB_JOIN_ACCEPT_DELAY_1:
{
mibGet->Param.JoinAcceptDelay1 = Nvm.MacGroup2.MacParams.JoinAcceptDelay1;
break;
}
case MIB_JOIN_ACCEPT_DELAY_2:
{
mibGet->Param.JoinAcceptDelay2 = Nvm.MacGroup2.MacParams.JoinAcceptDelay2;
break;
}
case MIB_CHANNELS_DEFAULT_DATARATE:
{
mibGet->Param.ChannelsDefaultDatarate = Nvm.MacGroup2.ChannelsDatarateDefault;
break;
}
case MIB_CHANNELS_DATARATE:
{
mibGet->Param.ChannelsDatarate = Nvm.MacGroup1.ChannelsDatarate;
break;
}
case MIB_CHANNELS_DEFAULT_TX_POWER:
{
mibGet->Param.ChannelsDefaultTxPower = Nvm.MacGroup2.ChannelsTxPowerDefault;
break;
}
case MIB_CHANNELS_TX_POWER:
{
mibGet->Param.ChannelsTxPower = Nvm.MacGroup1.ChannelsTxPower;
break;
}
case MIB_SYSTEM_MAX_RX_ERROR:
{
mibGet->Param.SystemMaxRxError = Nvm.MacGroup2.MacParams.SystemMaxRxError;
break;
}
case MIB_MIN_RX_SYMBOLS:
{
mibGet->Param.MinRxSymbols = Nvm.MacGroup2.MacParams.MinRxSymbols;
break;
}
case MIB_ANTENNA_GAIN:
{
mibGet->Param.AntennaGain = Nvm.MacGroup2.MacParams.AntennaGain;
break;
}
case MIB_NVM_CTXS:
{
mibGet->Param.Contexts = GetNvmData( );
break;
}
case MIB_DEFAULT_ANTENNA_GAIN:
{
mibGet->Param.DefaultAntennaGain = Nvm.MacGroup2.MacParamsDefaults.AntennaGain;
break;
}
case MIB_LORAWAN_VERSION:
{
mibGet->Param.LrWanVersion.LoRaWan = Nvm.MacGroup2.Version;
mibGet->Param.LrWanVersion.LoRaWanRegion = RegionGetVersion( );
break;
}
case MIB_IS_CERT_FPORT_ON:
{
mibGet->Param.IsCertPortOn = Nvm.MacGroup2.IsCertPortOn;
break;
}
default:
{
status = LoRaMacClassBMibGetRequestConfirm( mibGet );
break;
}
}
return status;
}
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t* mibSet )
{
LoRaMacStatus_t status = LORAMAC_STATUS_OK;
ChanMaskSetParams_t chanMaskSet;
VerifyParams_t verify;
if( mibSet == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
return LORAMAC_STATUS_BUSY;
}
switch( mibSet->Type )
{
case MIB_DEVICE_CLASS:
{
status = SwitchClass( mibSet->Param.Class );
break;
}
case MIB_NETWORK_ACTIVATION:
{
if( mibSet->Param.NetworkActivation != ACTIVATION_TYPE_OTAA )
{
Nvm.MacGroup2.NetworkActivation = mibSet->Param.NetworkActivation;
}
else
{ // Do not allow to set ACTIVATION_TYPE_OTAA since the MAC will set it automatically after a successful join process.
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_DEV_EUI:
{
if( SecureElementSetDevEui( mibSet->Param.DevEui ) != SECURE_ELEMENT_SUCCESS )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_JOIN_EUI:
{
if( SecureElementSetJoinEui( mibSet->Param.JoinEui ) != SECURE_ELEMENT_SUCCESS )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_SE_PIN:
{
if( SecureElementSetPin( mibSet->Param.SePin ) != SECURE_ELEMENT_SUCCESS )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_ADR:
{
Nvm.MacGroup2.AdrCtrlOn = mibSet->Param.AdrEnable;
break;
}
case MIB_NET_ID:
{
Nvm.MacGroup2.NetID = mibSet->Param.NetID;
break;
}
case MIB_DEV_ADDR:
{
Nvm.MacGroup2.DevAddr = mibSet->Param.DevAddr;
break;
}
case MIB_APP_KEY:
{
if( mibSet->Param.AppKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_KEY, mibSet->Param.AppKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_NWK_KEY:
{
if( mibSet->Param.NwkKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_KEY, mibSet->Param.NwkKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_J_S_INT_KEY:
{
if( mibSet->Param.JSIntKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_INT_KEY, mibSet->Param.JSIntKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_J_S_ENC_KEY:
{
if( mibSet->Param.JSEncKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( J_S_ENC_KEY, mibSet->Param.JSEncKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_F_NWK_S_INT_KEY:
{
if( mibSet->Param.FNwkSIntKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( F_NWK_S_INT_KEY, mibSet->Param.FNwkSIntKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_S_NWK_S_INT_KEY:
{
if( mibSet->Param.SNwkSIntKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( S_NWK_S_INT_KEY, mibSet->Param.SNwkSIntKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_NWK_S_ENC_KEY:
{
if( mibSet->Param.NwkSEncKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( NWK_S_ENC_KEY, mibSet->Param.NwkSEncKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_APP_S_KEY:
{
if( mibSet->Param.AppSKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( APP_S_KEY, mibSet->Param.AppSKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_KE_KEY:
{
if( mibSet->Param.McKEKey != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KE_KEY, mibSet->Param.McKEKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_KEY_0:
{
if( mibSet->Param.McKey0 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_0, mibSet->Param.McKey0 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_APP_S_KEY_0:
{
if( mibSet->Param.McAppSKey0 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_0, mibSet->Param.McAppSKey0 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_NWK_S_KEY_0:
{
if( mibSet->Param.McNwkSKey0 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_0, mibSet->Param.McNwkSKey0 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_KEY_1:
{
if( mibSet->Param.McKey1 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_1, mibSet->Param.McKey1 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_APP_S_KEY_1:
{
if( mibSet->Param.McAppSKey1 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_1, mibSet->Param.McAppSKey1 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_NWK_S_KEY_1:
{
if( mibSet->Param.McNwkSKey1 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_1, mibSet->Param.McNwkSKey1 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_KEY_2:
{
if( mibSet->Param.McKey2 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_2, mibSet->Param.McKey2 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_APP_S_KEY_2:
{
if( mibSet->Param.McAppSKey2 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_2, mibSet->Param.McAppSKey2 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_NWK_S_KEY_2:
{
if( mibSet->Param.McNwkSKey2 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_2, mibSet->Param.McNwkSKey2 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_KEY_3:
{
if( mibSet->Param.McKey3 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_KEY_3, mibSet->Param.McKey3 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_APP_S_KEY_3:
{
if( mibSet->Param.McAppSKey3 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_APP_S_KEY_3, mibSet->Param.McAppSKey3 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MC_NWK_S_KEY_3:
{
if( mibSet->Param.McNwkSKey3 != NULL )
{
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( MC_NWK_S_KEY_3, mibSet->Param.McNwkSKey3 ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_PUBLIC_NETWORK:
{
Nvm.MacGroup2.PublicNetwork = mibSet->Param.EnablePublicNetwork;
Radio.SetPublicNetwork( Nvm.MacGroup2.PublicNetwork );
break;
}
case MIB_RX2_CHANNEL:
{
verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
{
Nvm.MacGroup2.MacParams.Rx2Channel = mibSet->Param.Rx2Channel;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_RX2_DEFAULT_CHANNEL:
{
verify.DatarateParams.Datarate = mibSet->Param.Rx2Channel.Datarate;
verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
{
Nvm.MacGroup2.MacParamsDefaults.Rx2Channel = mibSet->Param.Rx2DefaultChannel;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_RXC_CHANNEL:
{
verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
{
Nvm.MacGroup2.MacParams.RxCChannel = mibSet->Param.RxCChannel;
if( ( Nvm.MacGroup2.DeviceClass == CLASS_C ) && ( Nvm.MacGroup2.NetworkActivation != ACTIVATION_TYPE_NONE ) )
{
// We can only compute the RX window parameters directly, if we are already
// in class c mode and joined. We cannot setup an RX window in case of any other
// class type.
// Set the radio into sleep mode in case we are still in RX mode
Radio.Sleep( );
OpenContinuousRxCWindow( );
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_RXC_DEFAULT_CHANNEL:
{
verify.DatarateParams.Datarate = mibSet->Param.RxCChannel.Datarate;
verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
{
Nvm.MacGroup2.MacParamsDefaults.RxCChannel = mibSet->Param.RxCDefaultChannel;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_DEFAULT_MASK:
{
chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsDefaultMask;
chanMaskSet.ChannelsMaskType = CHANNELS_DEFAULT_MASK;
if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_MASK:
{
chanMaskSet.ChannelsMaskIn = mibSet->Param.ChannelsMask;
chanMaskSet.ChannelsMaskType = CHANNELS_MASK;
if( RegionChanMaskSet( Nvm.MacGroup2.Region, &chanMaskSet ) == false )
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_NB_TRANS:
{
if( ( mibSet->Param.ChannelsNbTrans >= 1 ) &&
( mibSet->Param.ChannelsNbTrans <= 15 ) )
{
Nvm.MacGroup2.MacParams.ChannelsNbTrans = mibSet->Param.ChannelsNbTrans;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_MAX_RX_WINDOW_DURATION:
{
Nvm.MacGroup2.MacParams.MaxRxWindow = mibSet->Param.MaxRxWindow;
break;
}
case MIB_RECEIVE_DELAY_1:
{
Nvm.MacGroup2.MacParams.ReceiveDelay1 = mibSet->Param.ReceiveDelay1;
break;
}
case MIB_RECEIVE_DELAY_2:
{
Nvm.MacGroup2.MacParams.ReceiveDelay2 = mibSet->Param.ReceiveDelay2;
break;
}
case MIB_JOIN_ACCEPT_DELAY_1:
{
Nvm.MacGroup2.MacParams.JoinAcceptDelay1 = mibSet->Param.JoinAcceptDelay1;
break;
}
case MIB_JOIN_ACCEPT_DELAY_2:
{
Nvm.MacGroup2.MacParams.JoinAcceptDelay2 = mibSet->Param.JoinAcceptDelay2;
break;
}
case MIB_CHANNELS_DEFAULT_DATARATE:
{
verify.DatarateParams.Datarate = mibSet->Param.ChannelsDefaultDatarate;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_DR ) == true )
{
Nvm.MacGroup2.ChannelsDatarateDefault = verify.DatarateParams.Datarate;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_DATARATE:
{
verify.DatarateParams.Datarate = mibSet->Param.ChannelsDatarate;
verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true )
{
Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_DEFAULT_TX_POWER:
{
verify.TxPower = mibSet->Param.ChannelsDefaultTxPower;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DEF_TX_POWER ) == true )
{
Nvm.MacGroup2.ChannelsTxPowerDefault = verify.TxPower;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_CHANNELS_TX_POWER:
{
verify.TxPower = mibSet->Param.ChannelsTxPower;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_POWER ) == true )
{
Nvm.MacGroup1.ChannelsTxPower = verify.TxPower;
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_SYSTEM_MAX_RX_ERROR:
{
Nvm.MacGroup2.MacParams.SystemMaxRxError = Nvm.MacGroup2.MacParamsDefaults.SystemMaxRxError = mibSet->Param.SystemMaxRxError;
break;
}
case MIB_MIN_RX_SYMBOLS:
{
Nvm.MacGroup2.MacParams.MinRxSymbols = Nvm.MacGroup2.MacParamsDefaults.MinRxSymbols = mibSet->Param.MinRxSymbols;
break;
}
case MIB_ANTENNA_GAIN:
{
Nvm.MacGroup2.MacParams.AntennaGain = mibSet->Param.AntennaGain;
break;
}
case MIB_DEFAULT_ANTENNA_GAIN:
{
Nvm.MacGroup2.MacParamsDefaults.AntennaGain = mibSet->Param.DefaultAntennaGain;
break;
}
case MIB_NVM_CTXS:
{
if( mibSet->Param.Contexts != 0 )
{
status = RestoreNvmData( mibSet->Param.Contexts );
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_ABP_LORAWAN_VERSION:
{
if( mibSet->Param.AbpLrWanVersion.Fields.Minor <= 1 )
{
Nvm.MacGroup2.Version = mibSet->Param.AbpLrWanVersion;
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetLrWanVersion( mibSet->Param.AbpLrWanVersion ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
status = LORAMAC_STATUS_PARAMETER_INVALID;
}
break;
}
case MIB_IS_CERT_FPORT_ON:
{
Nvm.MacGroup2.IsCertPortOn = mibSet->Param.IsCertPortOn;
break;
}
default:
{
status = LoRaMacMibClassBSetRequestConfirm( mibSet );
break;
}
}
return status;
}
LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params )
{
ChannelAddParams_t channelAdd;
// Validate if the MAC is in a correct state
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
{
return LORAMAC_STATUS_BUSY;
}
}
channelAdd.NewChannel = &params;
channelAdd.ChannelId = id;
return RegionChannelAdd( Nvm.MacGroup2.Region, &channelAdd );
}
LoRaMacStatus_t LoRaMacChannelRemove( uint8_t id )
{
ChannelRemoveParams_t channelRemove;
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
if( ( MacCtx.MacState & LORAMAC_TX_CONFIG ) != LORAMAC_TX_CONFIG )
{
return LORAMAC_STATUS_BUSY;
}
}
channelRemove.ChannelId = id;
if( RegionChannelsRemove( Nvm.MacGroup2.Region, &channelRemove ) == false )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacMcChannelSetup( McChannelParams_t *channel )
{
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
return LORAMAC_STATUS_BUSY;
}
if( channel->GroupID >= LORAMAC_MAX_MC_CTX )
{
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
}
Nvm.MacGroup2.MulticastChannelList[channel->GroupID].ChannelParams = *channel;
if( channel->IsRemotelySetup == true )
{
const KeyIdentifier_t mcKeys[LORAMAC_MAX_MC_CTX] = { MC_KEY_0, MC_KEY_1, MC_KEY_2, MC_KEY_3 };
if( LoRaMacCryptoSetKey( mcKeys[channel->GroupID], channel->McKeys.McKeyE ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
if( LoRaMacCryptoDeriveMcSessionKeyPair( channel->GroupID, channel->Address ) != LORAMAC_CRYPTO_SUCCESS )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
else
{
const KeyIdentifier_t mcAppSKeys[LORAMAC_MAX_MC_CTX] = { MC_APP_S_KEY_0, MC_APP_S_KEY_1, MC_APP_S_KEY_2, MC_APP_S_KEY_3 };
const KeyIdentifier_t mcNwkSKeys[LORAMAC_MAX_MC_CTX] = { MC_NWK_S_KEY_0, MC_NWK_S_KEY_1, MC_NWK_S_KEY_2, MC_NWK_S_KEY_3 };
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcAppSKeys[channel->GroupID], channel->McKeys.Session.McAppSKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
if( LORAMAC_CRYPTO_SUCCESS != LoRaMacCryptoSetKey( mcNwkSKeys[channel->GroupID], channel->McKeys.Session.McNwkSKey ) )
{
return LORAMAC_STATUS_CRYPTO_ERROR;
}
}
if( channel->Class == CLASS_B )
{
// Calculate class b parameters
LoRaMacClassBSetMulticastPeriodicity( &Nvm.MacGroup2.MulticastChannelList[channel->GroupID] );
}
// Reset multicast channel downlink counter to initial value.
*Nvm.MacGroup2.MulticastChannelList[channel->GroupID].DownLinkCounter = FCNT_DOWN_INITAL_VALUE;
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacMcChannelDelete( AddressIdentifier_t groupID )
{
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
return LORAMAC_STATUS_BUSY;
}
if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
{
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
}
McChannelParams_t channel;
// Set all channel fields with 0
memset1( ( uint8_t* )&channel, 0, sizeof( McChannelParams_t ) );
Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams = channel;
return LORAMAC_STATUS_OK;
}
uint8_t LoRaMacMcChannelGetGroupId( uint32_t mcAddress )
{
for( uint8_t i = 0; i < LORAMAC_MAX_MC_CTX; i++ )
{
if( mcAddress == Nvm.MacGroup2.MulticastChannelList[i].ChannelParams.Address )
{
return i;
}
}
return 0xFF;
}
LoRaMacStatus_t LoRaMacMcChannelSetupRxParams( AddressIdentifier_t groupID, McRxParams_t *rxParams, uint8_t *status )
{
*status = 0x1C + ( groupID & 0x03 );
if( ( MacCtx.MacState & LORAMAC_TX_RUNNING ) == LORAMAC_TX_RUNNING )
{
return LORAMAC_STATUS_BUSY;
}
DeviceClass_t devClass = Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.Class;
if( ( devClass == CLASS_A ) || ( devClass > CLASS_C ) )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( ( groupID >= LORAMAC_MAX_MC_CTX ) ||
( Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.IsEnabled == false ) )
{
return LORAMAC_STATUS_MC_GROUP_UNDEFINED;
}
*status &= 0x0F; // groupID OK
VerifyParams_t verify;
// Check datarate
if( devClass == CLASS_B )
{
verify.DatarateParams.Datarate = rxParams->ClassB.Datarate;
}
else
{
verify.DatarateParams.Datarate = rxParams->ClassC.Datarate;
}
verify.DatarateParams.DownlinkDwellTime = Nvm.MacGroup2.MacParams.DownlinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_RX_DR ) == true )
{
*status &= 0xFB; // datarate OK
}
// Check frequency
if( devClass == CLASS_B )
{
verify.Frequency = rxParams->ClassB.Frequency;
}
else
{
verify.Frequency = rxParams->ClassC.Frequency;
}
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_FREQUENCY ) == true )
{
*status &= 0xF7; // frequency OK
}
if( *status == ( groupID & 0x03 ) )
{
// Apply parameters
Nvm.MacGroup2.MulticastChannelList[groupID].ChannelParams.RxParams = *rxParams;
}
return LORAMAC_STATUS_OK;
}
LoRaMacStatus_t LoRaMacMlmeRequest( MlmeReq_t* mlmeRequest )
{
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
MlmeConfirmQueue_t queueElement;
uint8_t macCmdPayload[2] = { 0x00, 0x00 };
if( mlmeRequest == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( LoRaMacIsBusy( ) == true )
{
return LORAMAC_STATUS_BUSY;
}
if( LoRaMacConfirmQueueIsFull( ) == true )
{
return LORAMAC_STATUS_BUSY;
}
if( LoRaMacConfirmQueueGetCnt( ) == 0 )
{
memset1( ( uint8_t* ) &MacCtx.MlmeConfirm, 0, sizeof( MacCtx.MlmeConfirm ) );
}
MacCtx.MlmeConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
MacCtx.MacFlags.Bits.MlmeReq = 1;
queueElement.Request = mlmeRequest->Type;
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
queueElement.RestrictCommonReadyToHandle = false;
queueElement.ReadyToHandle = false;
switch( mlmeRequest->Type )
{
case MLME_JOIN:
{
if( ( MacCtx.MacState & LORAMAC_TX_DELAYED ) == LORAMAC_TX_DELAYED )
{
return LORAMAC_STATUS_BUSY;
}
if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_OTAA )
{
ResetMacParameters( );
Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR );
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL;
status = SendReJoinReq( JOIN_REQ );
if( status != LORAMAC_STATUS_OK )
{
// Revert back the previous datarate ( mainly used for US915 like regions )
Nvm.MacGroup1.ChannelsDatarate = RegionAlternateDr( Nvm.MacGroup2.Region, mlmeRequest->Req.Join.Datarate, ALTERNATE_DR_RESTORE );
}
}
else if( mlmeRequest->Req.Join.NetworkActivation == ACTIVATION_TYPE_ABP )
{
// Restore default value for ChannelsDatarateChangedLinkAdrReq
Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq = false;
//Activate the default channels
InitDefaultsParams_t params;
params.Type = INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS;
RegionInitDefaults( Nvm.MacGroup2.Region, &params );
Nvm.MacGroup2.NetworkActivation = mlmeRequest->Req.Join.NetworkActivation;
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_OK;
queueElement.ReadyToHandle = true;
MacCtx.MacCallbacks->MacProcessNotify( );
MacCtx.MacFlags.Bits.MacDone = 1;
status = LORAMAC_STATUS_OK;
}
break;
}
case MLME_LINK_CHECK:
{
// LoRaMac will send this command piggy-pack
status = LORAMAC_STATUS_OK;
if( LoRaMacCommandsAddCmd( MOTE_MAC_LINK_CHECK_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
{
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
break;
}
case MLME_TXCW:
{
status = SetTxContinuousWave( mlmeRequest->Req.TxCw.Timeout, mlmeRequest->Req.TxCw.Frequency, mlmeRequest->Req.TxCw.Power );
break;
}
case MLME_DEVICE_TIME:
{
// LoRaMac will send this command piggy-pack
status = LORAMAC_STATUS_OK;
if( LoRaMacCommandsAddCmd( MOTE_MAC_DEVICE_TIME_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
{
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
break;
}
case MLME_PING_SLOT_INFO:
{
if( Nvm.MacGroup2.DeviceClass == CLASS_A )
{
uint8_t value = mlmeRequest->Req.PingSlotInfo.PingSlot.Value;
// LoRaMac will send this command piggy-pack
LoRaMacClassBSetPingSlotInfo( mlmeRequest->Req.PingSlotInfo.PingSlot.Fields.Periodicity );
macCmdPayload[0] = value;
status = LORAMAC_STATUS_OK;
if( LoRaMacCommandsAddCmd( MOTE_MAC_PING_SLOT_INFO_REQ, macCmdPayload, 1 ) != LORAMAC_COMMANDS_SUCCESS )
{
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
}
break;
}
case MLME_BEACON_TIMING:
{
// LoRaMac will send this command piggy-pack
status = LORAMAC_STATUS_OK;
if( LoRaMacCommandsAddCmd( MOTE_MAC_BEACON_TIMING_REQ, macCmdPayload, 0 ) != LORAMAC_COMMANDS_SUCCESS )
{
status = LORAMAC_STATUS_MAC_COMMAD_ERROR;
}
break;
}
case MLME_BEACON_ACQUISITION:
{
// Apply the request
queueElement.RestrictCommonReadyToHandle = true;
if( LoRaMacClassBIsAcquisitionInProgress( ) == false )
{
// Start class B algorithm
LoRaMacClassBSetBeaconState( BEACON_STATE_ACQUISITION );
LoRaMacClassBBeaconTimerEvent( NULL );
status = LORAMAC_STATUS_OK;
}
else
{
status = LORAMAC_STATUS_BUSY;
}
break;
}
default:
break;
}
// Fill return structure
mlmeRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
if( status != LORAMAC_STATUS_OK )
{
if( LoRaMacConfirmQueueGetCnt( ) == 0 )
{
MacCtx.NodeAckRequested = false;
MacCtx.MacFlags.Bits.MlmeReq = 0;
}
}
else
{
LoRaMacConfirmQueueAdd( &queueElement );
}
return status;
}
LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t* mcpsRequest )
{
GetPhyParams_t getPhy;
PhyParam_t phyParam;
LoRaMacStatus_t status = LORAMAC_STATUS_SERVICE_UNKNOWN;
LoRaMacHeader_t macHdr;
VerifyParams_t verify;
uint8_t fPort = 0;
void* fBuffer;
uint16_t fBufferSize;
int8_t datarate = DR_0;
bool readyToSend = false;
if( mcpsRequest == NULL )
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
if( LoRaMacIsBusy( ) == true )
{
return LORAMAC_STATUS_BUSY;
}
McpsReq_t request = *mcpsRequest;
macHdr.Value = 0;
memset1( ( uint8_t* ) &MacCtx.McpsConfirm, 0, sizeof( MacCtx.McpsConfirm ) );
MacCtx.McpsConfirm.Status = LORAMAC_EVENT_INFO_STATUS_ERROR;
// Apply confirmed downlinks, if the device has not received a valid
// downlink after a join accept.
if( ( Nvm.MacGroup2.NetworkActivation == ACTIVATION_TYPE_OTAA ) &&
( Nvm.MacGroup2.DeviceClass == CLASS_C ) &&
( Nvm.MacGroup2.DownlinkReceived == false ) &&
( request.Type == MCPS_UNCONFIRMED ) )
{
request.Type = MCPS_CONFIRMED;
}
switch( request.Type )
{
case MCPS_UNCONFIRMED:
{
readyToSend = true;
macHdr.Bits.MType = FRAME_TYPE_DATA_UNCONFIRMED_UP;
fPort = request.Req.Unconfirmed.fPort;
fBuffer = request.Req.Unconfirmed.fBuffer;
fBufferSize = request.Req.Unconfirmed.fBufferSize;
datarate = request.Req.Unconfirmed.Datarate;
break;
}
case MCPS_CONFIRMED:
{
readyToSend = true;
macHdr.Bits.MType = FRAME_TYPE_DATA_CONFIRMED_UP;
fPort = request.Req.Confirmed.fPort;
fBuffer = request.Req.Confirmed.fBuffer;
fBufferSize = request.Req.Confirmed.fBufferSize;
datarate = request.Req.Confirmed.Datarate;
break;
}
case MCPS_PROPRIETARY:
{
readyToSend = true;
macHdr.Bits.MType = FRAME_TYPE_PROPRIETARY;
fBuffer = request.Req.Proprietary.fBuffer;
fBufferSize = request.Req.Proprietary.fBufferSize;
datarate = request.Req.Proprietary.Datarate;
break;
}
default:
break;
}
// Make sure that the input datarate is compliant
// to the regional specification.
getPhy.Attribute = PHY_MIN_TX_DR;
getPhy.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
phyParam = RegionGetPhyParam( Nvm.MacGroup2.Region, &getPhy );
// Apply the minimum possible datarate.
// Some regions have limitations for the minimum datarate.
datarate = MAX( datarate, ( int8_t )phyParam.Value );
// Apply minimum datarate in this special case.
if( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation,
Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true )
{
datarate = ( int8_t )phyParam.Value;
}
if( readyToSend == true )
{
if( ( Nvm.MacGroup2.AdrCtrlOn == false ) ||
( CheckForMinimumAbpDatarate( Nvm.MacGroup2.AdrCtrlOn, Nvm.MacGroup2.NetworkActivation,
Nvm.MacGroup2.ChannelsDatarateChangedLinkAdrReq ) == true ) )
{
verify.DatarateParams.Datarate = datarate;
verify.DatarateParams.UplinkDwellTime = Nvm.MacGroup2.MacParams.UplinkDwellTime;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_TX_DR ) == true )
{
Nvm.MacGroup1.ChannelsDatarate = verify.DatarateParams.Datarate;
}
else
{
return LORAMAC_STATUS_PARAMETER_INVALID;
}
}
status = Send( &macHdr, fPort, fBuffer, fBufferSize );
if( status == LORAMAC_STATUS_OK )
{
MacCtx.McpsConfirm.McpsRequest = request.Type;
MacCtx.MacFlags.Bits.McpsReq = 1;
}
else
{
MacCtx.NodeAckRequested = false;
}
}
// Fill return structure
mcpsRequest->ReqReturn.DutyCycleWaitTime = MacCtx.DutyCycleWaitTime;
return status;
}
void LoRaMacTestSetDutyCycleOn( bool enable )
{
VerifyParams_t verify;
verify.DutyCycle = enable;
if( RegionVerify( Nvm.MacGroup2.Region, &verify, PHY_DUTY_CYCLE ) == true )
{
Nvm.MacGroup2.DutyCycleOn = enable;
}
}
LoRaMacStatus_t LoRaMacDeInitialization( void )
{
// Check the current state of the LoRaMac
if ( LoRaMacStop( ) == LORAMAC_STATUS_OK )
{
// Stop Timers
TimerStop( &MacCtx.TxDelayedTimer );
TimerStop( &MacCtx.RxWindowTimer1 );
TimerStop( &MacCtx.RxWindowTimer2 );
// Take care about class B
LoRaMacClassBHaltBeaconing( );
// Reset Mac parameters
ResetMacParameters( );
// Switch off Radio
Radio.Sleep( );
// Return success
return LORAMAC_STATUS_OK;
}
else
{
return LORAMAC_STATUS_BUSY;
}
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/lupyuen-LoRaMac-node-nuttx.git
git@gitlife.ru:oschina-mirror/lupyuen-LoRaMac-node-nuttx.git
oschina-mirror
lupyuen-LoRaMac-node-nuttx
lupyuen-LoRaMac-node-nuttx
v4.5.1