Skip to content
mac802154.c 66.7 KiB
Newer Older
/****************************************************************************
 * wireless/ieee802154/mac802154.c
 *
 *   Copyright (C) 2016 Sebastien Lorquet. All rights reserved.
 *   Copyright (C) 2017 Gregory Nutt. All rights reserved.
 *   Copyright (C) 2017 Verge Inc. All rights reserved.
 *   Author: Sebastien Lorquet <sebastien@lorquet.fr>
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *   Author: Anthony Merlino <anthony@vergeaero.com>
 *
 * 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>
#include <nuttx/mm/iob.h>
#include "mac802154_internal.h"
#include "mac802154_assoc.h"
#include "mac802154_data.h"
#include "mac802154_poll.h"
#include <nuttx/wireless/ieee802154/ieee802154_mac.h>
#include <nuttx/wireless/ieee802154/ieee802154_radio.h>

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Data structure pools and allocation helpers */

static void mac802154_resetqueues(FAR struct ieee802154_privmac_s *priv);
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,
                             FAR struct ieee802154_txdesc_s *tx_desc);
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);
static void mac802154_rxframe_worker(FAR void *arg);
static void mac802154_sfevent(FAR const struct ieee802154_radiocb_s *radiocb,
                              enum ieee802154_sfevent_e sfevent);
static void mac802154_purge_worker(FAR void *arg);
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);
static void mac802154_notify_worker(FAR void *arg);

/****************************************************************************
 ****************************************************************************/

/****************************************************************************
 *   Initializes the various queues used in the MAC layer. Called on creation
 *   of MAC.
 *
 ****************************************************************************/

static void mac802154_resetqueues(FAR struct ieee802154_privmac_s *priv)
  sq_init(&priv->txdone_queue);
  sq_init(&priv->csma_queue);
  sq_init(&priv->indirect_queue);
  sq_init(&priv->dataind_queue);
  sq_init(&priv->primitive_queue);
  /* Initialize the tx descriptor allocation pool */
  for (i = 0; i < CONFIG_MAC802154_NTXDESC; i++)
      sq_addlast((FAR sq_entry_t *)&priv->txdesc_pool[i], &priv->txdesc_queue);
  nxsem_init(&priv->txdesc_sem, 0, CONFIG_MAC802154_NTXDESC);
/****************************************************************************
 * Name: mac802154_txdesc_pool
 *
 * Description:
 *   This function allocates a tx descriptor and the dependent primitive (data
 *   confirmation) from the free list. The primitive and tx descriptor must be
 *   freed seperately.
 *
 * 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.
 *
 ****************************************************************************/

int mac802154_txdesc_alloc(FAR struct ieee802154_privmac_s *priv,
                           FAR struct ieee802154_txdesc_s **txdesc,
                           bool allow_interrupt)
  FAR struct ieee802154_primitive_s *primitive;
  /* 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
   */

  ret = nxsem_trywait(&priv->txdesc_sem);
      *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 */


      /* 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);
          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);
          wlwarn("WARNING: mac802154_takesem failed: %d\n", ret);

          mac802154_givesem(&priv->txdesc_sem);
          return -EINTR;
        }

      /* We can now safely unlink the next free structure from the free list */
      *txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->txdesc_queue);
    }
  /* We have now successfully allocated the tx descriptor.  Now we need to allocate
   * the primitive for the data confirmation that gets passed along with the
   * tx descriptor. These are allocated together, but not freed together.
   */

  primitive = ieee802154_primitive_allocate();
  (*txdesc)->purgetime = 0;
  (*txdesc)->conf = &primitive->u.dataconf;
