Newer
Older
/****************************************************************************
* wireless/ieee802154/mac802154.c
*
* Copyright (C) 2016 Sebastien Lorquet. All rights reserved.
Anthony Merlino
committed
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
Anthony Merlino
committed
* Copyright (C) 2017 Verge Inc. All rights reserved.
Anthony Merlino
committed
*
* Author: Sebastien Lorquet <sebastien@lorquet.fr>
Anthony Merlino
committed
* Author: Gregory Nutt <gnutt@nuttx.org>
Anthony Merlino
committed
* Author: Anthony Merlino <anthony@vergeaero.com>
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <nuttx/kmalloc.h>
#include <nuttx/wqueue.h>
Gregory Nutt
committed
#include <nuttx/semaphore.h>
#include <nuttx/mm/iob.h>
Anthony Merlino
committed
#include "mac802154.h"
Anthony Merlino
committed
#include "mac802154_notif.h"
#include "mac802154_internal.h"
#include "mac802154_assoc.h"
Anthony Merlino
committed
#include "mac802154_scan.h"
Anthony Merlino
committed
#include "mac802154_data.h"
#include "mac802154_poll.h"
Anthony Merlino
committed
#include <nuttx/wireless/ieee802154/ieee802154_mac.h>
Anthony Merlino
committed
#include <nuttx/wireless/ieee802154/ieee802154_radio.h>
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
Anthony Merlino
committed
/* Data structure pools and allocation helpers */
Anthony Merlino
committed
static void mac802154_resetqueues(FAR struct ieee802154_privmac_s *priv);
Anthony Merlino
committed
/* IEEE 802.15.4 PHY Interface OPs */
Anthony Merlino
committed
static int mac802154_radiopoll(FAR const struct ieee802154_radiocb_s *radiocb,
bool gts, FAR struct ieee802154_txdesc_s **tx_desc);
static void mac802154_txdone(FAR const struct ieee802154_radiocb_s *radiocb,
Anthony Merlino
committed
FAR struct ieee802154_txdesc_s *tx_desc);
Anthony Merlino
committed
static void mac802154_txdone_worker(FAR void *arg);
static void mac802154_rxframe(FAR const struct ieee802154_radiocb_s *radiocb,
FAR struct ieee802154_data_ind_s *ind);
Anthony Merlino
committed
static void mac802154_rxframe_worker(FAR void *arg);
Anthony Merlino
committed
static void mac802154_sfevent(FAR const struct ieee802154_radiocb_s *radiocb,
enum ieee802154_sfevent_e sfevent);
Anthony Merlino
committed
static void mac802154_purge_worker(FAR void *arg);
Anthony Merlino
committed
static void mac802154_rxdatareq(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_data_ind_s *ind);
static void mac802154_rxdataframe(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_data_ind_s *ind);
static void mac802154_rxbeaconframe(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_data_ind_s *ind);
Anthony Merlino
committed
/****************************************************************************
Anthony Merlino
committed
* Private Functions
****************************************************************************/
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_resetqueues
Anthony Merlino
committed
* Initializes the various queues used in the MAC layer. Called on creation
* of MAC.
*
****************************************************************************/
Anthony Merlino
committed
static void mac802154_resetqueues(FAR struct ieee802154_privmac_s *priv)
Anthony Merlino
committed
int i;
Anthony Merlino
committed
sq_init(&priv->txdone_queue);
sq_init(&priv->csma_queue);
Anthony Merlino
committed
sq_init(&priv->gts_queue);
Anthony Merlino
committed
sq_init(&priv->indirect_queue);
sq_init(&priv->dataind_queue);
Anthony Merlino
committed
/* Initialize the tx descriptor allocation pool */
Anthony Merlino
committed
sq_init(&priv->txdesc_queue);
Anthony Merlino
committed
for (i = 0; i < CONFIG_MAC802154_NTXDESC; i++)
Anthony Merlino
committed
sq_addlast((FAR sq_entry_t *)&priv->txdesc_pool[i], &priv->txdesc_queue);
nxsem_init(&priv->txdesc_sem, 0, CONFIG_MAC802154_NTXDESC);
Anthony Merlino
committed
/* Initialize the notifcation allocation pool */
Anthony Merlino
committed
mac802154_notifpool_init(priv);
Anthony Merlino
committed
/****************************************************************************
* Name: mac802154_txdesc_pool
*
* Description:
* This function allocates a tx descriptor and the dependent notification (data
* confirmation) from the free list. The notification and tx descriptor will
* be freed seperately, both by the MAC layer either directly, or through
* mac802154_notif_free in the case of the notification.
*
* Assumptions:
* priv MAC struct is locked when calling.
*
* Notes:
* If any of the semaphore waits inside this function get interrupted, the
* function will release the MAC layer. If this function returns -EINTR, the
* calling code should NOT release the MAC semaphore.
*
****************************************************************************/
Anthony Merlino
committed
int mac802154_txdesc_alloc(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_txdesc_s **txdesc,
bool allow_interrupt)
Anthony Merlino
committed
{
int ret;
Anthony Merlino
committed
FAR struct ieee802154_notif_s *notif;
Anthony Merlino
committed
/* Try and take a count from the semaphore. If this succeeds, we have
* "reserved" the structure, but still need to unlink it from the free list.
* The MAC is already locked, so there shouldn't be any other conflicting calls
*/
Gregory Nutt
committed
ret = nxsem_trywait(&priv->txdesc_sem);
Anthony Merlino
committed
if (ret == OK)
{
Anthony Merlino
committed
*txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->txdesc_queue);
}
else
{
/* Unlock MAC so that other work can be done to free a notification */
mac802154_unlock(priv)
Anthony Merlino
committed
/* Take a count from the tx desc semaphore, waiting if necessary. We
* only return from here with an error if we are allowing interruptions
* and we received a signal */
ret = mac802154_takesem(&priv->txdesc_sem, allow_interrupt);
if (ret < 0)
{
/* MAC is already released */
wlwarn("WARNING: mac802154_takesem failed: %d\n", ret);
Anthony Merlino
committed
return -EINTR;
}
/* If we've taken a count from the semaphore, we have "reserved" the struct
* but now we need to pop it off of the free list. We need to re-lock the
* MAC in order to ensure this happens correctly.
ret = mac802154_lock(priv, allow_interrupt);
Anthony Merlino
committed
if (ret < 0)
{
wlwarn("WARNING: mac802154_takesem failed: %d\n", ret);
Anthony Merlino
committed
mac802154_givesem(&priv->txdesc_sem);
return -EINTR;
}
/* We can now safely unlink the next free structure from the free list */
Anthony Merlino
committed
*txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->txdesc_queue);
}
Anthony Merlino
committed
/* We have now successfully allocated the tx descriptor. Now we need to allocate
* the notification for the data confirmation that gets passed along with the
* tx descriptor. These are allocated together, but not freed together.
*/
ret = mac802154_notif_alloc(priv, ¬if, allow_interrupt);
if (ret < 0)
{
/* The mac802154_notif_alloc function follows the same rules as this
* function. If it returns -EINTR, the MAC layer is already released
*/
Anthony Merlino
committed
/* We need to free the txdesc */
mac802154_txdesc_free(priv, *txdesc);
return -EINTR;
}
(*txdesc)->purgetime = 0;
Anthony Merlino
committed
(*txdesc)->retrycount = priv->maxretries;
Anthony Merlino
committed
Anthony Merlino
committed
(*txdesc)->conf = ¬if->u.dataconf;
return OK;
}
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_createdatareq
*
* Description:
* Internal function used by various parts of the MAC layer. This function
* allocates an IOB, populates the frame according to input args, and links
* the IOB into the provided tx descriptor.
*
* Assumptions:
* Called with the MAC locked
*
****************************************************************************/
Anthony Merlino
committed
void mac802154_createdatareq(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_addr_s *coordaddr,
enum ieee802154_addrmode_e srcmode,
FAR struct ieee802154_txdesc_s *txdesc)
{
FAR struct iob_s *iob;
/* The only node allowed to use a source address of none is the PAN Coordinator.
* PAN coordinators should not be sending data request commans.
*/
DEBUGASSERT(srcmode != IEEE802154_ADDRMODE_NONE);
/* Allocate an IOB to put the frame in */
iob = iob_alloc(false);
DEBUGASSERT(iob != NULL);
iob->io_flink = NULL;
iob->io_len = 0;
iob->io_offset = 0;
iob->io_pktlen = 0;
/* Set the frame control fields */
iob->io_data[0] = 0;
iob->io_data[1] = 0;
IEEE802154_SETACKREQ(iob->io_data, 0);
IEEE802154_SETFTYPE(iob->io_data, 0, IEEE802154_FRAME_COMMAND);
IEEE802154_SETDADDRMODE(iob->io_data, 0, coordaddr->mode);
IEEE802154_SETSADDRMODE(iob->io_data, 0, srcmode);
iob->io_len = 2;
/* Each time a data or a MAC command frame is generated, the MAC sublayer
* shall copy the value of macDSN into the Sequence Number field of the
* MHR of the outgoing frame and then increment it by one. [1] pg. 40.
*/
iob->io_data[iob->io_len++] = priv->dsn++;
/* If the destination address is present, copy the PAN ID and one of the
* addresses, depending on mode, into the MHR.
*/
if (coordaddr->mode != IEEE802154_ADDRMODE_NONE)
{
Anthony Merlino
committed
mac802154_putpanid(iob, coordaddr->panid);
if (coordaddr->mode == IEEE802154_ADDRMODE_SHORT)
{
Anthony Merlino
committed
mac802154_putsaddr(iob, coordaddr->saddr);
}
else if (coordaddr->mode == IEEE802154_ADDRMODE_EXTENDED)
{
Anthony Merlino
committed
mac802154_puteaddr(iob, coordaddr->eaddr);
}
}
/* If the Destination Addressing Mode field is set to indicate that
* destination addressing information is not present, the PAN ID Compression
* field shall be set to zero and the source PAN identifier shall contain the
* value of macPANId. Otherwise, the PAN ID Compression field shall be set to
* one. In this case and in accordance with the PAN ID Compression field, the
* Destination PAN Identifier field shall contain the value of macPANId, while
* the Source PAN Identifier field shall be omitted. [1] pg. 72
*/
if (coordaddr->mode != IEEE802154_ADDRMODE_NONE &&
Anthony Merlino
committed
IEEE802154_PANIDCMP(coordaddr->panid, priv->addr.panid))
{
IEEE802154_SETPANIDCOMP(iob->io_data, 0);
}
else
{
Anthony Merlino
committed
mac802154_putpanid(iob, priv->addr.panid);
}
if (srcmode == IEEE802154_ADDRMODE_SHORT)
{
Anthony Merlino
committed
mac802154_putsaddr(iob, priv->addr.saddr);
}
else if (srcmode == IEEE802154_ADDRMODE_EXTENDED)
{
Anthony Merlino
committed
mac802154_puteaddr(iob, priv->addr.eaddr);
}
/* Copy in the Command Frame Identifier */
iob->io_data[iob->io_len++] = IEEE802154_CMD_DATA_REQ;
/* Copy the IOB reference to the descriptor */
txdesc->frame = iob;
txdesc->frametype = IEEE802154_FRAME_COMMAND;
/* Save a copy of the destination addressing information into the tx descriptor.
* We only do this for commands to help with handling their progession.
*/
memcpy(&txdesc->destaddr, &coordaddr, sizeof(struct ieee802154_addr_s));
/* Save a reference of the tx descriptor */
priv->cmd_desc = txdesc;
}
Anthony Merlino
committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/****************************************************************************
* Name: mac802154_updatebeacon
*
* Description:
* This function is called in the following scenarios:
* - The MAC receives a START.request primitive
* - Upon receiving the IEEE802154_SFEVENT_ENDOFACTIVE event from the
* radio layer, the MAC checks the bf_update flag and if set calls this
* function. The bf_update flag is set when various attributes that
* effect the beacon are updated.
*
* Internal function used by various parts of the MAC layer. This function
* uses the various MAC attributes to update the beacon frame. It loads the
* inactive beacon frame structure and then notifies the radio layer of the
* new frame. the provided tx descriptor in the indirect list and manages the
* scheduling for purging the transaction if it does not get extracted in
* time.
*
* Assumptions:
* Called with the MAC locked
*
****************************************************************************/
void mac802154_updatebeacon(FAR struct ieee802154_privmac_s *priv)
{
FAR struct ieee802154_txdesc_s *txdesc;
uint8_t pendaddrspec_ind;
uint8_t pendeaddr = 0;
uint8_t pendsaddr = 0;
/* Switch the buffer */
priv->bf_ind = !priv->bf_ind;
/* Get a local reference to the beacon frame */
FAR struct ieee802154_beaconframe_s *beacon = &priv->beaconframe[priv->bf_ind];
/* Clear the frame control fields */
Gregory Nutt
committed
beacon->bf_data[0] = 0;
Anthony Merlino
committed
beacon->bf_data[1] = 0;
beacon->bf_len = 2;
IEEE802154_SETFTYPE(beacon->bf_data, 0, IEEE802154_FRAME_BEACON);
/* Check if there is a broadcast message pending, if there is, we must set
* the frame pending bit to 1.
*/
/* TODO: handle broadcast frame */
Gregory Nutt
committed
Anthony Merlino
committed
DEBUGASSERT(priv->addr.mode != IEEE802154_ADDRMODE_NONE);
IEEE802154_SETDADDRMODE(beacon->bf_data, 0, IEEE802154_ADDRMODE_NONE);
IEEE802154_SETSADDRMODE(beacon->bf_data, 0, priv->addr.mode);
IEEE802154_SETVERSION(beacon->bf_data, 0, 1);
/* The beacon sequence number has to be taken care of by the radio layer, since
* we only want to update the whole frame when more changes than just the bsn.
*/
Anthony Merlino
committed
beacon->bf_len++;
Anthony Merlino
committed
IEEE802154_PANIDCOPY(&beacon->bf_data[beacon->bf_len], priv->addr.panid);
beacon->bf_len += IEEE802154_PANIDSIZE;
if (priv->addr.mode == IEEE802154_ADDRMODE_SHORT)
{
IEEE802154_SADDRCOPY(&beacon->bf_data[beacon->bf_len], priv->addr.saddr);
beacon->bf_len += IEEE802154_SADDRSIZE;
}
else
{
IEEE802154_EADDRCOPY(&beacon->bf_data[beacon->bf_len], priv->addr.eaddr);
beacon->bf_len += IEEE802154_EADDRSIZE;
}
/* Clear the superframe specification, then set the appropriate bits */
beacon->bf_data[beacon->bf_len] = 0;
beacon->bf_data[beacon->bf_len + 1] = 0;
IEEE802154_SETBEACONORDER(beacon->bf_data, beacon->bf_len,
Anthony Merlino
committed
priv->sfspec.beaconorder);
Anthony Merlino
committed
IEEE802154_SETSFORDER(beacon->bf_data, beacon->bf_len,
Anthony Merlino
committed
priv->sfspec.sforder);
Anthony Merlino
committed
IEEE802154_SETFINCAPSLOT(beacon->bf_data, beacon->bf_len,
Anthony Merlino
committed
priv->sfspec.final_capslot);
if (priv->sfspec.ble)
Anthony Merlino
committed
{
IEEE802154_SETBLE(beacon->bf_data, beacon->bf_len);
}
Anthony Merlino
committed
if (priv->sfspec.pancoord)
Anthony Merlino
committed
{
IEEE802154_SETPANCOORD(beacon->bf_data, beacon->bf_len);
}
Anthony Merlino
committed
if (priv->sfspec.assocpermit)
Anthony Merlino
committed
{
IEEE802154_SETASSOCPERMIT(beacon->bf_data, beacon->bf_len);
}
beacon->bf_len += 2;
/* TODO: Handle GTS properly, for now, we just set the descriptor count to
* zero and specify that we do not permit GTS requests */
beacon->bf_data[beacon->bf_len++] = 0;
/* TODO: Add GTS List here */
/* Skip the pending address specification field for now */
pendaddrspec_ind = beacon->bf_len++;
Gregory Nutt
committed
Anthony Merlino
committed
txdesc = (FAR struct ieee802154_txdesc_s *)sq_peek(&priv->indirect_queue);
while(txdesc != NULL)
{
if (txdesc->destaddr.mode == IEEE802154_ADDRMODE_SHORT)
{
pendsaddr++;
IEEE802154_SADDRCOPY(&beacon->bf_data[beacon->bf_len], txdesc->destaddr.saddr);
beacon->bf_len += IEEE802154_SADDRSIZE;
}
else if (txdesc->destaddr.mode == IEEE802154_ADDRMODE_EXTENDED)
{
pendeaddr++;
IEEE802154_EADDRCOPY(&beacon->bf_data[beacon->bf_len], txdesc->destaddr.eaddr);
beacon->bf_len += IEEE802154_EADDRSIZE;
}
Gregory Nutt
committed
Anthony Merlino
committed
/* Check if we are up to 7 addresses yet */
if ((pendsaddr + pendeaddr) == 7)
{
break;
}
/* Get the next pending indirect transation */
txdesc = (FAR struct ieee802154_txdesc_s *)sq_next((FAR sq_entry_t *)txdesc);
}
Gregory Nutt
committed
Anthony Merlino
committed
/* At this point, we know how many of each transaction we have, we can setup
* the Pending Address Specification field
*/
Gregory Nutt
committed
Anthony Merlino
committed
beacon->bf_data[pendaddrspec_ind] = (pendsaddr & 0x07) | ((pendeaddr << 4) & 0x70);
/* Copy in the beacon payload */
memcpy(&beacon->bf_data[beacon->bf_len], priv->beaconpayload,
priv->beaconpayloadlength);
beacon->bf_len += priv->beaconpayloadlength;
priv->beaconupdate = false;
}
Anthony Merlino
committed
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_setupindirect
Anthony Merlino
committed
*
* Description:
Anthony Merlino
committed
* Internal function used by various parts of the MAC layer. This function
* places the provided tx descriptor in the indirect list and manages the
* scheduling for purging the transaction if it does not get extracted in
* time.
Anthony Merlino
committed
*
* Assumptions:
* Called with the MAC locked
Anthony Merlino
committed
*
****************************************************************************/
Anthony Merlino
committed
void mac802154_setupindirect(FAR struct ieee802154_privmac_s *priv,
FAR struct ieee802154_txdesc_s *txdesc)
Anthony Merlino
committed
{
Anthony Merlino
committed
uint32_t ticks;
uint32_t symbols;
Anthony Merlino
committed
Anthony Merlino
committed
/* Link the tx descriptor into the list */
Anthony Merlino
committed
Anthony Merlino
committed
sq_addlast((FAR sq_entry_t *)txdesc, &priv->indirect_queue);
Anthony Merlino
committed
Anthony Merlino
committed
/* Update the timestamp for purging the transaction */
Anthony Merlino
committed
Anthony Merlino
committed
/* The maximum time (in unit periods) that a transaction is stored by a
* coordinator and indicated in its beacon. The unit period is governed by
* macBeaconOrder, BO, as follows: For 0 ≤ BO ≤ 14, the unit period will be
* aBaseSuperframeDuration × 2 BO . For BO = 15, the unit period will be
* aBaseSuperframeDuration. [1] pg. 129
Anthony Merlino
committed
*/
Anthony Merlino
committed
if (priv->sfspec.beaconorder < 15)
Anthony Merlino
committed
{
symbols = priv->trans_persisttime *
Anthony Merlino
committed
(IEEE802154_BASE_SUPERFRAME_DURATION * (1 << priv->sfspec.beaconorder));
Anthony Merlino
committed
}
else
{
Anthony Merlino
committed
symbols = priv->trans_persisttime * IEEE802154_BASE_SUPERFRAME_DURATION;
Anthony Merlino
committed
}
Anthony Merlino
committed
ticks = mac802154_symtoticks(priv, symbols);
txdesc->purgetime = clock_systimer() + ticks;
Anthony Merlino
committed
Anthony Merlino
committed
/* Make sure the beacon gets updated */
Anthony Merlino
committed
if (priv->sfspec.beaconorder < 15)
Anthony Merlino
committed
{
priv->beaconupdate = true;
}
Anthony Merlino
committed
/* Check to see if the purge indirect timer is scheduled. If it is, when the
* timer fires, it will schedule the next purge timer event. Inherently, the
* queue will be in order of which transaction needs to be purged next.
Anthony Merlino
committed
* If the purge indirect timer has not been scheduled, schedule it for when
* this transaction should expire.
Anthony Merlino
committed
if (work_available(&priv->purge_work))
{
work_queue(MAC802154_WORK, &priv->purge_work, mac802154_purge_worker,
(FAR void *)priv, ticks);
Anthony Merlino
committed
}
}
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_purge_worker
Anthony Merlino
committed
* Worker function scheduled in order to purge expired indirect transactions.
* The first element in the list should always be removed. The list is searched
* and transactions are removed until a transaction has not yet expired. Then
* if there are any remaining transactions, the work function is rescheduled
* for the next expiring transaction.
*
****************************************************************************/
Anthony Merlino
committed
static void mac802154_purge_worker(FAR void *arg)
Anthony Merlino
committed
FAR struct ieee802154_privmac_s *priv =
(FAR struct ieee802154_privmac_s *)arg;
FAR struct ieee802154_txdesc_s *txdesc;
/* Get exclusive access to the driver structure. We don't care about any
* signals so don't allow interruptions
*/
mac802154_lock(priv, false);
Anthony Merlino
committed
while (1)
{
/* Pop transactions off indirect queue until the transaction timeout has not
* passed.
*/
txdesc = (FAR struct ieee802154_txdesc_s *)sq_peek(&priv->indirect_queue);
if (txdesc == NULL)
{
break;
}
Anthony Merlino
committed
/* Should probably check a little ahead and remove the transaction if it is within
* a certain number of clock ticks away. There is no since in scheduling the
* timer to expire in only a few ticks.
*/
Anthony Merlino
committed
if (clock_systimer() >= txdesc->purgetime)
Anthony Merlino
committed
{
/* Unlink the transaction */
sq_remfirst(&priv->indirect_queue);
/* Free the IOB, the notification, and the tx descriptor */
iob_free(txdesc->frame);
mac802154_notif_free_locked(priv,
(FAR struct ieee802154_notif_s *)txdesc->conf);
Anthony Merlino
committed
mac802154_txdesc_free(priv, txdesc);
Anthony Merlino
committed
priv->beaconupdate = true;
Anthony Merlino
committed
wlinfo("Indirect TX purged");
}
else
{
/* Reschedule the transaction for the next timeout */
work_queue(MAC802154_WORK, &priv->purge_work, mac802154_purge_worker,
(FAR void *)priv, txdesc->purgetime - clock_systimer());
Anthony Merlino
committed
break;
}
}
Anthony Merlino
committed
mac802154_unlock(priv);
}
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_radiopoll
*
* Description:
* Called from the radio driver through the callback struct. This function is
Anthony Merlino
committed
* called when the radio has room for another transaction. If the MAC
* layer has a transaction, it copies it into the supplied buffer and
* returns the length. A descriptor is also populated with the transaction.
*
****************************************************************************/
Anthony Merlino
committed
static int mac802154_radiopoll(FAR const struct ieee802154_radiocb_s *radiocb,
bool gts, FAR struct ieee802154_txdesc_s **txdesc)
{
FAR struct mac802154_radiocb_s *cb =
(FAR struct mac802154_radiocb_s *)radiocb;
FAR struct ieee802154_privmac_s *priv;
DEBUGASSERT(cb != NULL && cb->priv != NULL);
priv = cb->priv;
Anthony Merlino
committed
/* Get exclusive access to the driver structure. Ignore any EINTR signals */
mac802154_lock(priv, false);
Anthony Merlino
committed
if (gts)
Anthony Merlino
committed
/* Check to see if there are any GTS transactions waiting */
Anthony Merlino
committed
Anthony Merlino
committed
*txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->gts_queue);
Anthony Merlino
committed
else
{
/* Check to see if there are any CSMA transactions waiting */
Anthony Merlino
committed
*txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->csma_queue);
}
mac802154_unlock(priv)
Anthony Merlino
committed
if (*txdesc != NULL)
{
return (*txdesc)->frame->io_len;
}
Anthony Merlino
committed
return 0;
}
/****************************************************************************
* Name: mac802154_txdone
*
* Description:
* Called from the radio driver through the callback struct. This function is
* called when the radio has completed a transaction. The txdesc passed gives
* provides information about the completed transaction including the original
* handle provided when the transaction was created and the status of the
* transaction. This function copies the descriptor and schedules work to
* handle the transaction without blocking the radio.
*
****************************************************************************/
static void mac802154_txdone(FAR const struct ieee802154_radiocb_s *radiocb,
Anthony Merlino
committed
FAR struct ieee802154_txdesc_s *txdesc)
{
FAR struct mac802154_radiocb_s *cb =
(FAR struct mac802154_radiocb_s *)radiocb;
FAR struct ieee802154_privmac_s *priv;
DEBUGASSERT(cb != NULL && cb->priv != NULL);
priv = cb->priv;
/* Get exclusive access to the driver structure. We don't care about any
Anthony Merlino
committed
* signals so don't allow interruptions
mac802154_lock(priv, false);
Anthony Merlino
committed
sq_addlast((FAR sq_entry_t *)txdesc, &priv->txdone_queue);
mac802154_unlock(priv)
/* Schedule work with the work queue to process the completion further */
if (work_available(&priv->tx_work))
{
work_queue(MAC802154_WORK, &priv->tx_work, mac802154_txdone_worker,
(FAR void *)priv, 0);
}
}
/****************************************************************************
* Name: mac802154_txdone_worker
*
* Description:
* Worker function scheduled from mac802154_txdone. This function pops any
* TX descriptors off of the list and calls the next highest layers callback
* to inform the layer of the completed transaction and the status of it.
*
****************************************************************************/
static void mac802154_txdone_worker(FAR void *arg)
{
FAR struct ieee802154_privmac_s *priv =
(FAR struct ieee802154_privmac_s *)arg;
Anthony Merlino
committed
FAR struct ieee802154_txdesc_s *txdesc;
FAR struct ieee802154_notif_s *notif;
/* Get exclusive access to the driver structure. We don't care about any
Anthony Merlino
committed
* signals so don't allow interruptions
mac802154_lock(priv, false);
Anthony Merlino
committed
while (1)
{
txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->txdone_queue);
if (txdesc == NULL)
{
break;
}
Anthony Merlino
committed
/* Cast the data_conf to a notification. We get both the private and public
* notification structure to make it easier to use.
Anthony Merlino
committed
*/
notif =(FAR struct ieee802154_notif_s *)txdesc->conf;
Anthony Merlino
committed
wlinfo("Tx status: %s\n", IEEE802154_STATUS_STRING[txdesc->conf->status]);
Anthony Merlino
committed
switch(txdesc->frametype)
Anthony Merlino
committed
{
case IEEE802154_FRAME_DATA:
{
notif->notiftype = IEEE802154_NOTIFY_CONF_DATA;
Anthony Merlino
committed
/* Release the MAC, call the callback, get exclusive access again */
mac802154_unlock(priv)
mac802154_notify(priv, notif);
mac802154_lock(priv, false);
Anthony Merlino
committed
}
break;
Anthony Merlino
committed
case IEEE802154_FRAME_COMMAND:
{
Anthony Merlino
committed
switch (priv->curr_cmd)
{
case IEEE802154_CMD_ASSOC_REQ:
Anthony Merlino
committed
mac802154_txdone_assocreq(priv, txdesc);
Anthony Merlino
committed
break;
Anthony Merlino
committed
case IEEE802154_CMD_ASSOC_RESP:
break;
Anthony Merlino
committed
case IEEE802154_CMD_DISASSOC_NOT:
break;
Anthony Merlino
committed
case IEEE802154_CMD_DATA_REQ:
Anthony Merlino
committed
/* Data requests can be sent for 3 different reasons.
*
* 1. On a beacon-enabled PAN, this command shall be sent
* by a device when macAutoRequest is equal to TRUE and
* a beacon frame indicating that data are pending for
* that device is received from its coordinator.
* 2. when instructed to do so by the next higher layer on
* reception of the MLME-POLL.request primitive.
* 3. a device may send this command to the coordinator
* macResponseWaitTime after the acknowledgment to an
* association request command.
*/
switch (priv->curr_op)
{
case MAC802154_OP_ASSOC:
mac802154_txdone_datareq_assoc(priv, txdesc);
break;
Anthony Merlino
committed
case MAC802154_OP_POLL:
mac802154_txdone_datareq_poll(priv, txdesc);
break;
Anthony Merlino
committed
default:
break;
}
Anthony Merlino
committed
break;
Anthony Merlino
committed
case IEEE802154_CMD_PANID_CONF_NOT:
break;
Anthony Merlino
committed
case IEEE802154_CMD_ORPHAN_NOT:
break;
Anthony Merlino
committed
case IEEE802154_CMD_BEACON_REQ:
break;
Anthony Merlino
committed
case IEEE802154_CMD_COORD_REALIGN:
break;
Anthony Merlino
committed
case IEEE802154_CMD_GTS_REQ:
break;
Anthony Merlino
committed
default:
mac802154_notif_free_locked(priv, notif);
Anthony Merlino
committed
break;
}
Anthony Merlino
committed
}
break;
Anthony Merlino
committed
default:
{
mac802154_notif_free_locked(priv, notif);
Anthony Merlino
committed
}
break;
Anthony Merlino
committed
}
/* Free the IOB and the tx descriptor */
iob_free(txdesc->frame);
mac802154_txdesc_free(priv, txdesc);
}
mac802154_unlock(priv)
Anthony Merlino
committed
}
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_rxframe
Anthony Merlino
committed
*
* Description:
Anthony Merlino
committed
* Called from the radio driver through the callback struct. This function is
* called when the radio has received a frame. The frame is passed in an iob,
* so that we can free it when we are done processing. A pointer to the RX
* descriptor is passed along with the iob, but it must be copied here as it
* is allocated directly on the caller's stack. We simply link the frame,
* copy the RX descriptor, and schedule a worker to process the frame later so
* that we do not hold up the radio.
Anthony Merlino
committed
*
****************************************************************************/
Anthony Merlino
committed
static void mac802154_rxframe(FAR const struct ieee802154_radiocb_s *radiocb,
FAR struct ieee802154_data_ind_s *ind)
Anthony Merlino
committed
{
Anthony Merlino
committed
FAR struct mac802154_radiocb_s *cb =
(FAR struct mac802154_radiocb_s *)radiocb;
FAR struct ieee802154_privmac_s *priv;
Anthony Merlino
committed
Anthony Merlino
committed
DEBUGASSERT(cb != NULL && cb->priv != NULL);
priv = cb->priv;
Anthony Merlino
committed
Anthony Merlino
committed
/* Get exclusive access to the driver structure. We don't care about any
* signals so if we see one, just go back to trying to get access again.
*/
Anthony Merlino
committed
mac802154_lock(priv, false);
Anthony Merlino
committed
Anthony Merlino
committed
/* Push the iob onto the tail of the frame list for processing */
Anthony Merlino
committed
Anthony Merlino
committed
sq_addlast((FAR sq_entry_t *)ind, &priv->dataind_queue);
Anthony Merlino
committed
wlinfo("Frame received\n");
Anthony Merlino
committed
mac802154_unlock(priv)
Anthony Merlino
committed
Anthony Merlino
committed
/* Schedule work with the work queue to process the completion further */
Anthony Merlino
committed
Anthony Merlino
committed
if (work_available(&priv->rx_work))
Anthony Merlino
committed
{
Anthony Merlino
committed
work_queue(MAC802154_WORK, &priv->rx_work, mac802154_rxframe_worker,
(FAR void *)priv, 0);
Anthony Merlino
committed
}
Anthony Merlino
committed
}
/****************************************************************************
Anthony Merlino
committed
* Name: mac802154_rxframe_worker
Anthony Merlino
committed
*
* Description:
Anthony Merlino
committed
* Worker function scheduled from mac802154_rxframe. This function processes
* any frames in the list. Frames intended to be consumed by the MAC layer
* will not produce any callbacks to the next highest layer. Frames intended
* for the application layer will be forwarded to them.
Anthony Merlino
committed
*
****************************************************************************/
Anthony Merlino
committed
Anthony Merlino
committed
static void mac802154_rxframe_worker(FAR void *arg)
Anthony Merlino
committed
{
Anthony Merlino
committed
FAR struct ieee802154_privmac_s *priv =
(FAR struct ieee802154_privmac_s *)arg;
FAR struct ieee802154_data_ind_s *ind;
Anthony Merlino
committed
FAR struct iob_s *iob;
Anthony Merlino
committed
uint16_t *frame_ctrl;
bool panid_comp;
uint8_t ftype;
Anthony Merlino
committed
Anthony Merlino
committed
while(1)
Anthony Merlino
committed
/* Get exclusive access to the driver structure. We don't care about any
* signals so if we see one, just go back to trying to get access again.
*/
Anthony Merlino
committed
mac802154_lock(priv, false);
Anthony Merlino
committed
Anthony Merlino
committed
/* Pop the iob from the head of the frame list for processing */
Anthony Merlino
committed
Anthony Merlino
committed
ind = (FAR struct ieee802154_data_ind_s *)sq_remfirst(&priv->dataind_queue);
Anthony Merlino
committed
Anthony Merlino
committed
/* Once we pop off the indication, we don't need to keep the mac locked */
Anthony Merlino
committed
mac802154_unlock(priv)
Anthony Merlino
committed
Anthony Merlino
committed
if (ind == NULL)
{
return;
}
Anthony Merlino
committed
Anthony Merlino
committed
/* Get a local copy of the frame to make it easier to access */
Anthony Merlino
committed
Anthony Merlino
committed
iob = ind->frame;
Anthony Merlino
committed
/* Set a local pointer to the frame control then move the offset past
Anthony Merlino
committed
* the frame control field
*/
Anthony Merlino
committed
Anthony Merlino
committed
frame_ctrl = (uint16_t *)&iob->io_data[iob->io_offset];
iob->io_offset += 2;
Anthony Merlino
committed
Anthony Merlino
committed
/* We use the data_ind_s as a container for the frame information even if
* this isn't a data frame
*/
Anthony Merlino
committed
Anthony Merlino
committed
ind->src.mode = (*frame_ctrl & IEEE802154_FRAMECTRL_SADDR) >>
IEEE802154_FRAMECTRL_SHIFT_SADDR;
Anthony Merlino
committed
Anthony Merlino
committed
ind->dest.mode = (*frame_ctrl & IEEE802154_FRAMECTRL_DADDR) >>
IEEE802154_FRAMECTRL_SHIFT_DADDR;
Anthony Merlino
committed
Anthony Merlino
committed
panid_comp = (*frame_ctrl & IEEE802154_FRAMECTRL_PANIDCOMP) >>
IEEE802154_FRAMECTRL_SHIFT_PANIDCOMP;
Anthony Merlino
committed
Anthony Merlino
committed
ind->dsn = iob->io_data[iob->io_offset++];
Anthony Merlino
committed
Anthony Merlino
committed
/* If the destination address is included */
Anthony Merlino
committed
Anthony Merlino
committed
if (ind->dest.mode != IEEE802154_ADDRMODE_NONE)