Skip to content

Commit b634ca4

Browse files
author
Hasnain Virk
committed
Architecture rework, bug fixing & missing features
MAC layer is now a class rather than being a blob. In addition to that Mac commands are now being handled in a seperate subsystem (a class of its own). In future we will do the same with othe sublayers of MAC like MLME, MCPS etc. The drive behind this exercise is to make MAC and supporting layers into an object oriented system. Major bug fixes include: - last join time inclusion in band parameters - disabling rx2 window if we missed the slot already - MLME uplink schdule hook - nbRep according to spec - maintaining datarate after successful joining - suppressing MLME requests if MAC is in TX_DELAYED state - Uplink dwell time verification Some missing features are implemented. Details are as follows. Support for LinkCheckRequet: An application API is added, add_link_check_request() to delegate a request for Link Check Request MAC command. * Application provides a callback function that needs to be called on reception of link check response. * Mac command is piggybacked with data frames. This API makes the sticky MAC command stick with the application payloads until/unless the application un-sticks the said mac command using remove_link_check_request() API. Handling fPending bit: If in the Downlink, we get the fPending bit set in fctrl octet, we attempt to send an empty message back to Network Server to open additional Receive windows. This operation is independent of the application. An RX_DONE event is queued bedore generating the said empty message. Specification does not mention what can be the type of that empty message. We have decided it to be of CONFIRMED type as it gives us an added benefit of retries if the corresponding RX slots are missed. Radio event callbacks as Mbed callbacks: radio_events_t structure has been carrying C-style callbacks which was inherited from the legacy code. These callbacks has now been changed to Mbed Callbacks that makes sure that we can be object oriented from now on.
1 parent 1eedadd commit b634ca4

25 files changed

+2095
-1506
lines changed

features/lorawan/LoRaWANInterface.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ inline LoRaWANStack& stk_obj()
2828
return LoRaWANStack::get_lorawan_stack();
2929
}
3030

31-
LoRaWANInterface::LoRaWANInterface(LoRaRadio& radio)
31+
LoRaWANInterface::LoRaWANInterface(LoRaRadio& radio) : _link_check_requested(false)
3232
{
3333
// Pass mac_handlers to radio to the radio driver after
3434
// binding radio driver to PHY layer
@@ -117,6 +117,17 @@ lora_mac_status_t LoRaWANInterface::disconnect()
117117
return LORA_MAC_STATUS_OK;
118118
}
119119