/****************************************************************************
 *
 * 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
 *
 ****************************************************************************/

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)
    {
      mac802154_putpanid(iob, coordaddr->panid);

      if (coordaddr->mode == IEEE802154_ADDRMODE_SHORT)
        {
          mac802154_putsaddr(iob, coordaddr->saddr);
        }
      else if (coordaddr->mode == IEEE802154_ADDRMODE_EXTENDED)
        {
          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 &&
      IEEE802154_PANIDCMP(coordaddr->panid, priv->addr.panid))
      IEEE802154_SETPANIDCOMP(iob->io_data, 0);
      mac802154_putpanid(iob, priv->addr.panid);
  if (srcmode == IEEE802154_ADDRMODE_SHORT)
    {
      mac802154_putsaddr(iob, priv->addr.saddr);
    }
  else if (srcmode == IEEE802154_ADDRMODE_EXTENDED)
    {
      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 */
/****************************************************************************
 * Name: mac802154_notify
 *
 * Description:
 *   Queue the primitive in the queue and queue work on the LPWORK
 *   queue if is not already scheduled.
 *
 * Assumptions:
 *    Called with the MAC locked
 *
 ****************************************************************************/

void mac802154_notify(FAR struct ieee802154_privmac_s *priv,
                      FAR struct ieee802154_primitive_s *primitive)
{
  sq_addlast((FAR sq_entry_t *)primitive, &priv->primitive_queue);

  if (work_available(&priv->notifwork))
    {
      work_queue(LPWORK, &priv->notifwork, mac802154_notify_worker,
                 (FAR void *)priv, 0);
    }
}

/****************************************************************************
 * Name: mac802154_notify_worker
 *
 * Description:
 *    Pop each primitive off the queue and call the registered
 *    callbacks.  There is special logic for handling ieee802154_data_ind_s.
 *
 ****************************************************************************/

static void mac802154_notify_worker(FAR void *arg)
{
  FAR struct ieee802154_privmac_s *priv = (FAR struct ieee802154_privmac_s *)arg;
  FAR struct mac802154_maccb_s *cb;
  FAR struct ieee802154_primitive_s *primitive;
  int ret;


  mac802154_lock(priv, false);
  primitive =
    (FAR struct ieee802154_primitive_s *)sq_remfirst(&priv->primitive_queue);
  mac802154_unlock(priv);

  while (primitive != NULL)
    {
      /* Data indications are a special case since the frame can only be passed to
       * one place. The return value of the notify call is used to accept or reject
       * the primitive. In the case of the data indication, there can only be one
       * accept. Callbacks are stored in order of there receiver priority ordered
       * when the callbacks are bound in mac802154_bind().
       */

      if (primitive->type == IEEE802154_PRIMITIVE_IND_DATA)
        {
          bool dispose = true;

          primitive->nclients = 1;

          for (cb = priv->cb; cb != NULL; cb = cb->flink)
            {
              if (cb->notify != NULL)
                {
                  ret = cb->notify(cb, primitive);
                  if (ret >= 0)
                    {
                      /* The receiver accepted and disposed of the frame and it's
                       * meta-data. We are done.
                       */

                      dispose = false;
                      break;
                    }
                }
            }

          if (dispose)
            {
              iob_free(primitive->u.dataind.frame);
              ieee802154_primitive_free(primitive);
            }
        }
      else
        {
          /* Set the number of clients count so that the primitive resources will be
           * preserved until all clients are finished with it.
           */

          primitive->nclients = priv->nclients;

          /* Try to notify every registered MAC client */

          for (cb = priv->cb; cb != NULL; cb = cb->flink)
            {
              if (cb->notify != NULL)
                {
                  ret = cb->notify(cb, primitive);
                    {
                      ieee802154_primitive_free(primitive);
                    }
                }
              else
                {
                  ieee802154_primitive_free(primitive);
                }
            }
        }

      /* Get the next primitive then loop */

      mac802154_lock(priv, false);
      primitive =
        (FAR struct ieee802154_primitive_s *)sq_remfirst(&priv->primitive_queue);
      mac802154_unlock(priv);
    }
}

/****************************************************************************
 * 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 */

  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 */
  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.
   */

  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,
  IEEE802154_SETSFORDER(beacon->bf_data, beacon->bf_len,
  IEEE802154_SETFINCAPSLOT(beacon->bf_data, beacon->bf_len,
                           priv->sfspec.final_capslot);
  if (priv->sfspec.ble)
    {
      IEEE802154_SETBLE(beacon->bf_data, beacon->bf_len);
    }
    {
      IEEE802154_SETPANCOORD(beacon->bf_data, beacon->bf_len);
    }
    {
      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++;
  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;
        }
      /* 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);
    }
  /* At this point, we know how many of each transaction we have, we can setup
   * the Pending Address Specification field
   */
  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;
}

/****************************************************************************
 *    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.
 *    Called with the MAC locked
 *
 ****************************************************************************/

void mac802154_setupindirect(FAR struct ieee802154_privmac_s *priv,
                             FAR struct ieee802154_txdesc_s *txdesc)
  /* Link the tx descriptor into the list */
  sq_addlast((FAR sq_entry_t *)txdesc, &priv->indirect_queue);
  /* Update the timestamp for purging the transaction */
  /* 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
      symbols = priv->trans_persisttime *
        (IEEE802154_BASE_SUPERFRAME_DURATION * (1 << priv->sfspec.beaconorder));
      symbols = priv->trans_persisttime * IEEE802154_BASE_SUPERFRAME_DURATION;
  ticks = mac802154_symtoticks(priv, symbols);
  txdesc->purgetime = clock_systimer() + ticks;
  /* 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.
   * If the purge indirect timer has not been scheduled, schedule it for when
   * this transaction should expire.
      work_queue(HPWORK, &priv->purge_work, mac802154_purge_worker,
}

/****************************************************************************
 *   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.
 *
 ****************************************************************************/

static void mac802154_purge_worker(FAR void *arg)
  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
   */


  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;
      }

    /* 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.
     */
    if (clock_systimer() >= txdesc->purgetime)
      {
        /* Unlink the transaction */

        sq_remfirst(&priv->indirect_queue);

        /* Free the IOB, the notification, and the tx descriptor */

        iob_free(txdesc->frame);
        ieee802154_primitive_free((FAR struct ieee802154_primitive_s *)txdesc->conf);

        wlinfo("Indirect TX purged");
      }
    else
      {
        /* Reschedule the transaction for the next timeout */

        work_queue(HPWORK, &priv->purge_work, mac802154_purge_worker,
                   (FAR void *)priv, txdesc->purgetime - clock_systimer());
}

/****************************************************************************
 *
 * Description:
 *   Called from the radio driver through the callback struct.  This function is
 *   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.
 *
 ****************************************************************************/

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;

  /* Get exclusive access to the driver structure.  Ignore any EINTR signals */
      /* Check to see if there are any GTS transactions waiting */
      *txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->gts_queue);
  else
    {
      /* Check to see if there are any CSMA transactions waiting */
      *txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->csma_queue);
    }
  if (*txdesc != NULL)
    {
      return (*txdesc)->frame->io_len;
    }
}

