[riot-notifications] [RIOT-OS/RIOT] RFC: Implement IEEE802 MAC SAP interface (MLME/MCPS) (#11324)

José Alamos notifications at github.com
Mon Apr 1 14:48:51 CEST 2019


#### Description
While working with different MAC layers (e.g LoRaWAN, TSCH, etc) I've noticed most of them share a very convenient interface for controlling the MAC layer and sending/receiving data.
This is the Service Access Point interface defined by the IEEE (as seen in the IEEE802.15.4-2006 document)

![interface](https://507994eb-a-62cb3a1a-s-sites.googlegroups.com/site/avrxinu/project-definition/avr_sources/802_15_4-radio/fig60.png?attachauth=ANoY7cqZxOpQMI69NeT6uPBP3lljiGWtpSr6Yy21LZI4MDTQoqjxCHL2JTd9Cs2OfVKRDgH1y0q8AmmziPVUp8S6KeU83ACo115Byv48LzoyQCLbdf4DxbW9A9MRcamlofcix1xWp6X3y4GlJ6MRUpDhiNF7itpCaiKx4BDd6pK3ssxQvktCO9BvJQQn7_3GSt9tmnEYLyH306DvebrkjuPBZvygJT1Vk7pkZ9arcDDMysMRPvRyg-8Dpm2U9n7m1_oN_Q36ki50Zd5kUlDfDEhA4MYlBfDzww%3D%3D&attredirects=0)

This MAC has the following characteristics:
- It's defined by the Mac Common Part Sublayer (MCPS) and Mac (sub)-Layer Management Entity (MLME). MCPS is in charge of data operations and MLME of all kind of MAC management (join, get/set, associate, change channel, etc).
- The MAC user sees 2 Service Access Points in order to control the MAC layer operation: MCPS-SAP and MLME-SAP.
Inside the MLME component there's a Physical layer Information Base (PIB) and sometimes a MAC layer Information Base (MIB) that stores internal variables (channel, modes, etc). It's accessed via the MLME-SAP.
- Each Service Access Point has 4 primitives for `MAC user <=> MAC` communication: Request/Confirm and Indication/Response.
- Request/Confirm: The upper layer **Requests** an operation (send data, change datarate, join a network) and receives a *synchronous* or *asynchronous* **Confirm** with status and/or useful data from the request.
    - E.g joining a network might take several seconds. The Confirm will be asynchronous (on either timeout or if received an Acknowledgement from a coordinator, for instance). On the other side, changing channel or getting a value from the MAC can be done synchronously
- Indication/Response: The MAC layer can indicate the MAC user about an event (received data, lost connection, a beacon was received) using the Indication primitive. The upper layer _might_ send back a Response to that indication (although it seems to be used rarely).

The actions of Confirm and Indication are defined by the user via callbacks and/or data structures, so the same MAC layer can be used by different users (gnrc_netif, a dedicated thread, a test, etc).

PD-SAP and PLME-SAP are service access points for the transceiver, but this will be out of the scope for now.

I see some pros with this MAC:
- It's standard (see IEEE802.15.4-2006 document, the original document is not for free but there's a summary [here](http://www.cs.unibo.it/~lbedogni/files/vario/802.15.4-summary.pdf)). I've seen it at least in [Semtech LoRaMAC](https://stackforce.github.io/LoRaMac-doc/_q_u_i_c_k__s_t_a_r_t__g_u_i_d_e.html), [NXP IEEE802.15.4 stack](https://www.nxp.com/docs/en/user-guide/JN-UG-3024.pdf) and [Nordic IEEE802.15.4 API](http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.0.0%2Fgroup__mac__reset.html)
- It helps with testing. To test a MAC layer, just define some test-specific Confirm and Indication.
- We could unify most of MAC code (Semtech LoRaMAC, GNRC LoRaWAN (#11022 ), TSCH, etc).
    - There are some devices that include the MAC layer (see e.g [RN2483](https://www.microchip.com/wwwproducts/en/RN2483) or [CA-8210](https://www.cascoda.com/wp-content/uploads/2018/11/CA-8210_datasheet_0418.pdf)), a.k.a FullMAC devices. Since we already have some software defined MACs (SoftMAC), these SAP interfaces could be used for both transparently.
- The asynchronous/synchronous nature is quite convenient for connection-oriented MACs (PPP, LoRaWAN, Wifi, etc). E.g just send a Request primitive and wait for a Confirm primitive with either SUCCESS or FAIL.

See the [LoRaMAC reference](https://stackforce.github.io/LoRaMac-doc/_q_u_i_c_k__s_t_a_r_t__g_u_i_d_e.html) for a real use case of this type of MAC.

## Integration to RIOT
![int](https://gist.githubusercontent.com/jia200x/daac639c022d5b085e0019b63eadbdb0/raw/1ec16ad4e3c5f6022bc03bb155e9591bc0f1d76c/SAP.png)

- The MAC user (gnrc_netif, a test or a dedicated thread) talks directly with the MAC using SAP primitives. Since the MAC user implements Confirm and Indication, primitives can have different behaviors (e.g pass a packet to an upper layer, assertion in case of tests, etc).
- The MAC could eventually interact with the lower layer (transceiver) using the netdev interface or a PD-SAP/PLME-SAP (not described here, but part of the IEEE standard). I drew this part with dotted lines because it could be optional for us...)
- If the transceiver implements the MAC layer, the MCPS/MLME SAP interface should be defined in the driver. That way, the MAC user will always see the same interface.

## A (very) simple POC API
mac.c
```c
/* Here goes the MAC code */
/* associated to a descriptor (e.g mac->mlme_request) */
void mlme_request(sap_request_t *request, sap_confirm_t *confirm)
{
    switch(sap->type) {
        case MLME_GET:
            /* Get a MAC internal variable. This is synchronous */
            netdev->driver->get(netdev, request->param.get.netopt, request->param.get.data, request->param.get.data_len);
            confirm->status = SAP_SUCCESS;
            break;
        case MLME_SCAN:
            /* Scan is asynchronous. Set confirm status to DEFERRED in order to indicate the user the MAC will call the Confirm callback when ready */
           start_scan();
           confirm->status = SAP_DEFERRED;
           break;
    }
}
/* Called when scan finishes */
void _isr_scan_finished(void *arg)
{
    sap_mac_t *mac = arg;
    sap_confirm_t confirm;
    /* Populate confirm with scan data */
    confirm.data = get_scan_data();
    if(scan_status == 0) {
        confirm.status = SAP_SUCCESS;
    }
    else {
        confirm.status = SAP_FAIL;
    }
        mac->mlme_confirm(&confirm);
}

/* associated to a descriptor (e.g mac->mcps_request) */
void mcps_request(sap_request_t *request, sap_confirm_t *confirm)
{
    switch(sap->type) {
        case MCPS_DATA:
            if(mac_is_disconnected()) {
                /* MAC is disconnected from medium. Confirm with error */
                confirm->status = SAP_FAIL;
                return;
            }
            netdev->driver->send(netdev, request->param.data_req.iolist, ...);
            /* When an ACK is received, the MAC layer will call the Confirm callback */
            confirm->status = SAP_DEFERRED;
            break;
    }
}

/* Called by the radio when received an ACK */
_isr_received_ack(void *arg)
{
    sap_mac_t *mac = arg;
    sap_confirm_t confirm;
    confirm.type = MCPS_DATA;
    confirm.status = SAP_SUCCESS;
     mac->mcps_confirm(&confirm);
}

/* Called when received a packet */
_isr_packet_received(void *arg)
{
    sap_mac_t *mac = arg;
    sap_indication_t indication;
    /* ... */
    indication.msdu = get_packet();
    indication.rssi = get_rssi();
    indication.channel = get_rx_channel();

    /* Indicate the upper layer we just received a packet */
    mac->mcps_indication(&indication)
}
```

main.c
```c
/* Here goes the MAC user code. Note there can be multiple implementations of
confirm/indication primitives depending on the expected behavior, and these functions are
included in the mac descriptor (mac->mlme_confirm, mac->mcps_confirm and mac->mcps_indication */

int main(void)
{
    /* Request the mac layer to scan */
    sap_request_t request;
    request.type = MLME_SCAN;

    /* struct to hold confirm information */
    sap_confirm_t confirm;
    mac->mlme_request(&request, &confirm);
    /* confirm->status will be SAP_DEFERRED, since the result of the scan command is asynchronous */
    
    request.type = MLME_GET;
    request.param.get.netopt = NETOPT_FOO;
    /* ... */
    mac->mlme_request(&request, &confirm);
    /* confirm->status will be SAP_SUCCESS, since getting a variable is synchronous.
       confirm->param.get.data contains the value */

    request.type = MCPS_DATA;
    request.param.data_req.channel = my_channel;
    request.param.data_req.iolist = my_iolist;
    request.param.data_req.ack_requested = true;
    mac->mcps_request(&request, &confirm);
    /* confirm->status will be SAP_DEFERRED if everything went OK */
}

/* main.c mlme_confirm implementation */
void mlme_confirm(sap_confirm_t *confirm)
{
    switch(confirm->type) {
        case MLME_SCAN:
            if(confirm->status == SAP_SUCCESS) {
                puts("Scan went successfully!");
            }
            break;
    }
}
/* main.c mcps_confirm implementation */
void mcps_confirm(sap_confirm_t *confirm)
{
    switch(confirm->type) {
        case MCPS_DATA:
            if(confirm->status == SAP_SUCCESS) {
                puts("ACK was received successfully. Transmission was OK");
            }
            break;
    }
}

/* main.c mcps_indication implementation */
void mcps_indication(sap_indication_t *indication)
{
    switch(confirm->type) {
        case MCPS_DATA:
            puts("Received packet with payload:");
            print_packet(indication.msdu);
            break;
    }
}
```

I already implemented this mechanism in #11022, but it's not that decoupled to GNRC LoRaWAN.


Does this idea make sense? All comments would be very helpful


<!-- Thanks for contributing! -->


-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/RIOT-OS/RIOT/issues/11324
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.riot-os.org/pipermail/notifications/attachments/20190401/ef98e902/attachment-0001.html>


More information about the notifications mailing list