14fb37c995
ieee802154: Simplifies notify() and rxframe() calls to a single notify() call. dataind's and all other "notifs" are now "primitives" which aligns with standard terminology * mac802154: Adds missing breaks from case statement * sixlowpan: Fixes bad logic where ACK is not requested if address is not a broadcast * ieee802154: Simplification of "notifs" and "datainds" to generic primitives passed via a single notify call to the layer above the MAC * Directories.mk should reference CONFIG_WIRELESS instead of CONFIG_DRIVERS_WIRELESS * xbee_netdev: Network must be locked when calling sixlowpan_input * sixlowpan: Reassembly buffer can't be freed if provided by radio driver * sixlowpan: Don't free IOB if there is an error processing it as the MAC will try to pass it along to another receiver * ieee802154: Adds basic logging to ieee802154_primitive.c * Minor fixes after rebase * xbee: Adds AT query timeout to retry if XBee doesn't respond to request * same70-xplained: Adds Xbee support. Makes mikroBus slot Kconfig 'choice' * mac802154: Removes unused function declaration * drivers/mrf24j40: Fixes compilation error using . operator rather than -> operator * mac802154_device: Changes a few mac802154_primtive_free's to ieee802154_primitive_free() and changes notif to primitive in a couple places. * mac802154: Adds promiscous mode logic to bypass parsing of incoming frames. MAC char device also checks for promiscous mode and passes whole frames including header and FCS to the application if promiscous mode is enabled. * sixlowpan: Fixes logic to correctly check if packet is large enough to include header. This would cause packets to be considered too small when they are sufficiently sized. * sixlowpan: Fixes forwarding logic to use forwarding device rather than received device to look up destination link layer address * net/ipforward: Fixes typo that caused build error when IP forwarding was enabled with CONFIG_NET_ICMPv6_NEIGHBOR enabled as well. * configs/same70-xplained: Simple spelling fix Approved-by: Gregory Nutt <gnutt@nuttx.org>
1558 lines
46 KiB
C
1558 lines
46 KiB
C
/****************************************************************************
|
|
* drivers/wireless/xbee/drivers/xbee.c
|
|
*
|
|
* Copyright (C) 2017 Verge Inc. All rights reserved.
|
|
* 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
|
|
*
|
|
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 <assert.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <semaphore.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/mm/iob.h>
|
|
|
|
#include "xbee.h"
|
|
#include "xbee_mac.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define XBEE_ATQUERY_TIMEOUT 100
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int xbee_interrupt(int irq, FAR void *context, FAR void *arg);
|
|
static void xbee_attnworker(FAR void *arg);
|
|
static bool xbee_validate_apiframe(uint8_t frametype, uint16_t framelen);
|
|
static bool xbee_verify_checksum(FAR const struct iob_s *iob);
|
|
static void xbee_process_apiframes(FAR struct xbee_priv_s *priv,
|
|
FAR struct iob_s *iob);
|
|
static void xbee_process_txstatus(FAR struct xbee_priv_s *priv, uint8_t frameid,
|
|
uint8_t status);
|
|
static void xbee_process_rxframe(FAR struct xbee_priv_s *priv,
|
|
FAR struct iob_s *frame,
|
|
enum ieee802154_addrmode_e addrmode);
|
|
static void xbee_notify(FAR struct xbee_priv_s *priv,
|
|
FAR struct ieee802154_primitive_s *primitive);
|
|
static void xbee_notify_worker(FAR void *arg);
|
|
static void xbee_atquery_timeout(int argc, uint32_t arg, ...);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_interrupt
|
|
*
|
|
* Description:
|
|
* Hardware interrupt handler
|
|
*
|
|
* Parameters:
|
|
* irq - Number of the IRQ that generated the interrupt
|
|
* context - Interrupt register state save info (architecture-specific)
|
|
*
|
|
* Returned Value:
|
|
* OK on success
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int xbee_interrupt(int irq, FAR void *context, FAR void *arg)
|
|
{
|
|
FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
priv->attn_latched = true;
|
|
|
|
/* In complex environments, we cannot do SPI transfers from the interrupt
|
|
* handler because semaphores are probably used to lock the SPI bus. In
|
|
* this case, we will defer processing to the worker thread. This is also
|
|
* much kinder in the use of system resources and is, therefore, probably
|
|
* a good thing to do in any event.
|
|
*/
|
|
|
|
if (work_available(&priv->attnwork))
|
|
{
|
|
return work_queue(HPWORK, &priv->attnwork, xbee_attnworker, (FAR void *)priv, 0);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_attnworker
|
|
*
|
|
* Description:
|
|
* Perform interrupt handling (Attention) logic outside of the interrupt handler
|
|
* (on the work queue thread).
|
|
*
|
|
* Parameters:
|
|
* arg - The reference to the driver structure (cast to void*)
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_attnworker(FAR void *arg)
|
|
{
|
|
FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg;
|
|
FAR struct iob_s *iobhead = NULL;
|
|
FAR struct iob_s *iob = NULL;
|
|
FAR struct iob_s *previob = NULL;
|
|
uint16_t rxframelen = 0;
|
|
|
|
DEBUGASSERT(priv);
|
|
DEBUGASSERT(priv->spi);
|
|
|
|
/* NOTE: There is a helpful side-effect to trying to get the SPI Lock here
|
|
* even when there is a write going on. That is, if the SPI write are on a
|
|
* thread with lower priority, trying to get the lock here should boost the
|
|
* priority of that thread, helping move along the low-level driver work
|
|
* that really should be happening in a high priority way anyway.
|
|
*/
|
|
|
|
SPI_LOCK(priv->spi, 1);
|
|
SPI_SETBITS(priv->spi, 8);
|
|
SPI_SETMODE(priv->spi, SPIDEV_MODE0);
|
|
SPI_SETFREQUENCY(priv->spi, CONFIG_IEEE802154_XBEE_FREQUENCY);
|
|
|
|
/* Assert CS */
|
|
|
|
SPI_SELECT(priv->spi, SPIDEV_IEEE802154(0), true);
|
|
|
|
/* Check to make sure all the data hasn't already been clocked in and
|
|
* we just need to process it.
|
|
*/
|
|
|
|
if (priv->attn_latched && !priv->lower->poll(priv->lower))
|
|
{
|
|
priv->attn_latched = false;
|
|
}
|
|
|
|
/* Allocate an IOB for the incoming data. */
|
|
|
|
iob = iob_alloc(false);
|
|
iob->io_flink = NULL;
|
|
iob->io_len = 0;
|
|
iob->io_offset = 0;
|
|
iob->io_pktlen = 0;
|
|
|
|
/* Keep a reference to the first IOB. If we need to allocate more than
|
|
* one to hold each API frame, then we will still have this reference to
|
|
* the head of the list
|
|
*/
|
|
|
|
iobhead = iob;
|
|
|
|
if (priv->attn_latched)
|
|
{
|
|
while (priv->lower->poll(priv->lower))
|
|
{
|
|
DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE);
|
|
|
|
SPI_RECVBLOCK(priv->spi, &iob->io_data[iob->io_len], 1);
|
|
|
|
switch (iob->io_len)
|
|
{
|
|
case XBEE_APIFRAMEINDEX_STARTBYTE:
|
|
{
|
|
if (iob->io_data[iob->io_len] == XBEE_STARTBYTE)
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_LENGTHMSB:
|
|
{
|
|
rxframelen = iob->io_data[iob->io_len++] << 8;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_LENGTHLSB:
|
|
{
|
|
rxframelen |= iob->io_data[iob->io_len++];
|
|
rxframelen += XBEE_APIFRAME_OVERHEAD;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_TYPE:
|
|
{
|
|
/* Check that the length and frame type make sense together */
|
|
|
|
if (!xbee_validate_apiframe(iob->io_data[iob->io_len],
|
|
rxframelen - XBEE_APIFRAME_OVERHEAD))
|
|
{
|
|
wlwarn("invalid length on incoming API frame. Dropping!\n");
|
|
iob->io_len = 0;
|
|
}
|
|
else
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (iob->io_len == rxframelen - 1)
|
|
{
|
|
iob->io_len++;
|
|
if (xbee_verify_checksum(iob))
|
|
{
|
|
/* This API frame is complete. Allocate a new IOB
|
|
* and link it to the existing one. When we are all
|
|
* finished we will pass this IOB list along for
|
|
* processing.
|
|
*/
|
|
|
|
iob->io_flink = iob_alloc(false);
|
|
iob = iob->io_flink;
|
|
|
|
iob->io_flink = NULL;
|
|
iob->io_len = 0;
|
|
iob->io_offset = 0;
|
|
iob->io_pktlen = 0;
|
|
}
|
|
else
|
|
{
|
|
wlwarn("invalid checksum on incoming API frame. Dropping!\n");
|
|
iob->io_len = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
priv->attn_latched = false;
|
|
}
|
|
|
|
/* The last IOB in the list (or the only one) may be able to be freed since
|
|
* it may not have any valid data. If it contains some data, but not a whole
|
|
* API frame, something is wrong, so we just warn the user and drop the
|
|
* data. If the data was valid, the ATTN line should have stayed asserted
|
|
* until all the data was clocked in. So if we don't have a full frame,
|
|
* we can only drop it.
|
|
*/
|
|
|
|
if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen)
|
|
{
|
|
if (iobhead == iob)
|
|
{
|
|
iobhead = NULL;
|
|
}
|
|
else
|
|
{
|
|
previob = iobhead;
|
|
while (previob->io_flink != iob)
|
|
{
|
|
previob = previob->io_flink;
|
|
}
|
|
previob->io_flink = NULL;
|
|
}
|
|
|
|
if (iob->io_len > 0)
|
|
{
|
|
wlwarn("Partial API frame clocked in. Dropping!\n");
|
|
}
|
|
|
|
iob_free(iob);
|
|
}
|
|
|
|
if (iobhead != NULL)
|
|
{
|
|
if (priv->rx_apiframes == NULL)
|
|
{
|
|
priv->rx_apiframes = iobhead;
|
|
}
|
|
else
|
|
{
|
|
iob = priv->rx_apiframes;
|
|
while (iob->io_flink != NULL)
|
|
{
|
|
iob = iob->io_flink;
|
|
}
|
|
|
|
iob->io_flink = iobhead;
|
|
}
|
|
}
|
|
|
|
/* Before unlocking the SPI bus, we "detach" the IOB list from the private
|
|
* struct and keep a copy. When the SPI bus becomes free, more data can
|
|
* be clocked in from an SPI write. By detaching the IOB list, we can process
|
|
* the incoming data without holding up the SPI bus
|
|
*/
|
|
|
|
iobhead = priv->rx_apiframes;
|
|
priv->rx_apiframes = NULL;
|
|
|
|
/* De-assert CS */
|
|
|
|
SPI_SELECT(priv->spi, SPIDEV_IEEE802154(0), false);
|
|
|
|
/* Relinquish control of the SPI Bus */
|
|
|
|
SPI_LOCK(priv->spi, 0);
|
|
|
|
if (iobhead != NULL)
|
|
{
|
|
xbee_process_apiframes(priv, iobhead);
|
|
}
|
|
|
|
/* If an interrupt occured while the worker was running, it was not
|
|
* scheduled since there is a good chance this function has already handled
|
|
* it as part of the previous ATTN assertion. Therefore, if the ATTN
|
|
* line is asserted again reschedule the work.
|
|
*/
|
|
|
|
if (priv->attn_latched)
|
|
{
|
|
work_queue(HPWORK, &priv->attnwork, xbee_attnworker, (FAR void *)priv, 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_validate_apiframe
|
|
*
|
|
* Description:
|
|
* Verifies that the API frame type is known and that the length makes
|
|
* sense for that frame type.
|
|
*
|
|
* Parameters:
|
|
* frame - pointer to the frame data
|
|
* datalen - The size of the data section of the frame. This is the value
|
|
* included as the second and third byte of the frame.
|
|
*
|
|
* Returns:
|
|
* true - Frame type is known and length is logical
|
|
* false - Frame type is unknown or length is invalid for frame type
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool xbee_validate_apiframe(uint8_t frametype, uint16_t datalen)
|
|
{
|
|
switch (frametype)
|
|
{
|
|
case XBEE_APIFRAME_MODEMSTATUS:
|
|
{
|
|
if (datalen != 2)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_ATRESPONSE:
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_TXSTATUS:
|
|
{
|
|
if (datalen != 3)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_RX_EADDR:
|
|
{
|
|
if (datalen < 14)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_RX_SADDR:
|
|
{
|
|
if (datalen < 8)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_verify_checksum
|
|
*
|
|
* Description:
|
|
* Verifies API frame checksum.
|
|
*
|
|
* Parameters:
|
|
* frame - pointer to the frame data
|
|
* framelen - size of the overall frame. NOT the data length field
|
|
*
|
|
* Returns:
|
|
* true - Checksum is valid
|
|
* false - Checksum is invalid
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool xbee_verify_checksum(FAR const struct iob_s *iob)
|
|
{
|
|
int i;
|
|
uint8_t checksum = 0;
|
|
|
|
DEBUGASSERT(iob->io_len > XBEE_APIFRAME_OVERHEAD);
|
|
|
|
/* Skip the start byte and frame length, but include the checksum */
|
|
|
|
for (i = 3; i < iob->io_len; i++)
|
|
{
|
|
checksum += iob->io_data[i];
|
|
}
|
|
|
|
if (checksum != 0xFF)
|
|
{
|
|
wlwarn("Invalid checksum\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_process_apiframes
|
|
*
|
|
* Description:
|
|
* Processes a list of complete API frames.
|
|
*
|
|
* Assumptions:
|
|
* Frame has already been validated using frame type and frame length and
|
|
* the checksum has also been verified.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_process_apiframes(FAR struct xbee_priv_s *priv,
|
|
FAR struct iob_s *framelist)
|
|
{
|
|
FAR struct ieee802154_primitive_s *primitive;
|
|
FAR struct iob_s *frame;
|
|
FAR struct iob_s *nextframe;
|
|
FAR char *command;
|
|
|
|
DEBUGASSERT(framelist != NULL);
|
|
|
|
frame = framelist;
|
|
|
|
/* At the end of each iteration, frame is set to the next frame in the list
|
|
* and the IOB is freed. If the IOB is not supposed to be freed, per the
|
|
* API frame type, the logic must update the frame to the next frame in the
|
|
* list and use continue to skip freeing the IOB.
|
|
*/
|
|
|
|
while (frame)
|
|
{
|
|
/* Skip over start byte and length */
|
|
|
|
frame->io_offset += XBEE_APIFRAMEINDEX_TYPE;
|
|
|
|
switch (frame->io_data[frame->io_offset++])
|
|
{
|
|
case XBEE_APIFRAME_MODEMSTATUS:
|
|
{
|
|
wlinfo("Modem Status: %d\n", frame->io_data[frame->io_offset++]);
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_ATRESPONSE:
|
|
{
|
|
frame->io_offset++; /* Skip over frame index */
|
|
|
|
command = (FAR char *)&frame->io_data[frame->io_offset];
|
|
frame->io_offset += 2;
|
|
|
|
wlinfo("AT Response Received: %.*s\n", 2, command);
|
|
|
|
/* Make sure the command status is OK=0 */
|
|
|
|
if (frame->io_data[frame->io_offset])
|
|
{
|
|
wlwarn("AT Command Error: %d\n",
|
|
frame->io_data[frame->io_offset]);
|
|
}
|
|
else
|
|
{
|
|
frame->io_offset++;
|
|
|
|
if (memcmp(command, "ID", 2) == 0)
|
|
{
|
|
priv->addr.panid[1] = frame->io_data[frame->io_offset++];
|
|
priv->addr.panid[0] = frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "SH", 2) == 0)
|
|
{
|
|
priv->addr.eaddr[7] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[6] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[5] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[4] = frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "SL", 2) == 0)
|
|
{
|
|
priv->addr.eaddr[3] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[2] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[1] = frame->io_data[frame->io_offset++];
|
|
priv->addr.eaddr[0] = frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "MY", 2) == 0)
|
|
{
|
|
priv->addr.saddr[1] = frame->io_data[frame->io_offset++];
|
|
priv->addr.saddr[0] = frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "CH", 2) == 0)
|
|
{
|
|
priv->chan = frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "VR", 2) == 0)
|
|
{
|
|
priv->firmwareversion = frame->io_data[frame->io_offset++] << 8;
|
|
priv->firmwareversion |= frame->io_data[frame->io_offset++];
|
|
}
|
|
else if (memcmp(command, "AI", 2) == 0)
|
|
{
|
|
wlinfo("Association Indication: %d\n",
|
|
frame->io_data[frame->io_offset]);
|
|
|
|
/* 0xFF = No assocication status determined yet. */
|
|
|
|
if (frame->io_data[frame->io_offset] != 0xFF &&
|
|
frame->io_data[frame->io_offset] != 0x13)
|
|
{
|
|
wd_cancel(priv->assocwd);
|
|
|
|
primitive = ieee802154_primitive_allocate();
|
|
primitive->type = IEEE802154_PRIMITIVE_CONF_ASSOC;
|
|
|
|
if (frame->io_data[frame->io_offset] == 0)
|
|
{
|
|
primitive->u.assocconf.status = IEEE802154_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
primitive->u.assocconf.status = IEEE802154_STATUS_FAILURE;
|
|
}
|
|
|
|
xbee_notify(priv, primitive);
|
|
}
|
|
}
|
|
else if (memcmp(command, "A1", 2) == 0)
|
|
{
|
|
wlinfo("Endpoint Association: %d\n",
|
|
frame->io_data[frame->io_offset]);
|
|
}
|
|
else if (memcmp(command, "A2", 2) == 0)
|
|
{
|
|
wlinfo("Coordinator Association: %d\n",
|
|
frame->io_data[frame->io_offset]);
|
|
}
|
|
else if (memcmp(command, "CE", 2) == 0)
|
|
{
|
|
wlinfo("Coordinator Enable: %d\n",
|
|
frame->io_data[frame->io_offset]);
|
|
}
|
|
else if (memcmp(command, "SP", 2) == 0)
|
|
{
|
|
wlinfo("Sleep Period: %dsec\n",
|
|
frame->io_data[frame->io_offset]/100);
|
|
}
|
|
else
|
|
{
|
|
wlwarn("Unhandled AT Response: %.*s\n", 2, command);
|
|
}
|
|
|
|
/* If this is the command we are querying for */
|
|
|
|
if ((priv->querycmd[0] == *command) &&
|
|
(priv->querycmd[1] == *(command + 1)))
|
|
{
|
|
priv->querydone = true;
|
|
nxsem_post(&priv->atresp_sem);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_TXSTATUS:
|
|
{
|
|
xbee_process_txstatus(priv, frame->io_data[frame->io_offset],
|
|
frame->io_data[frame->io_offset + 1]);
|
|
nxsem_post(&priv->txdone_sem);
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_RX_EADDR:
|
|
{
|
|
nextframe = frame->io_flink;
|
|
frame->io_flink = NULL;
|
|
xbee_process_rxframe(priv, frame, IEEE802154_ADDRMODE_EXTENDED);
|
|
frame = nextframe;
|
|
|
|
/* xbee_process_rxframe takes care of freeing the IOB or passing
|
|
* it along to the next highest layer */
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAME_RX_SADDR:
|
|
{
|
|
nextframe = frame->io_flink;
|
|
frame->io_flink = NULL;
|
|
xbee_process_rxframe(priv, frame, IEEE802154_ADDRMODE_SHORT);
|
|
frame = nextframe;
|
|
|
|
/* xbee_process_rxframe takes care of freeing the IOB or passing
|
|
* it along to the next highest layer */
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
/* This really should never happen since xbee_validateframe should
|
|
* have caught it.
|
|
*/
|
|
|
|
wlwarn("Unknown frame type: %d\n", frame[XBEE_APIFRAMEINDEX_TYPE]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* IOB free logic assumes that a valid io_flink entry in the IOB that
|
|
* is being freed indicates that the IOB is a part of an IOB chain. Since
|
|
* that is not the case here and we are just using the io_flink field
|
|
* as a way of managing a list of independent frames, we set the io_flink
|
|
* to NULL prior to freeing it.
|
|
*/
|
|
|
|
nextframe = frame->io_flink;
|
|
frame->io_flink = NULL;
|
|
iob_free(frame);
|
|
frame = nextframe;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_process_rxframe
|
|
*
|
|
* Description:
|
|
* Process an incoming RX frame.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_process_rxframe(FAR struct xbee_priv_s *priv,
|
|
FAR struct iob_s *frame,
|
|
enum ieee802154_addrmode_e addrmode)
|
|
{
|
|
FAR struct ieee802154_primitive_s *primitive;
|
|
FAR struct ieee802154_data_ind_s *dataind;
|
|
|
|
DEBUGASSERT(frame != NULL);
|
|
|
|
primitive = ieee802154_primitive_allocate();
|
|
dataind = &primitive->u.dataind;
|
|
|
|
primitive->type = IEEE802154_PRIMITIVE_IND_DATA;
|
|
|
|
dataind->frame = frame;
|
|
|
|
/* The XBee does not give us information about how the device was addressed.
|
|
* It only indicates the source mode. Therefore, we use the src address mode
|
|
* as the destination address mode, unless the short address is set to
|
|
* IEEE802154_SADDR_BCAST or IEEE802154_SADDR_UNSPEC
|
|
*/
|
|
|
|
memcpy(&dataind->dest, &priv->addr, sizeof(struct ieee802154_addr_s));
|
|
|
|
if (addrmode == IEEE802154_ADDRMODE_EXTENDED)
|
|
{
|
|
dataind->dest.mode = IEEE802154_ADDRMODE_EXTENDED;
|
|
dataind->src.mode = IEEE802154_ADDRMODE_EXTENDED;
|
|
dataind->src.eaddr[7] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[6] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[5] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[4] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[3] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[2] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[1] = frame->io_data[frame->io_offset++];
|
|
dataind->src.eaddr[0] = frame->io_data[frame->io_offset++];
|
|
}
|
|
else
|
|
{
|
|
if (priv->addr.saddr == IEEE802154_SADDR_BCAST ||
|
|
priv->addr.saddr == IEEE802154_SADDR_UNSPEC)
|
|
{
|
|
dataind->dest.mode = IEEE802154_ADDRMODE_EXTENDED;
|
|
}
|
|
else
|
|
{
|
|
dataind->dest.mode = IEEE802154_ADDRMODE_SHORT;
|
|
}
|
|
|
|
dataind->src.mode = IEEE802154_ADDRMODE_SHORT;
|
|
dataind->src.saddr[1] = frame->io_data[frame->io_offset++];
|
|
dataind->src.saddr[0] = frame->io_data[frame->io_offset++];
|
|
}
|
|
|
|
dataind->rssi = frame->io_data[frame->io_offset++];
|
|
|
|
frame->io_offset++; /* Skip options byte */
|
|
|
|
frame->io_len--; /* Remove the checksum */
|
|
|
|
xbee_notify(priv, primitive);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_process_txstatus
|
|
*
|
|
* Description:
|
|
* Process an incoming TX status message. This searches the list of pending
|
|
* tx requests and notifies the
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_process_txstatus(FAR struct xbee_priv_s *priv, uint8_t frameid,
|
|
uint8_t status)
|
|
{
|
|
FAR struct ieee802154_primitive_s *primitive;
|
|
|
|
primitive = ieee802154_primitive_allocate();
|
|
|
|
primitive->type = IEEE802154_PRIMITIVE_CONF_DATA;
|
|
|
|
switch (status)
|
|
{
|
|
case 0x00:
|
|
primitive->u.dataconf.status = IEEE802154_STATUS_SUCCESS;
|
|
break;
|
|
case 0x01:
|
|
case 0x21:
|
|
primitive->u.dataconf.status = IEEE802154_STATUS_NO_ACK;
|
|
break;
|
|
case 0x02:
|
|
primitive->u.dataconf.status = IEEE802154_STATUS_CHANNEL_ACCESS_FAILURE;
|
|
break;
|
|
default:
|
|
primitive->u.dataconf.status = IEEE802154_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
wlinfo("TX done. Frame ID: %d Status: 0x%02X\n", frameid, status);
|
|
|
|
xbee_notify(priv, primitive);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_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
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_notify(FAR struct xbee_priv_s *priv,
|
|
FAR struct ieee802154_primitive_s *primitive)
|
|
{
|
|
while (nxsem_wait(&priv->primitive_sem) < 0);
|
|
|
|
sq_addlast((FAR sq_entry_t *)primitive, &priv->primitive_queue);
|
|
nxsem_post(&priv->primitive_sem);
|
|
|
|
if (work_available(&priv->notifwork))
|
|
{
|
|
work_queue(LPWORK, &priv->notifwork, xbee_notify_worker,
|
|
(FAR void *)priv, 0);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_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 xbee_notify_worker(FAR void *arg)
|
|
{
|
|
FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg;
|
|
FAR struct xbee_maccb_s *cb;
|
|
FAR struct ieee802154_primitive_s *primitive;
|
|
int ret;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
while (nxsem_wait(&priv->primitive_sem) < 0);
|
|
primitive =
|
|
(FAR struct ieee802154_primitive_s *)sq_remfirst(&priv->primitive_queue);
|
|
nxsem_post(&priv->primitive_sem);
|
|
|
|
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 */
|
|
|
|
while (nxsem_wait(&priv->primitive_sem) < 0);
|
|
primitive =
|
|
(FAR struct ieee802154_primitive_s *)sq_remfirst(&priv->primitive_queue);
|
|
nxsem_post(&priv->primitive_sem);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_atquery_timeout
|
|
*
|
|
* Description:
|
|
* This function runs when an AT Query has timed out waiting for a response
|
|
* from the XBee module. This really should never happen, but if it does,
|
|
* handle it gracefully by retrying the query.
|
|
*
|
|
* Parameters:
|
|
* argc - The number of available arguments
|
|
* arg - The first argument
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void xbee_atquery_timeout(int argc, uint32_t arg, ...)
|
|
{
|
|
FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
wlwarn("AT Query timeout\n");
|
|
|
|
/* Wake the pending query thread so it can retry */
|
|
|
|
nxsem_post(&priv->atresp_sem);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_init
|
|
*
|
|
* Description:
|
|
* Initialize an XBee driver. The XBee device is assumed to be
|
|
* in the post-reset state upon entry to this function.
|
|
*
|
|
* Parameters:
|
|
* spi - A reference to the platform's SPI driver for the XBee
|
|
* lower - The MCU-specific interrupt used to control low-level MCU
|
|
* functions (i.e., XBee GPIO interrupts).
|
|
*
|
|
* Returned Value:
|
|
* OK on success; Negated errno on failure.
|
|
*
|
|
* Assumptions:
|
|
*
|
|
****************************************************************************/
|
|
|
|
XBEEHANDLE xbee_init(FAR struct spi_dev_s *spi,
|
|
FAR const struct xbee_lower_s *lower)
|
|
{
|
|
FAR struct xbee_priv_s *priv;
|
|
|
|
/* Allocate object */
|
|
|
|
priv = (FAR struct xbee_priv_s *) kmm_zalloc(sizeof(struct xbee_priv_s));
|
|
if (priv == NULL)
|
|
{
|
|
wlinfo("Failed allocation xbee_priv_s structure\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Attach irq */
|
|
|
|
if (lower->attach(lower, xbee_interrupt, priv) != OK)
|
|
{
|
|
wlinfo("Failed to attach IRQ with XBee lower half\n");
|
|
kmm_free(priv);
|
|
return NULL;
|
|
}
|
|
|
|
priv->lower = lower;
|
|
priv->spi = spi;
|
|
|
|
nxsem_init(&priv->primitive_sem, 0, 1);
|
|
nxsem_init(&priv->atquery_sem, 0, 1);
|
|
nxsem_init(&priv->tx_sem, 0, 1);
|
|
nxsem_init(&priv->txdone_sem, 0, 0);
|
|
nxsem_setprotocol(&priv->txdone_sem, SEM_PRIO_NONE);
|
|
|
|
ieee802154_primitivepool_initialize();
|
|
|
|
sq_init(&priv->primitive_queue);
|
|
|
|
priv->assocwd = wd_create();
|
|
priv->atquery_wd = wd_create();
|
|
|
|
priv->frameid = 0; /* Frame ID should never be 0, but it is incremented
|
|
* in xbee_next_frameid before being used so it will be 1 */
|
|
priv->querycmd[0] = 0;
|
|
priv->querycmd[1] = 0;
|
|
|
|
/* Reset the XBee radio */
|
|
|
|
priv->lower->reset(priv->lower);
|
|
|
|
/* Enable interrupts */
|
|
|
|
priv->lower->enable(priv->lower, true);
|
|
|
|
/* Trigger a dummy query without waiting to tell the XBee to operate in SPI
|
|
* mode. By default the XBee uses the UART interface. It switches automatically
|
|
* when a valid SPI frame is received.
|
|
*/
|
|
|
|
xbee_send_atquery(priv, "VR");
|
|
|
|
return (XBEEHANDLE)priv;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_send_apiframe
|
|
*
|
|
* Description:
|
|
* Write an api frame over SPI
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_send_apiframe(FAR struct xbee_priv_s *priv,
|
|
FAR const uint8_t *frame, uint16_t framelen)
|
|
{
|
|
FAR struct iob_s *iob;
|
|
FAR struct iob_s *previob;
|
|
FAR struct iob_s *iobhead;
|
|
uint16_t rxframelen = 0;
|
|
int i;
|
|
|
|
/* Get access to SPI bus, set relevant settings */
|
|
|
|
SPI_LOCK(priv->spi, 1);
|
|
SPI_SETBITS(priv->spi, 8);
|
|
SPI_SETMODE(priv->spi, SPIDEV_MODE0);
|
|
SPI_SETFREQUENCY(priv->spi, CONFIG_IEEE802154_XBEE_FREQUENCY);
|
|
|
|
/* Assert CS */
|
|
|
|
SPI_SELECT(priv->spi, SPIDEV_IEEE802154(0), true);
|
|
|
|
/* Allocate an IOB for the incoming data. The XBee supports full-duplex
|
|
* SPI communication. This means that the MISO data can become valid at any
|
|
* time. This requires us to process incoming MISO data to see if it is valid.
|
|
*/
|
|
|
|
iob = iob_alloc(false);
|
|
iob->io_flink = NULL;
|
|
iob->io_len = 0;
|
|
iob->io_offset = 0;
|
|
iob->io_pktlen = 0;
|
|
|
|
/* Keep a reference to the first IOB. If we need to allocate more than
|
|
* one to hold each API frame, then we will still have this reference to the
|
|
* head of the list
|
|
*/
|
|
|
|
iobhead = iob;
|
|
|
|
i = 0;
|
|
while (i < framelen || priv->lower->poll(priv->lower))
|
|
{
|
|
if (i < framelen)
|
|
{
|
|
iob->io_data[iob->io_len] = SPI_SEND(priv->spi, frame[i++]);
|
|
}
|
|
else
|
|
{
|
|
SPI_RECVBLOCK(priv->spi, &iob->io_data[iob->io_len], 1);
|
|
}
|
|
|
|
/* attn_latched should be set true immediately from the interrupt. Any
|
|
* data prior to that can be completely ignored.
|
|
*/
|
|
|
|
if (priv->attn_latched)
|
|
{
|
|
DEBUGASSERT(iob->io_len <= CONFIG_IOB_BUFSIZE);
|
|
|
|
switch (iob->io_len)
|
|
{
|
|
case XBEE_APIFRAMEINDEX_STARTBYTE:
|
|
{
|
|
if (iob->io_data[iob->io_len] == XBEE_STARTBYTE)
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_LENGTHMSB:
|
|
{
|
|
rxframelen = iob->io_data[iob->io_len++] << 8;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_LENGTHLSB:
|
|
{
|
|
rxframelen |= iob->io_data[iob->io_len++];
|
|
rxframelen += XBEE_APIFRAME_OVERHEAD;
|
|
}
|
|
break;
|
|
case XBEE_APIFRAMEINDEX_TYPE:
|
|
{
|
|
/* Check that the length and frame type make sense together */
|
|
|
|
if (!xbee_validate_apiframe(iob->io_data[iob->io_len],
|
|
rxframelen - XBEE_APIFRAME_OVERHEAD))
|
|
{
|
|
wlwarn("invalid length on incoming API frame. Dropping!\n");
|
|
iob->io_len = 0;
|
|
}
|
|
else
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
if (iob->io_len == rxframelen - 1)
|
|
{
|
|
iob->io_len++;
|
|
if (xbee_verify_checksum(iob))
|
|
{
|
|
/* This API frame is complete. Allocate a new IOB
|
|
* and link it to the existing one. When we are all
|
|
* finished we will pass this IOB list along for
|
|
* processing.
|
|
*/
|
|
|
|
iob->io_flink = iob_alloc(false);
|
|
iob = iob->io_flink;
|
|
|
|
iob->io_flink = NULL;
|
|
iob->io_len = 0;
|
|
iob->io_offset = 0;
|
|
iob->io_pktlen = 0;
|
|
}
|
|
else
|
|
{
|
|
wlwarn("invalid checksum on incoming API frame. Dropping!\n");
|
|
iob->io_len = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iob->io_len++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The last IOB in the list (or the only one) may be able to be freed since
|
|
* it may not have any valid data. If it contains some data, but not a whole
|
|
* API frame, something is wrong, so we just warn the user and drop the
|
|
* data. If the data was valid, the ATTN line should have stayed asserted
|
|
* until all the data was clocked in. So if we don't have a full frame,
|
|
* we can only drop it.
|
|
*/
|
|
|
|
if (iob->io_len < XBEE_APIFRAME_OVERHEAD || iob->io_len != rxframelen)
|
|
{
|
|
if (iobhead == iob)
|
|
{
|
|
iobhead = NULL;
|
|
}
|
|
else
|
|
{
|
|
previob = iobhead;
|
|
while (previob->io_flink != iob)
|
|
{
|
|
previob = previob->io_flink;
|
|
}
|
|
previob->io_flink = NULL;
|
|
}
|
|
|
|
if (iob->io_len > 0)
|
|
{
|
|
wlwarn("Partial API frame clocked in. Dropping!\n");
|
|
}
|
|
|
|
iob_free(iob);
|
|
}
|
|
|
|
if (iobhead != NULL)
|
|
{
|
|
if (priv->rx_apiframes == NULL)
|
|
{
|
|
priv->rx_apiframes = iobhead;
|
|
}
|
|
else
|
|
{
|
|
iob = priv->rx_apiframes;
|
|
while (iob->io_flink != NULL)
|
|
{
|
|
iob = iob->io_flink;
|
|
}
|
|
|
|
iob->io_flink = iobhead;
|
|
}
|
|
}
|
|
|
|
/* De-assert CS */
|
|
|
|
SPI_SELECT(priv->spi, SPIDEV_IEEE802154(0), false);
|
|
|
|
/* Relinquish control of the SPI Bus */
|
|
|
|
SPI_LOCK(priv->spi,0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_atquery
|
|
*
|
|
* Description:
|
|
* Sends AT Query and waits for response from device
|
|
*
|
|
****************************************************************************/
|
|
|
|
int xbee_atquery(FAR struct xbee_priv_s *priv, FAR const char *atcommand)
|
|
{
|
|
int ret;
|
|
|
|
/* Only allow one query at a time */
|
|
|
|
ret = nxsem_wait(&priv->atquery_sem);
|
|
if (ret < 0)
|
|
{
|
|
DEBUGASSERT(ret == -EINTR);
|
|
return ret;
|
|
}
|
|
|
|
priv->querydone = false;
|
|
|
|
/* We reinitialize this every time, in case something gets out of phase with
|
|
* the timeout and the received response.
|
|
*/
|
|
|
|
nxsem_init(&priv->atresp_sem, 0, 0);
|
|
nxsem_setprotocol(&priv->atresp_sem, SEM_PRIO_NONE);
|
|
|
|
do
|
|
{
|
|
/* Setup a timeout */
|
|
|
|
(void)wd_start(priv->atquery_wd, XBEE_ATQUERY_TIMEOUT, xbee_atquery_timeout,
|
|
1, (wdparm_t)priv);
|
|
|
|
/* Send the query */
|
|
|
|
priv->querycmd[0] = *atcommand;
|
|
priv->querycmd[1] = *(atcommand + 1);
|
|
xbee_send_atquery(priv, atcommand);
|
|
|
|
/* Wait for the response to be received */
|
|
|
|
ret = nxsem_wait(&priv->atresp_sem);
|
|
if (ret < 0)
|
|
{
|
|
DEBUGASSERT(ret == -EINTR);
|
|
wd_cancel(priv->atquery_wd);
|
|
priv->querycmd[0] = 0;
|
|
priv->querycmd[1] = 0;
|
|
nxsem_post(&priv->atquery_sem);
|
|
return ret;
|
|
}
|
|
}
|
|
while (!priv->querydone);
|
|
|
|
nxsem_post(&priv->atquery_sem);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_send_atquery
|
|
*
|
|
* Description:
|
|
* Helper function to send the AT query to the XBee
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_send_atquery(FAR struct xbee_priv_s *priv, FAR const char *atcommand)
|
|
{
|
|
uint8_t frame[8];
|
|
|
|
wlinfo("AT Query: %c%c\n", *atcommand, *(atcommand + 1));
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 4;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 1;
|
|
frame[5] = *atcommand;
|
|
frame[6] = *(atcommand + 1);
|
|
|
|
xbee_insert_checksum(frame, 8);
|
|
|
|
xbee_send_apiframe(priv, frame, 8);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_panid
|
|
*
|
|
* Description:
|
|
* Sends API frame with AT command request in order to set the PAN ID
|
|
* (Network ID) of the device.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_panid(FAR struct xbee_priv_s *priv, FAR const uint8_t *panid)
|
|
{
|
|
uint8_t frame[10];
|
|
|
|
IEEE802154_PANIDCOPY(priv->addr.panid, panid);
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 6;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'I';
|
|
frame[6] = 'D';
|
|
frame[7] = *(panid + 1);
|
|
frame[8] = *(panid);
|
|
|
|
xbee_insert_checksum(frame, 10);
|
|
|
|
xbee_send_apiframe(priv, frame, 10);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_saddr
|
|
*
|
|
* Description:
|
|
* Sends API frame with AT command request in order to set the Short Address
|
|
* (Source Address (MY)) of the device
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_saddr(FAR struct xbee_priv_s *priv, FAR const uint8_t *saddr)
|
|
{
|
|
uint8_t frame[10];
|
|
|
|
IEEE802154_SADDRCOPY(priv->addr.saddr, saddr);
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 6;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'M';
|
|
frame[6] = 'Y';
|
|
frame[7] = *(saddr + 1);
|
|
frame[8] = *(saddr);
|
|
|
|
xbee_insert_checksum(frame, 10);
|
|
|
|
xbee_send_apiframe(priv, frame, 10);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_chan
|
|
*
|
|
* Description:
|
|
* Sends API frame with AT command request in order to set the RF channel
|
|
* (Operatin Channel) of the device.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_chan(FAR struct xbee_priv_s *priv, uint8_t chan)
|
|
{
|
|
uint8_t frame[9];
|
|
|
|
priv->chan = chan;
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 5;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'C';
|
|
frame[6] = 'H';
|
|
frame[7] = chan;
|
|
|
|
xbee_insert_checksum(frame, 9);
|
|
|
|
xbee_send_apiframe(priv, frame, 9);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_epassocflags
|
|
*
|
|
* Description:
|
|
* Set flags in 'A1' command register to determine how endpoint behaves
|
|
* with regards to association.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_epassocflags(FAR struct xbee_priv_s *priv, uint8_t flags)
|
|
{
|
|
uint8_t frame[9];
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 5;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'A';
|
|
frame[6] = '1';
|
|
frame[7] = flags;
|
|
|
|
xbee_insert_checksum(frame, 9);
|
|
|
|
xbee_send_apiframe(priv, frame, 9);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_coordassocflags
|
|
*
|
|
* Description:
|
|
* Set flags in 'A2' command register to determine how coordinator behaves
|
|
* with regards to association.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_coordassocflags(FAR struct xbee_priv_s *priv, uint8_t flags)
|
|
{
|
|
uint8_t frame[9];
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 5;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'A';
|
|
frame[6] = '2';
|
|
frame[7] = flags;
|
|
|
|
xbee_insert_checksum(frame, 9);
|
|
|
|
xbee_send_apiframe(priv, frame, 9);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_set_sleepperiod
|
|
*
|
|
* Description:
|
|
* Set Cyclic Sleep Period using 'SP' AT command.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_set_sleepperiod(FAR struct xbee_priv_s *priv, uint16_t period)
|
|
{
|
|
uint8_t frame[10];
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 6;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'S';
|
|
frame[6] = 'P';
|
|
frame[7] = period >> 8;
|
|
frame[8] = period;
|
|
|
|
xbee_insert_checksum(frame, 10);
|
|
|
|
xbee_send_apiframe(priv, frame, 10);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_enable_coord
|
|
*
|
|
* Description:
|
|
* Enables/Disables coordinator mode using 'CE' command
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_enable_coord(FAR struct xbee_priv_s *priv, bool enable)
|
|
{
|
|
uint8_t frame[9];
|
|
|
|
frame[0] = XBEE_STARTBYTE;
|
|
frame[1] = 0;
|
|
frame[2] = 5;
|
|
frame[3] = XBEE_APIFRAME_ATCOMMMAND;
|
|
frame[4] = 0;
|
|
frame[5] = 'C';
|
|
frame[6] = 'E';
|
|
frame[7] = enable;
|
|
|
|
xbee_insert_checksum(frame, 9);
|
|
|
|
xbee_send_apiframe(priv, frame, 9);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: xbee_regdump
|
|
*
|
|
* Description:
|
|
* Perform a series of queries updating struct and printing settings to SYSLOG.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void xbee_regdump(FAR struct xbee_priv_s *priv)
|
|
{
|
|
xbee_query_firmwareversion(priv);
|
|
|
|
wlinfo("XBee Firmware Version: %04x\n", priv->firmwareversion);
|
|
|
|
xbee_send_atquery(priv, "CE");
|
|
xbee_send_atquery(priv, "A1");
|
|
xbee_send_atquery(priv, "A2");
|
|
xbee_send_atquery(priv, "SP");
|
|
}
|