/****************************************************************************
 * 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,
                             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
   * signals so don't allow interruptions
  sq_addlast((FAR sq_entry_t *)txdesc, &priv->txdone_queue);

  /* Schedule work with the work queue to process the completion further */

  if (work_available(&priv->tx_work))
    {
      work_queue(HPWORK, &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;
  FAR struct ieee802154_primitive_s *primitive;

  /* Get exclusive access to the driver structure.  We don't care about any
   * signals so don't allow interruptions
  while (1)
    {
      txdesc = (FAR struct ieee802154_txdesc_s *)sq_remfirst(&priv->txdone_queue);

      if (txdesc == NULL)
        {
          break;
        }

      /* Cast the data_conf to a notification. We get both the private and public
       * notification structure to make it easier to use.
      primitive =(FAR struct ieee802154_primitive_s *)txdesc->conf;
      wlinfo("Tx status: %s\n", IEEE802154_STATUS_STRING[txdesc->conf->status]);
              primitive->type = IEEE802154_PRIMITIVE_CONF_DATA;
              mac802154_notify(priv, primitive);
              switch (priv->curr_cmd)
                {
                  case IEEE802154_CMD_ASSOC_REQ:
                    mac802154_txdone_assocreq(priv, txdesc);
                    /* 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;
                        case MAC802154_OP_POLL:
                          mac802154_txdone_datareq_poll(priv, txdesc);
                          break;
                  case IEEE802154_CMD_PANID_CONF_NOT:
                    break;
                    ieee802154_primitive_free(primitive);
              ieee802154_primitive_free(primitive);
        }

      /* Free the IOB and the tx descriptor */

      iob_free(txdesc->frame);
      mac802154_txdesc_free(priv, txdesc);
    }

}

/****************************************************************************
 *   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.
 *
 ****************************************************************************/

static void mac802154_rxframe(FAR const struct ieee802154_radiocb_s *radiocb,
                              FAR struct ieee802154_data_ind_s *ind)