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_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
static void mac802154_notify_worker(FAR void *arg);
/****************************************************************************
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);
sq_init(&priv->primitive_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
/****************************************************************************
* 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.
Anthony Merlino
committed
*
* 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;
FAR struct ieee802154_primitive_s *primitive;
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 primitive for the data confirmation that gets passed along with the
Anthony Merlino
committed
* tx descriptor. These are allocated together, but not freed together.
*/
primitive = ieee802154_primitive_allocate();
(*txdesc)->purgetime = 0;
Anthony Merlino
committed
(*txdesc)->retrycount = priv->maxretries;
Anthony Merlino
committed
(*txdesc)->conf = &primitive->u.dataconf;
Anthony Merlino
committed
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;
}
345
346
347
348
349
350
351
352
353
354
355
356
357
358
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/****************************************************************************
* 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);
if (ret < 0)
{
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);
}
}
Anthony Merlino
committed
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
/****************************************************************************
* 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(HPWORK, &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);
ieee802154_primitive_free((FAR struct ieee802154_primitive_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(HPWORK, &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(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;
Anthony Merlino
committed
FAR struct ieee802154_txdesc_s *txdesc;
FAR struct ieee802154_primitive_s *primitive;
/* 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
*/
primitive =(FAR struct ieee802154_primitive_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:
{
primitive->type = IEEE802154_PRIMITIVE_CONF_DATA;
mac802154_notify(priv, primitive);
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:
ieee802154_primitive_free(primitive);
Anthony Merlino
committed
break;
}
Anthony Merlino
committed
}
break;
Anthony Merlino
committed
default:
{
ieee802154_primitive_free(primitive);
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 =