/**************************************************************************** * drivers/wireless/xbee/drivers/xbee_mac.c * * Copyright (C) 2017 Verge Inc. All rights reserved. * Author: Anthony Merlino * * 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 #include #include #include #include "xbee.h" #include "xbee_mac.h" #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define XBEE_ASSOC_POLLDELAY 100 /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void xbee_assoctimer(int argc, uint32_t arg, ...); static void xbee_assocworker(FAR void *arg); /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: xbee_assoctimer * * Description: * This function is used to schedule * an associatioin indication poll. When * association first gets triggered, a watchdog timer is started. This function * is called when it expires. The watchdog timer is scheduled again until * the association is either successful or fails. * * Parameters: * argc - The number of available arguments * arg - The first argument * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static void xbee_assoctimer(int argc, uint32_t arg, ...) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg; int ret; /* In complex environments, we cannot do SPI transfers from the timout * 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. */ DEBUGASSERT(priv && work_available(&priv->assocwork)); /* Notice that poll watchdog is not active so further poll timeouts can * occur until we restart the poll timeout watchdog. */ ret = work_queue(HPWORK, &priv->assocwork, xbee_assocworker, (FAR void *)priv, 0); (void)ret; DEBUGASSERT(ret == OK); } /**************************************************************************** * Name: xbee_assocworker * * Description: * Poll the device for the assosciation status. This function is indirectly * scheduled rom xbee_req_associate in order to poll the device for association * progress. * * Parameters: * arg - The reference to the driver structure (cast to void*) * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static void xbee_assocworker(FAR void *arg) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)arg; xbee_at_query(priv, "AI"); (void)wd_start(priv->assocwd, XBEE_ASSOC_POLLDELAY, xbee_assoctimer, 1, (wdparm_t)arg); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: xbee_bind * * Description: * Bind the MAC callback table to the XBee driver. * * Parameters: * xbee - Reference to the XBee driver structure * cb - MAC callback operations * * Returned Value: * OK on success; Negated errno on failure. * ****************************************************************************/ int xbee_bind(XBEEHANDLE xbee, FAR struct xbee_maccb_s *cb) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; FAR struct xbee_maccb_s *next; FAR struct xbee_maccb_s *prev; /* Add the MAC client callback structure to the list of MAC callbacks in * priority order. * * Search the list to find the location to insert the new instance. * The list is maintained in descending priority order. */ for (prev = NULL, next = priv->cb; (next != NULL && cb->prio <= next->prio); prev = next, next = next->flink); /* Add the instance to the spot found in the list. Check if the instance * goes at the head of the list. */ if (prev == NULL) { cb->flink = priv->cb; /* May be NULL */ priv->cb = cb; } /* No.. the instance goes between prev and next */ else { cb->flink = next; /* May be NULL */ prev->flink = cb; } /* Keep track of the number of clients requesting notification */ if (cb->notify != NULL) { priv->nclients++; } return OK; } /**************************************************************************** * Name: xbee_get_mhrlen * * Description: * Calculate the MAC header length given the frame meta-data. For the XBee, * we use the header to store the entire API frame for the TX request. The * size we need is fixed based on the address mode we are using as it changes * which API frame we need to issue. * ****************************************************************************/ int xbee_get_mhrlen(XBEEHANDLE xbee, FAR const struct ieee802154_frame_meta_s *meta) { int ret = 9; /* Smallest possible header size */ /* We assume that the XBee is configured with application header on but * encryption not on. */ ret += 2; if (meta->srcmode == IEEE802154_ADDRMODE_EXTENDED) { ret += 6; } if (meta->destaddr.mode == IEEE802154_ADDRMODE_EXTENDED) { ret += 6; } return ret; } /**************************************************************************** * Name: xbee_req_data * * Description: * The MCPS-DATA.request primitive requests the transfer of a data SPDU * (i.e., MSDU) from a local SSCS entity to a single peer SSCS entity. * ****************************************************************************/ int xbee_req_data(XBEEHANDLE xbee, FAR const struct ieee802154_frame_meta_s *meta, FAR struct iob_s *frame) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; int index; uint16_t apiframelen; uint8_t frametype; #ifdef CONFIG_DEBUG_ASSERTIONS int prevoffs = frame->io_offset; #endif /* Figure out how much room we need to place the API frame header */ if (meta->destaddr.mode == IEEE802154_ADDRMODE_EXTENDED) { DEBUGASSERT(frame->io_offset >= 14); frame->io_offset -= 14; frametype = XBEE_APIFRAME_TXREQ_EADDR; } else if (meta->destaddr.mode == IEEE802154_ADDRMODE_SHORT) { DEBUGASSERT(frame->io_offset >= 8); frame->io_offset -= 8; frametype = XBEE_APIFRAME_TXREQ_SADDR; } else { return -EINVAL; } index = frame->io_offset; apiframelen = (frame->io_len - frame->io_offset - 3); frame->io_data[index++] = XBEE_STARTBYTE; frame->io_data[index++] = ((apiframelen >> 8) & 0xFF); frame->io_data[index++] = (apiframelen & 0xFF); frame->io_data[index++] = frametype; frame->io_data[index++] = xbee_next_frameid(priv); if (meta->destaddr.mode == IEEE802154_ADDRMODE_EXTENDED) { frame->io_data[index++] = meta->destaddr.eaddr[7]; frame->io_data[index++] = meta->destaddr.eaddr[6]; frame->io_data[index++] = meta->destaddr.eaddr[5]; frame->io_data[index++] = meta->destaddr.eaddr[4]; frame->io_data[index++] = meta->destaddr.eaddr[3]; frame->io_data[index++] = meta->destaddr.eaddr[2]; frame->io_data[index++] = meta->destaddr.eaddr[1]; frame->io_data[index++] = meta->destaddr.eaddr[0]; } else { frame->io_data[index++] = meta->destaddr.saddr[1]; frame->io_data[index++] = meta->destaddr.saddr[0]; } frame->io_data[index++] = 0; /* Options byte. Currently we do not support anything here */ DEBUGASSERT(index == prevoffs); /* Increment io_len by 1 to account for checksum */ frame->io_len++; xbee_insert_checksum(&frame->io_data[frame->io_offset], (frame->io_len - frame->io_offset)); xbee_send_apiframe(priv, &frame->io_data[frame->io_offset], (frame->io_len - frame->io_offset)); iob_free(frame); return OK; } /**************************************************************************** * Name: xbee_req_get * * Description: * The MLME-GET.request primitive requests information about a given PIB * attribute. * * NOTE: The standard specifies that the attribute value should be returned * via the asynchronous MLME-GET.confirm primitve. However, in our * implementation, we synchronously return the value immediately.Therefore, we * merge the functionality of the MLME-GET.request and MLME-GET.confirm * primitives together. * ****************************************************************************/ int xbee_req_get(XBEEHANDLE xbee, enum ieee802154_attr_e attr, FAR union ieee802154_attr_u *attrval) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; int ret = IEEE802154_STATUS_SUCCESS; switch (attr) { case IEEE802154_ATTR_MAC_PANID: { xbee_query_panid(priv); IEEE802154_PANIDCOPY(attrval->mac.panid, priv->addr.panid); } break; case IEEE802154_ATTR_MAC_SADDR: { xbee_query_saddr(priv); IEEE802154_SADDRCOPY(attrval->mac.saddr, priv->addr.saddr); } break; case IEEE802154_ATTR_MAC_EADDR: { xbee_query_eaddr(priv); IEEE802154_EADDRCOPY(attrval->mac.eaddr, priv->addr.eaddr); } break; case IEEE802154_ATTR_MAC_COORD_SADDR: { IEEE802154_SADDRCOPY(attrval->mac.coordsaddr, priv->pandesc.coordaddr.saddr); } break; case IEEE802154_ATTR_MAC_COORD_EADDR: { IEEE802154_EADDRCOPY(attrval->mac.coordeaddr, priv->pandesc.coordaddr.eaddr); } break; case IEEE802154_ATTR_MAC_DEVMODE: { attrval->mac.devmode = priv->devmode; } break; case IEEE802154_ATTR_PHY_CHAN: { xbee_query_chan(priv); attrval->phy.chan = priv->chan; } break; case IEEE802154_ATTR_RADIO_REGDUMP: { xbee_regdump(priv); } break; default: { wlwarn("Unsupported attribute\n"); ret = IEEE802154_STATUS_UNSUPPORTED_ATTRIBUTE; } break; } return ret; } /**************************************************************************** * Name: xbee_req_set * * Description: * The MLME-SET.request primitive attempts to write the given value to the * indicated MAC PIB attribute. * * NOTE: The standard specifies that confirmation should be indicated via * the asynchronous MLME-SET.confirm primitve. However, in our implementation * we synchronously return the status from the request. Therefore, we do merge * the functionality of the MLME-SET.request and MLME-SET.confirm primitives * together. * ****************************************************************************/ int xbee_req_set(XBEEHANDLE xbee, enum ieee802154_attr_e attr, FAR const union ieee802154_attr_u *attrval) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; int ret = IEEE802154_STATUS_SUCCESS; switch (attr) { case IEEE802154_ATTR_MAC_PANID: { xbee_set_panid(priv, attrval->mac.panid); } break; case IEEE802154_ATTR_MAC_EADDR: { ret = IEEE802154_STATUS_DENIED; } break; case IEEE802154_ATTR_MAC_SADDR: { xbee_set_saddr(priv, attrval->mac.saddr); } break; case IEEE802154_ATTR_PHY_CHAN: { xbee_set_chan(priv, attrval->phy.chan); } break; case IEEE802154_ATTR_MAC_ASSOCIATION_PERMIT: { if (attrval->mac.assocpermit) { xbee_set_coordassocflags(priv, XBEE_COORDASSOCFLAGS_ALLOWASSOC); } else { xbee_set_coordassocflags(priv, 0); } } break; #if 0 case IEEE802154_ATTR_MAC_COORD_SADDR: { xbee_set_coordsaddr(priv, attrval->mac.coordsaddr); } break; case IEEE802154_ATTR_MAC_COORD_EADDR: { xbee_set_coordeaddr(priv, attrval->mac.coordeaddr); } break; case IEEE802154_ATTR_MAC_RESPONSE_WAIT_TIME: { priv->resp_waittime = attrval->mac.resp_waittime; } case IEEE802154_ATTR_MAC_RX_ON_WHEN_IDLE: { xbee_setrxonidle(priv, attrval->mac.rxonidle); } #endif default: { wlwarn("Unsupported attribute\n"); ret = IEEE802154_STATUS_UNSUPPORTED_ATTRIBUTE; } break; } return ret; } /**************************************************************************** * Name: xbee_req_start * * Description: * The MLME-START.request primitive makes a request for the device to start * acting as a coordinator. The XBee modules do not support beacon-enabled * networking! * ****************************************************************************/ int xbee_req_start(XBEEHANDLE xbee, FAR struct ieee802154_start_req_s *req) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; if (req->beaconorder != 15) { wlwarn("xbee: beacon-enabled networks not supported\n"); return -EINVAL; } xbee_set_panid(priv, req->panid); xbee_set_chan(priv, req->chan); xbee_enable_coord(priv, true); xbee_set_sleepperiod(priv, 0); return OK; } /**************************************************************************** * Name: xbee_req_associate * * Description: * The MLME-ASSOCIATE.request primitive allows a device to request an * association with a coordinator. * * On receipt of the MLME-ASSOCIATE.request primitive, the MLME of an * unassociated device first updates the appropriate PHY and MAC PIB * attributes, as described in 5.1.3.1, and then generates an association * request command, as defined in 5.3.1 [1] pg.80 * ****************************************************************************/ int xbee_req_associate(XBEEHANDLE xbee, FAR struct ieee802154_assoc_req_s *req) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; if (req->coordaddr.mode == IEEE802154_ADDRMODE_NONE) { return -EINVAL; } xbee_enable_coord(priv, false); xbee_set_panid(priv, req->coordaddr.panid); xbee_set_chan(priv, req->chan); xbee_set_epassocflags(priv, XBEE_EPASSOCFLAGS_AUTOASSOC); /* In order to track the association status, we must poll the device for * an update. */ return wd_start(priv->assocwd, XBEE_ASSOC_POLLDELAY, xbee_assoctimer, 1, (wdparm_t)priv); } /**************************************************************************** * Name: xbee_req_reset * * Description: * The MLME-RESET.request primitive allows the next higher layer to request * that the MLME performs a reset operation. * * Input Parameters: * xbee - Handle to the XBee instance * resetattr - Whether or not to reset the MAC PIB attributes to defaults * ****************************************************************************/ int xbee_req_reset(XBEEHANDLE xbee, bool resetattr) { FAR struct xbee_priv_s *priv = (FAR struct xbee_priv_s *)xbee; /* Reset the XBee radio */ priv->lower->reset(priv->lower); if (resetattr) { xbee_set_panid(priv, IEEE802154_PANID_UNSPEC); xbee_set_saddr(priv, IEEE802154_SADDR_UNSPEC); xbee_enable_coord(priv, false); xbee_set_epassocflags(priv, 0); xbee_set_coordassocflags(priv, 0); } return OK; }