120+
lora_mac_status_t LoRaWANInterface::add_link_check_request()
121+
{
122+
_link_check_requested = true;
123+
return stk_obj().set_link_check_request();
124+
}
125+
126+
void LoRaWANInterface::remove_link_check_request()
127+
{
128+
_link_check_requested = false;
129+
}
130+
120131
lora_mac_status_t LoRaWANInterface::set_datarate(uint8_t data_rate)
121132
{
122133
return stk_obj().set_channel_data_rate(data_rate);
@@ -160,6 +171,12 @@ lora_mac_status_t LoRaWANInterface::remove_channel_plan()
160171
int16_t LoRaWANInterface::send(uint8_t port, const uint8_t* data,
161172
uint16_t length, int flags)
162173
{
174+
if (_link_check_requested) {
175+
// add a link check request with normal data, until the application
176+
// explicitly removes it.
177+
add_link_check_request();
178+
}
179+
163180
if (data) {
164181
return stk_obj().handle_tx(port, data, length, flags);
165182
} else {

features/lorawan/LoRaWANInterface.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,44 @@ class LoRaWANInterface: public LoRaWANBase {
130130
*/
131131
virtual lora_mac_status_t disconnect();
132132

133+
/** Validate the connectivity with the network.
134+
*
135+
* Application may use this API to submit a request to the stack for
136+
* validation of its connectivity to a Network Server. Under the hood, this
137+
* API schedules a Link Check Request command (LinkCheckReq) for the network
138+
* server and once the response, i.e., LinkCheckAns MAC command is received
139+
* from the Network Server, user provided method is called.
140+
*
141+
* One way to use this API may be the validation of connectivity after a long
142+
* deep sleep. Mbed LoRaWANStack piggy-backs the MAC commands with data
143+
* frame payload so the application needs to try sending something and the Network
144+
* Server may respond during the RX slots.
145+
*
146+
* This API is usable only when the 'link_check_resp' callback is set by
147+
* the application. See add_lora_app_callbacks API. If the above mentioned
148+
* callback is not set, a LORA_MAC_STATUS_PARAMETER_INVALID error is thrown.
149+
*
150+
* First parameter to callback function is the demodulation margin and
151+
* the second parameter is the number of gateways that successfully received
152+
* the last request.
153+
*
154+
* A 'Link Check Request' MAC command remains set for every subsequent
155+
* transmission, until/unless application explicitly turns it off using
156+
* remove_link_check_request() API.
157+
*
158+
* @return LORA_MAC_STATUS_OK on successfully queuing a request, or
159+
* a negative error code on failure.
160+
*
161+
*/
162+
virtual lora_mac_status_t add_link_check_request();
163+
164+
/** Removes link check request sticky MAC command.
165+
*
166+
* Any already queued request may still get entertained. However, no new
167+
* requests will be made.
168+
*/
169+
virtual void remove_link_check_request();
170+
133171
/** Sets up a particular data rate
134172
*
135173
* `set_datarate()` first verifies whether the data rate given is valid or not.
@@ -361,7 +399,7 @@ class LoRaWANInterface: public LoRaWANBase {
361399
* int main()
362400
* {
363401
* lorawan.initialize(&queue);
364-
* cbs.lorawan_events = mbed::callback(my_event_handler);
402+
* cbs.events = mbed::callback(my_event_handler);
365403
* lorawan.add_app_callbacks(&cbs);
366404
* lorawan.connect();
367405
* }
@@ -387,6 +425,9 @@ class LoRaWANInterface: public LoRaWANBase {
387425
* callbacks.
388426
*/
389427
virtual lora_mac_status_t add_app_callbacks(lorawan_app_callbacks_t *callbacks);
428+
429+
private:
430+
bool _link_check_requested;
390431
};
391432

392433
#endif /* LORAWANINTERFACE_H_ */

features/lorawan/LoRaWANStack.cpp

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ lora_mac_status_t LoRaWANStack::set_application_port(uint8_t port)
129129
****************************************************************************/
130130
LoRaWANStack::LoRaWANStack()
131131
: _device_current_state(DEVICE_STATE_NOT_INITIALIZED), _mac_handlers(NULL),
132-
_num_retry(1), _queue(NULL), _duty_cycle_on(LORAWAN_DUTYCYCLE_ON)
132+
_num_retry(1), _queue(NULL), _duty_cycle_on(LORAWAN_DUTYCYCLE_ON)
133133
{
134134
#ifdef MBED_CONF_LORA_APP_PORT
135135
// is_port_valid() is not virtual, so we can call it in constructor
@@ -168,7 +168,7 @@ LoRaWANStack& LoRaWANStack::get_lorawan_stack()
168168
radio_events_t *LoRaWANStack::bind_radio_driver(LoRaRadio& radio)
169169
{
170170
// Store pointer to callback routines inside MAC layer (non-IRQ safe)
171-
_mac_handlers = GetPhyEventHandlers();
171+
_mac_handlers = _loramac.GetPhyEventHandlers();
172172
// passes the reference to radio driver down to PHY layer
173173
lora_phy.get()->set_radio_instance(radio);
174174
return _mac_handlers;
@@ -204,7 +204,8 @@ lora_mac_status_t LoRaWANStack::initialize_mac_layer(EventQueue *queue)
204204
LoRaMacPrimitives.MacMcpsConfirm = callback(this, &LoRaWANStack::mcps_confirm);
205205
LoRaMacPrimitives.MacMcpsIndication = callback(this, &LoRaWANStack::mcps_indication);
206206
LoRaMacPrimitives.MacMlmeConfirm = callback(this, &LoRaWANStack::mlme_confirm);
207-
LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, lora_phy.get(), queue);
207+
LoRaMacPrimitives.MacMlmeIndication = callback(this, &LoRaWANStack::mlme_indication);
208+
_loramac.LoRaMacInitialization(&LoRaMacPrimitives, &LoRaMacCallbacks, lora_phy.get(), queue);
208209

209210
mib_req.type = LORA_MIB_ADR;
210211
mib_req.param.adr_enable = LORAWAN_ADR_ON;
@@ -315,7 +316,7 @@ lora_mac_status_t LoRaWANStack::send_compliance_test_frame_to_mac()
315316
uint16_t LoRaWANStack::check_possible_tx_size(uint16_t size)
316317
{
317318
LoRaMacTxInfo_t txInfo;
318-
if (LoRaMacQueryTxPossible(size, &txInfo) == LORAMAC_STATUS_LENGTH_ERROR) {
319+
if (_loramac.LoRaMacQueryTxPossible(size, &txInfo) == LORAMAC_STATUS_LENGTH_ERROR) {
319320
// Cannot transmit this much. Return how much data can be sent
320321
// at the moment
321322
return txInfo.MaxPossiblePayload;
@@ -461,9 +462,30 @@ void LoRaWANStack::mlme_confirm(MlmeConfirm_t *mlme_confirm)
461462
mlme_confirm_handler(&lora_mlme_confirm);
462463
}
463464

464-
void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs)
465+
/*!
466+
* \brief MLME-Indication event function
467+
*
468+
* \param [IN] mlmeIndication - Pointer to the indication structure.
469+
*/
470+
void LoRaWANStack::mlme_indication( MlmeIndication_t *mlmeIndication )
465471
{
472+
switch( mlmeIndication->MlmeIndication )
473+
{
474+
case MLME_SCHEDULE_UPLINK:
475+
{// The MAC signals that we shall provide an uplink as soon as possible
476+
// TODO: Sending implementation missing and will be implemented using
477+
// another task.
478+
//OnTxNextPacketTimerEvent( );
479+
break;
480+
}
481+
default:
482+
break;
483+
}
484+
}
485+
466486

487+
void LoRaWANStack::set_lora_callbacks(lorawan_app_callbacks_t *cbs)
488+
{
467489
if (cbs) {
468490
if (cbs->events) {
469491
_callbacks.events = cbs->events;
@@ -508,7 +530,7 @@ lora_mac_status_t LoRaWANStack::add_channels(const lora_channelplan_t &channel_p
508530
mac_layer_ch_params.Frequency = channel_plan.channels[i].ch_param.frequency;
509531
mac_layer_ch_params.Rx1Frequency =channel_plan.channels[i].ch_param.rx1_frequency;
510532

511-
status = LoRaMacChannelAdd(channel_plan.channels[i].id, mac_layer_ch_params);
533+
status = _loramac.LoRaMacChannelAdd(channel_plan.channels[i].id, mac_layer_ch_params);
512534

513535
if (status != LORAMAC_STATUS_OK) {
514536
return error_type_converter(status);
@@ -560,7 +582,7 @@ lora_mac_status_t LoRaWANStack::drop_channel_list()
560582
continue;
561583
}
562584

563-
status = error_type_converter(LoRaMacChannelRemove(i));
585+
status = error_type_converter(_loramac.LoRaMacChannelRemove(i));
564586

565587
if (status != LORA_MAC_STATUS_OK) {
566588
return status;
@@ -609,7 +631,7 @@ lora_mac_status_t LoRaWANStack::remove_a_channel(uint8_t channel_id)
609631
return LORA_MAC_STATUS_PARAMETER_INVALID;
610632
}
611633

612-
return error_type_converter(LoRaMacChannelRemove(channel_id));
634+
return error_type_converter(_loramac.LoRaMacChannelRemove(channel_id));
613635
}
614636

615637
lora_mac_status_t LoRaWANStack::get_enabled_channels(lora_channelplan_t& channel_plan)
@@ -872,7 +894,9 @@ int16_t LoRaWANStack::handle_tx(uint8_t port, const uint8_t* data,
872894
_tx_msg.f_buffer_size = length;
873895
_tx_msg.pending_size = 0;
874896
// copy user buffer upto the max_possible_size
875-
memcpy(_tx_msg.f_buffer, data, length);
897+
if (data && length > 0) {
898+
memcpy(_tx_msg.f_buffer, data, length);
899+
}
876900
}
877901

878902
// Handles all unconfirmed messages, including proprietary and multicast
@@ -1018,7 +1042,7 @@ lora_mac_status_t LoRaWANStack::mlme_request_handler(lora_mac_mlme_req_t *mlme_r
10181042
break;
10191043
}
10201044

1021-
return error_type_converter(LoRaMacMlmeRequest(&request));
1045+
return error_type_converter(_loramac.LoRaMacMlmeRequest(&request));
10221046
}
10231047

10241048
/** MLME-Confirm event function
@@ -1057,8 +1081,15 @@ void LoRaWANStack::mlme_confirm_handler(lora_mac_mlme_confirm_t *mlme_confirm)
10571081
_compliance_test.link_check = true;
10581082
_compliance_test.demod_margin = mlme_confirm->demod_margin;
10591083
_compliance_test.nb_gateways = mlme_confirm->nb_gateways;
1060-
}
1084+
} else
10611085
#endif
1086+
{
1087+
// normal operation as oppose to compliance testing
1088+
if (_callbacks.link_check_resp) {
1089+
_queue->call(_callbacks.link_check_resp, mlme_confirm->demod_margin,
1090+
mlme_confirm->nb_gateways);
1091+
}
1092+
}
10621093
}
10631094
break;
10641095
default:
@@ -1101,7 +1132,7 @@ lora_mac_status_t LoRaWANStack::mcps_request_handler(lora_mac_mcps_req_t *mcps_r
11011132
break;
11021133
}
11031134

1104-
return error_type_converter(LoRaMacMcpsRequest(&request));
1135+
return error_type_converter(_loramac.LoRaMacMcpsRequest(&request));
11051136
}
11061137

11071138
/** MCPS-Confirm event function
@@ -1253,6 +1284,14 @@ void LoRaWANStack::mcps_indication_handler(lora_mac_mcps_indication_t *mcps_indi
12531284
if (_callbacks.events) {
12541285
_queue->call(_callbacks.events, RX_DONE);
12551286
}
1287+
1288+
// If fPending bit is set we try to generate an empty packet
1289+
// with CONFIRMED flag set. We always set a CONFIRMED flag so
1290+
// that we could retry a certain number of times if the uplink
1291+
// failed for some reason
1292+
if (mcps_indication->frame_pending) {
1293+
handle_tx(mcps_indication->port, NULL, 0, MSG_CONFIRMED_FLAG);
1294+
}
12561295
} else {
12571296
// Invalid port, ports 0, 224 and 225-255 are reserved.
12581297
}
@@ -1292,10 +1331,10 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi
12921331
mib_set_request(&mib_req);
12931332

12941333
#if MBED_CONF_LORA_PHY == 0
1295-
LoRaMacTestSetDutyCycleOn(false);
1334+
_loramac.LoRaMacTestSetDutyCycleOn(false);
12961335
#endif
12971336
//5000ms
1298-
LoRaMacSetTxTimer(5000);
1337+
_loramac.LoRaMacSetTxTimer(5000);
12991338
set_device_state(DEVICE_STATE_COMPLIANCE_TEST);
13001339
tr_debug("Compliance test activated.");
13011340
}
@@ -1314,11 +1353,11 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi
13141353
mib_req.param.adr_enable = LORAWAN_ADR_ON;
13151354
mib_set_request(&mib_req);
13161355
#if MBED_CONF_LORA_PHY == 0
1317-
LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON);
1356+
_loramac.LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON);
13181357
#endif
13191358
// Go to idle state after compliance test mode.
13201359
tr_debug("Compliance test disabled.");
1321-
LoRaMacStopTxTimer();
1360+
_loramac.LoRaMacStopTxTimer();
13221361

13231362
// Clear any compliance test message stuff before going back to normal operation.
13241363
memset(&_tx_msg, 0, sizeof(_tx_msg));
@@ -1366,7 +1405,7 @@ void LoRaWANStack::compliance_test_handler(lora_mac_mcps_indication_t *mcps_indi
13661405
mib_request.param.adr_enable = LORAWAN_ADR_ON;
13671406
mib_set_request(&mib_request);
13681407
#if MBED_CONF_LORA_PHY == 0
1369-
LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON);
1408+
_loramac.LoRaMacTestSetDutyCycleOn(LORAWAN_DUTYCYCLE_ON);
13701409
#endif
13711410
mlme_request.type = LORA_MLME_JOIN;
13721411
mlme_request.req.join.dev_eui = _lw_session.connection.connection_u.otaa.dev_eui;
@@ -1569,7 +1608,7 @@ lora_mac_status_t LoRaWANStack::mib_set_request(lora_mac_mib_request_confirm_t *
15691608
/*
15701609
* Set MIB data to LoRa stack
15711610
*/
1572-
status = error_type_converter(LoRaMacMibSetRequestConfirm(&stack_mib_set));
1611+
status = error_type_converter(_loramac.LoRaMacMibSetRequestConfirm(&stack_mib_set));
15731612
/*
15741613
* Release memory if reserved by multicast list
15751614
*/
@@ -1598,7 +1637,7 @@ lora_mac_status_t LoRaWANStack::mib_get_request(lora_mac_mib_request_confirm_t *
15981637

15991638
// Interprets from lora_mac_mib_t to Mib_t
16001639
stack_mib_get.Type = interpret_mib_req_confirm_type(mib_get_params->type);
1601-
mac_status = error_type_converter(LoRaMacMibGetRequestConfirm(&stack_mib_get));
1640+
mac_status = error_type_converter(_loramac.LoRaMacMibGetRequestConfirm(&stack_mib_get));
16021641

16031642
if (LORA_MAC_STATUS_OK != mac_status) {
16041643
return LORA_MAC_STATUS_SERVICE_UNKNOWN;
@@ -1782,6 +1821,19 @@ lora_mac_status_t LoRaWANStack::error_type_converter(LoRaMacStatus_t type)
17821821
}
17831822
}
17841823

1824+
lora_mac_status_t LoRaWANStack::set_link_check_request()
1825+
{
1826+
if (!_callbacks.link_check_resp) {
1827+
tr_error("Must assign a callback function for link check request. ");
1828+
return LORA_MAC_STATUS_PARAMETER_INVALID;
1829+
}
1830+
1831+
lora_mac_mlme_req_t mlme_req;
1832+
1833+
mlme_req.type = LORA_MLME_LINK_CHECK;
1834+
return mlme_request_handler(&mlme_req);
1835+
}
1836+
17851837
void LoRaWANStack::shutdown()
17861838
{
17871839
set_device_state(DEVICE_STATE_SHUTDOWN);
@@ -1944,7 +1996,7 @@ lora_mac_status_t LoRaWANStack::lora_state_machine()
19441996
tr_debug("Device is in compliance test mode.");
19451997

19461998
//5000ms
1947-
LoRaMacSetTxTimer(5000);
1999+
_loramac.LoRaMacSetTxTimer(5000);
19482000
if (_compliance_test.running == true) {
19492001
send_compliance_test_frame_to_mac();
19502002
}

0 commit comments

Comments
 (0)