Ejemplo n.º 1
0
/******************************************************************************
 * @fn          nwk_nwkInit
 *
 * @brief       Initialize NWK conext.
 *
 * input parameters
 *
 * output parameters
 *
 * @return   Status of operation.
 */
smplStatus_t nwk_nwkInit(uint8_t (*f)(linkID_t))
{
  /* initialize globals */
  nwk_globalsInit();

  /* initialize frame processing */
  nwk_frameInit(f);

	/* added for CCE port */ 
  memset(sConTable.connStruct, 0, sizeof(sConTable.connStruct));
  nwk_QInit();
	
  /* initialize each network application. */
  nwk_freqInit();
  nwk_pingInit();
  nwk_joinInit(f);
  nwk_mgmtInit();
  nwk_linkInit();
  nwk_securityInit();

  /* set up the last connection as the broadcast port mapped to the broadcast Link ID */
  sConTable.connStruct[NUM_CONNECTIONS].isValid     = 1;
  sConTable.connStruct[NUM_CONNECTIONS].hops2target = MAX_HOPS;
  sConTable.connStruct[NUM_CONNECTIONS].portRx      = SMPL_PORT_USER_BCAST;
  sConTable.connStruct[NUM_CONNECTIONS].portTx      = SMPL_PORT_USER_BCAST;
  sConTable.connStruct[NUM_CONNECTIONS].thisLinkID  = SMPL_LINKID_USER_UUD;
  /* set peer address to broadcast so it is used when Application sends to the broadcast Link ID */
  memcpy(sConTable.connStruct[NUM_CONNECTIONS].peerAddr, nwk_getBCastAddress(), NET_ADDR_SIZE);

  return SMPL_SUCCESS;
}
Ejemplo n.º 2
0
/******************************************************************************
 * @fn          broadcast_channel_change
 *
* @brief       For Access Point only: broadcast a channel change frame.
 *
 * input parameters
 * @param   idx  -  index into channel table of new (logical) channel
 *
 * @return   none.
 */
#ifdef ACCESS_POINT
#define CC_REDUNDANCY      1   /* Change-channel redundancy count */
static void broadcast_channel_change(uint8_t idx)
{
  ioctlRawSend_t send;
  uint8_t        msg[FREQ_REQ_MOVE_FRAME_SIZE];
  uint8_t        repeat = CC_REDUNDANCY;

  if (idx >= NWK_FREQ_TBL_SIZE)
  {
    return;
  }

  msg[FB_APP_INFO_OS] = FREQ_REQ_MOVE;
  msg[F_CHAN_OS]      = idx;

  send.addr = (addr_t *)nwk_getBCastAddress();
  send.msg  = msg;
  send.len  = sizeof(msg);
  send.port = SMPL_PORT_FREQ;

  SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &send);
  /* Redundancy addresses the fact that an RE (or any always-listening
   * device) might miss the command
   */
  while (repeat--)
  {
    NWK_DELAY(250);
    SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &send);
  }
}
Ejemplo n.º 3
0
/******************************************************************************
 * @fn          nwk_nwkInit
 *
 * @brief       Initialize NWK conext.
 *
 * input parameters
 *
 * output parameters
 *
 * @return   Status of operation.
 */
smplStatus_t nwk_nwkInit(uint8_t (*f)(linkID_t))
{
  /* Truly ugly initialization because CCE won't initialize properly. Must
   * skip first const element. Yuk.
   */
  memset((((uint8_t *)&sPersistInfo)+1), 0x0, (sizeof(sPersistInfo)-1));
  /* OK. The zeroed elements are set. Now go back and do fixups...  */

  sPersistInfo.numConnections   = SYS_NUM_CONNECTIONS;
  sPersistInfo.curNextLinkPort  = SMPL_PORT_USER_MAX;
  sPersistInfo.curMaxReplyPort  = PORT_BASE_NUMBER;
  sPersistInfo.nextLinkID       = 1;

  /* initialize globals */
  nwk_globalsInit();

  /* initialize frame processing */
  nwk_frameInit(f);

  /* initialize queue manager */
  nwk_QInit();
	
  /* initialize each network application. */
  nwk_freqInit();
  nwk_pingInit();
  nwk_joinInit(f);
  nwk_mgmtInit();
  nwk_linkInit();
  nwk_securityInit();
#ifdef NWK_PLL
  nwk_PLLInit();
#endif

  /* set up the last connection as the broadcast port mapped to the broadcast Link ID */
  if (CONNSTATE_FREE == sPersistInfo.connStruct[NUM_CONNECTIONS].connState)
  {
    sPersistInfo.connStruct[NUM_CONNECTIONS].connState   = CONNSTATE_CONNECTED;
    sPersistInfo.connStruct[NUM_CONNECTIONS].hops2target = MAX_HOPS;
    sPersistInfo.connStruct[NUM_CONNECTIONS].portRx      = SMPL_PORT_USER_BCAST;
    sPersistInfo.connStruct[NUM_CONNECTIONS].portTx      = SMPL_PORT_USER_BCAST;
    sPersistInfo.connStruct[NUM_CONNECTIONS].thisLinkID  = SMPL_LINKID_USER_UUD;
    /* set peer address to broadcast so it is used when Application sends to the broadcast Link ID */
    memcpy(sPersistInfo.connStruct[NUM_CONNECTIONS].peerAddr, nwk_getBCastAddress(), NET_ADDR_SIZE);
  }

  return SMPL_SUCCESS;
}
Ejemplo n.º 4
0
static void broadcast_channel_change(uint8_t idx)
{
  ioctlRawSend_t send;
  uint8_t        msg[FREQ_REQ_MOVE_FRAME_SIZE];

  if (idx >= NWK_FREQ_TBL_SIZE)
  {
    return;
  }

  msg[FB_APP_INFO_OS] = FREQ_REQ_MOVE;
  msg[F_CHAN_OS]      = idx;

  send.addr = (addr_t *)nwk_getBCastAddress();
  send.msg  = msg;
  send.len  = sizeof(msg);
  send.port = SMPL_PORT_FREQ;

  SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &send);
}
Ejemplo n.º 5
0
/******************************************************************************
 * @fn          dispatchFrame
 *
 * @brief       Received frame looks OK so far. Dispatch to either NWK app by
 *              invoking the handler or the user's app by simply leaving the
 *              frame in the queue and letting the app poll the port.
 *
 * input parameters
 * @param   fiPtr    - frameInfo_t pointer to received frame
 *
 * output parameters
 *
 * @return   void
 */
static void dispatchFrame(frameInfo_t *fiPtr)
{
  uint8_t     port       = GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_PORT_OS);
  uint8_t     nwkAppSize = sizeof(func)/sizeof(func[0]);
  fhStatus_t  rc;
  linkID_t    lid;
#if defined(ACCESS_POINT)
  uint8_t loc;
#endif
#if !defined(END_DEVICE)
  uint8_t isForMe;
#endif

  /* be sure it's not an echo... */
  if (!memcmp(MRFI_P_SRC_ADDR(&fiPtr->mrfiPkt), sMyAddr, NET_ADDR_SIZE))
  {
    fiPtr->fi_usage = FI_AVAILABLE;
    return;
  }

  /* Make sure encyrption bit conforms to our security support context. */
#if defined(SMPL_SECURE)
  if (!(GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_ENCRYPT_OS)))
  {
    /* Encyrption bit is not on when when it should be */
    fiPtr->fi_usage = FI_AVAILABLE;
    return;
  }
#else
  if (GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_ENCRYPT_OS))
  {
    /* Encyrption bit is on when when it should not be */
    fiPtr->fi_usage = FI_AVAILABLE;
    return;
  }
#endif  /* SMPL_SECURE */

  /* If it's a network application port dispatch to service routine. Dispose
   * of frame depending on return code.
   */
  if (port && (port <= nwkAppSize))
  {
#if defined(SMPL_SECURE)
    /* Non-connection-based frame. We can decode here if it was encrypted */
    if (!nwk_getSecureFrame(&fiPtr->mrfiPkt, MRFI_GET_PAYLOAD_LEN(&fiPtr->mrfiPkt) - F_SEC_CTR_OS, 0))
    {
      fiPtr->fi_usage = FI_AVAILABLE;
      return;
    }
#endif
    rc = func[port-1](&fiPtr->mrfiPkt);
    if (FHS_KEEP == rc)
    {
      fiPtr->fi_usage = FI_INUSE_UNTIL_DEL;
    }
#if !defined(END_DEVICE)
    else if (FHS_REPLAY == rc)
    {
      /* an AP or an RE could be relaying a NWK application frame... */
      nwk_replayFrame(fiPtr);
    }
#endif
    else  /* rc == FHS_RELEASE (default...) */
    {
      fiPtr->fi_usage = FI_AVAILABLE;
    }
    return;
  }
  /* sanity check */
  else if ((port != SMPL_PORT_USER_BCAST) && ((port < PORT_BASE_NUMBER) || (port > SMPL_PORT_STATIC_MAX)))
  {
    /* bogus port. drop frame */
    fiPtr->fi_usage = FI_AVAILABLE;
    return;
  }

  /* At this point we know the target is a user app. If this is an end device
   * and we got this far save the frame and we're done. If we're an AP there
   * are 3 cases: it's for us, it's for s store-and-forward client, or we need
   * to replay the frame. If we're and RE and the frame didn't come from an RE
   * and it's not for us, replay the frame.
   */

#if defined(END_DEVICE)
  /* If we're s polling end device we only accept application frames from
   * the AP. This prevents duplicate reception if we happen to be on when
   * a linked peer sends.
   */
#if defined(RX_POLLS)
  if (F_TX_DEVICE_ED != GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_TX_DEVICE))
  {
    if (nwk_isConnectionValid(&fiPtr->mrfiPkt, &lid))
    {
      fiPtr->fi_usage = FI_INUSE_UNTIL_DEL;
    }
    else
    {
      fiPtr->fi_usage = FI_AVAILABLE;
    }
  }
  else
  {
    fiPtr->fi_usage = FI_AVAILABLE;
  }
#else
  /* it's destined for a user app. */
  if (nwk_isConnectionValid(&fiPtr->mrfiPkt, &lid))
  {
    fiPtr->fi_usage = FI_INUSE_UNTIL_DEL;
    if (spCallback && spCallback(lid))
    {
      fiPtr->fi_usage = FI_AVAILABLE;
      return;
    }
  }
  else
  {
    fiPtr->fi_usage = FI_AVAILABLE;
  }
#endif  /* RX_POLLS */

#else   /* END_DEVICE */

  /* We have an issue if the frame is broadcast to the UUD port. The AP (or RE) must
   * handle this frame as if it were the target in case there is an application
   * running that is listening on that port. But if it's a broadcast it must also be
   * replayed. It isn't enough just to test for the UUD port because it could be a
   * directed frame to another device. We must check explicitly for broadcast
   * destination address.
   */
  isForMe = !memcmp(sMyAddr, MRFI_P_DST_ADDR(&fiPtr->mrfiPkt), NET_ADDR_SIZE);
  if (isForMe || ((port == SMPL_PORT_USER_BCAST) && !memcmp(nwk_getBCastAddress(), MRFI_P_DST_ADDR(&fiPtr->mrfiPkt), NET_ADDR_SIZE)))
  {
    /* The folllowing test will succeed for the UUD port regardless of the
     * source address.
     */
    if (nwk_isConnectionValid(&fiPtr->mrfiPkt, &lid))
    {
      /* If this is for the UUD port and we are here then the device is either
       * an AP or an RE. In either case it must replay the UUD port frame if the
       * frame is not "for me". But it also must handle it since it could have a
       * UUD-listening application. Do the reply first and let the subsequent code
       * correctly set the frame usage state. Note that the routine return can be
       * from this code block. If not it will drop through to the bottom without
       * doing a replay.
       */
      /* Do I need to replay it? */
      if (!isForMe)
      {
        /* must be a broadcast for the UUD port */
        nwk_replayFrame(fiPtr);
      }
      /* OK. Now I handle it... */
      fiPtr->fi_usage = FI_INUSE_UNTIL_DEL;
      if (spCallback && spCallback(lid))
      {
        fiPtr->fi_usage = FI_AVAILABLE;
        return;
      }
    }
    else
    {
      fiPtr->fi_usage = FI_AVAILABLE;
    }
  }
#if defined( ACCESS_POINT )
  /* Check to see if we need to save this for a S and F client. Otherwise,
   * if it's not for us, get rid of it.
   */
  else if (nwk_isSandFClient(MRFI_P_DST_ADDR(&fiPtr->mrfiPkt), &loc))
  {
    /* Don't bother if it is a duplicate frame or if it's a forwarded frame
     * echoed back from an RE.
     */
    if (!isDupSandFFrame(&fiPtr->mrfiPkt) &&
        !(GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_FWD_FRAME))
       )
    {
#if defined(APP_AUTO_ACK)
      /* Make sure ack request bit is off. Sender will have gone away. */
      PUT_INTO_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_ACK_REQ, 0);
#endif
      fiPtr->fi_usage = FI_INUSE_UNTIL_FWD;
    }
    else
    {
      fiPtr->fi_usage = FI_AVAILABLE;
    }
  }
  else if (GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_TX_DEVICE) == F_TX_DEVICE_AP)
  {
    /* I'm an AP and this frame came from an AP. Don't replay. */
    fiPtr->fi_usage = FI_AVAILABLE;
  }
#elif defined( RANGE_EXTENDER )
  else if (GET_FROM_FRAME(MRFI_P_PAYLOAD(&fiPtr->mrfiPkt), F_TX_DEVICE) == F_TX_DEVICE_RE)
  {
    /* I'm an RE and this frame came from an RE. Don't replay. */
    fiPtr->fi_usage = FI_AVAILABLE;//////ojooooo descomentar!!!
	//nwk_replayFrame(fiPtr);//ojoooooooooo borrar!!!
  }
#endif
  else
  {
    /* It's not for me and I'm either an AP or I'm an RE and the frame
     * didn't come from an RE. Replay the frame.
     */
    nwk_replayFrame(fiPtr);
  }
#endif  /* !END_DEVICE */
  return;
}
Ejemplo n.º 6
0
/******************************************************************************
 * @fn          nwk_scanForChannels
 *
 * @brief       Scan for channels by sending a ping frame on each channel in the
 *              channel table and listen for a reply.
 *
 * input parameters
 * @param  channels    - pointer to area to receive list of channels from which
 *                       ping replies were received.
 *
 * output parameters
 * @param   channels   - populated list of channels.
 *
 * @return   statuis of operation..
 */
uint8_t nwk_scanForChannels(freqEntry_t *channels)
{
  uint8_t      msg[FREQ_REQ_PING_FRAME_SIZE], i, num=0, notBcast = 1;
  addr_t      *apAddr, retAddr;
  uint8_t      radioState = MRFI_GetRadioState();
  freqEntry_t  chan;
  freqEntry_t  curChan;

  union
  {
    ioctlRawSend_t    send;
    ioctlRawReceive_t recv;
  } ioctl_info;

  nwk_getChannel(&curChan);

  /* send to AP. If we don't know AP address, broadcast. */
  apAddr = (addr_t *)nwk_getAPAddress();
  if (!apAddr)
  {
    apAddr = (addr_t *)nwk_getBCastAddress();
    notBcast = 0;
  }

  for (i=0; i<NWK_FREQ_TBL_SIZE; ++i)
  {
    chan.logicalChan = i;

    nwk_setChannel(&chan);

    ioctl_info.send.addr = apAddr;
    ioctl_info.send.msg  = msg;
    ioctl_info.send.len  = sizeof(msg);
    ioctl_info.send.port = SMPL_PORT_FREQ;

    msg[FB_APP_INFO_OS] = FREQ_REQ_PING;
    msg[FB_TID_OS]      = sTid;

    SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &ioctl_info.send);

    ioctl_info.recv.port = SMPL_PORT_FREQ;
    ioctl_info.recv.msg  = msg;
    ioctl_info.recv.addr = &retAddr;

    NWK_CHECK_FOR_SETRX(radioState);
    NWK_REPLY_DELAY();
    NWK_CHECK_FOR_RESTORE_STATE(radioState);

    if (SMPL_SUCCESS == SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_READ, &ioctl_info.recv))
    {
      /* Once we know the Access Point we're related to we only accept
       * ping replies from that one.
       */
      if (!notBcast || (notBcast && !memcmp(&retAddr, apAddr, NET_ADDR_SIZE)))
      {
        channels[num++].logicalChan = i;
      }
    }

    sTid++;
    if (num && notBcast)
    {
      /* we're done...only one possible channel if we know the AP address. */
      break;
    }
    /* TODO: process encryption stuff */
  }

  /* reset original channel */
  nwk_setChannel(&curChan);

  return num;
}
Ejemplo n.º 7
0
smplStatus_t nwk_link(linkID_t *lid)
{
    uint8_t msg[LINK_FRAME_SIZE];
    connInfo_t   *pCInfo = nwk_getNextConnection();
    smplStatus_t rc;

    if (pCInfo)
    {
        addr_t addr;
        union
        {
            ioctlRawSend_t send;
            ioctlRawReceive_t recv;
        } ioctl_info;

        if (!nwk_allocateLocalRxPort(LINK_SEND, pCInfo))
        {
            nwk_freeConnection(pCInfo);
            return SMPL_NOMEM;
        }

        memcpy(addr.addr, nwk_getBCastAddress(), NET_ADDR_SIZE);
        ioctl_info.send.addr = &addr;
        ioctl_info.send.msg  = msg;
        ioctl_info.send.len  = sizeof(msg);
        ioctl_info.send.port = SMPL_PORT_LINK;

        /* Put link token in */
        nwk_putNumObjectIntoMsg((void *)&sLinkToken, msg + L_LINK_TOKEN_OS, sizeof(sLinkToken));

        /* set port to which the remote device should send */
        msg[L_RMT_PORT_OS] = pCInfo->portRx;

        /* set the transaction ID. this allows target to figure out duplicates */
        msg[LB_TID_OS] = sTid;

        /* set my Rx type */
        msg[L_MY_RXTYPE_OS] = nwk_getMyRxType();

        /* set request byte */
        msg[LB_REQ_OS] = LINK_REQ_LINK;

        /* protocol version number */
        msg[L_PROTOCOL_VERSION_OS] = nwk_getProtocolVersion();

#if defined(SMPL_SECURE)
        pCInfo->connTxCTR = MRFI_RandomByte()                   | \
            ((uint32_t)(MRFI_RandomByte()) << 8)  | \
            ((uint32_t)(MRFI_RandomByte()) << 16) | \
            ((uint32_t)(MRFI_RandomByte()) << 24);

        nwk_putNumObjectIntoMsg((void *)&pCInfo->connTxCTR, (void *)&msg[L_CTR_OS], 4);
#endif


        if (SMPL_SUCCESS != (rc = SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &ioctl_info.send)))
        {
            return rc;
        }

        {
            uint8_t radioState = MRFI_GetRadioState();

            ioctl_info.recv.port = SMPL_PORT_LINK;
            ioctl_info.recv.msg  = msg;
            ioctl_info.recv.addr = (addr_t *)pCInfo->peerAddr;

            NWK_CHECK_FOR_SETRX(radioState);
            NWK_REPLY_DELAY();
            NWK_CHECK_FOR_RESTORE_STATE(radioState);

            if (SMPL_SUCCESS == SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_READ, &ioctl_info.recv))
            {
                uint8_t firstByte = msg[LB_REQ_OS] & (~NWK_APP_REPLY_BIT);

                /* Sanity check for correct reply frame. Older version
                 * has the length instead of the request as the first byte.
                 */
                if ((firstByte != LINK_REQ_LINK) &&
                    (firstByte != LINK_REPLY_LEGACY_MSG_LENGTH)
                    )
                {
                    /* invalidate connection object */
                    nwk_freeConnection(pCInfo);
                    return SMPL_NO_LINK;

                }
            }
            else
            {
                /* no successful receive */
                nwk_freeConnection(pCInfo);
                return SMPL_TIMEOUT;
            }

            pCInfo->connState = CONNSTATE_CONNECTED;
            pCInfo->portTx    = msg[LR_RMT_PORT_OS]; /* link reply returns remote port */
            *lid              = pCInfo->thisLinkID;  /* return our local port number */

            /* Set hop count. If it's a polling device set the count to the
             * distance to the AP. Otherwise, set it to the max less the remaining
             * which will be the path taken for this frame. It will be no worse
             * then tha max and probably will be better.
             */
            if (F_RX_TYPE_POLLS == msg[LR_MY_RXTYPE_OS])
            {
                pCInfo->hops2target = MAX_HOPS_FROM_AP;
            }
            else
            {
                /* Can't really use this trick because the device could move. If the
                 * devices are all static this may work unless the initial reception
                 * was marginal.
                 */
#if defined(DEVICE_DOES_NOT_MOVE)
                pCInfo->hops2target = MAX_HOPS - ioctl_info.recv.hopCount;
#else
                pCInfo->hops2target = MAX_HOPS;
#endif
            }

#if defined(SMPL_SECURE)
            nwk_getNumObjectFromMsg((void *)&msg[LR_CTR_OS], (void *)&pCInfo->connRxCTR, 4);
#endif
        }

        /* guard against duplicates... */
        ++sTid;
        if (!sTid)
        {
            sTid = 1;
        }
        return SMPL_SUCCESS;
    }

    return SMPL_NOMEM;
}
Ejemplo n.º 8
0
/******************************************************************************
 * @fn          nwk_join
 *
 * @brief       Join functioanlity for non-AP devices. Send the Join token
 *              and wait for the reply.
 *
 * input parameters
 *
 * output parameters
 *
 * @return   Status of operation.
 */
smplStatus_t nwk_join(void)
{
  uint8_t      msg[JOIN_FRAME_SIZE];
  uint32_t     linkToken;
  addr_t       apAddr;
  uint8_t      radioState = MRFI_GetRadioState();
  smplStatus_t rc = SMPL_NO_JOIN;
  union
  {
    ioctlRawSend_t    send;
    ioctlRawReceive_t recv;
  } ioctl_info;

#if defined( FREQUENCY_AGILITY )
  uint8_t  i, numChan;
  freqEntry_t channels[NWK_FREQ_TBL_SIZE];

  if (!(numChan=nwk_scanForChannels(channels)))
  {
    return SMPL_NO_CHANNEL;
  }

  for (i=0; i<numChan; ++i)
  {
    nwk_setChannel(&channels[i]);
#else
  {
#endif

    ioctl_info.send.addr = (addr_t *)nwk_getBCastAddress();
    ioctl_info.send.msg  = msg;
    ioctl_info.send.len  = sizeof(msg);
    ioctl_info.send.port = SMPL_PORT_JOIN;

    /* Put join token in */
    nwk_putNumObjectIntoMsg((void *)&sJoinToken, msg+J_JOIN_TOKEN_OS, sizeof(sJoinToken));
    /* set app info byte */
    msg[JB_REQ_OS] = JOIN_REQ_JOIN;
    msg[JB_TID_OS] = sTid;
    /* Set number of connections supported. Used only by AP if it is
     * a data hub.
     */
    msg[J_NUMCONN_OS] = NUM_CONNECTIONS;
    /* protocol version number */
    msg[J_PROTOCOL_VERSION_OS] = nwk_getProtocolVersion();

    SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_WRITE, &ioctl_info.send);

    ioctl_info.recv.port = SMPL_PORT_JOIN;
    ioctl_info.recv.msg  = msg;
    ioctl_info.recv.addr = &apAddr;    /* save AP address from reply */

    NWK_CHECK_FOR_SETRX(radioState);
    NWK_REPLY_DELAY();
    NWK_CHECK_FOR_RESTORE_STATE(radioState);

    if (SMPL_SUCCESS == SMPL_Ioctl(IOCTL_OBJ_RAW_IO, IOCTL_ACT_READ, &ioctl_info.recv))
    {
      uint8_t firstByte = msg[JB_REQ_OS] & (~NWK_APP_REPLY_BIT);

      /* Sanity check for correct reply frame. Older version
       * has the length instead of the request as the first byte.
       */
      if ((firstByte == JOIN_REQ_JOIN) ||
          (firstByte == JOIN_REPLY_LEGACY_MSG_LENGTH)
         )
      {
        /* join reply returns link token */
        memcpy(&linkToken, msg+JR_LINK_TOKEN_OS, sizeof(linkToken));

        nwk_setLinkToken(linkToken);
        /* save AP address */
        nwk_setAPAddress(&apAddr);
        sTid++;   /* guard against duplicates */
        rc = SMPL_SUCCESS;
#if defined( FREQUENCY_AGILITY )
        break;
#endif
      }
    }
    /* TODO: process encryption stuff */
  }

  return rc;

}

#endif /* ACCESS_POINT */

/******************************************************************************
 * @fn          nwk_processJoin
 *
 * @brief       Processes a Join frame. If this is a reply let it go to the
 *              application. Otherwise generate and send the reply.
 *
 * input parameters
 * @param   frame     - Pointer to Join frame
 *
 * output parameters
 *
 * @return   Keep frame for application, release frame, or replay frame.
 */
fhStatus_t nwk_processJoin(mrfiPacket_t *frame)
{
  fhStatus_t rc = FHS_RELEASE;
  uint8_t    replyType;

  /* Make sure this is a reply and see if we sent this. Validate the
   * packet for reception by client app.
   */
  if (SMPL_MY_REPLY == (replyType=nwk_isValidReply(frame, sTid, JB_REQ_OS, JB_TID_OS)))
  {
    /* It's a match and it's a reply. Validate the received packet by
     * returning a 1 so it can be received by the client app.
     */
    MRFI_PostKillSem();
    rc = FHS_KEEP;
  }
#if defined(ACCESS_POINT)
  else if (SMPL_A_REPLY == replyType)
  {
    /* No match. If I'm not an ED this is a reply that should be passed on. */
    rc = FHS_REPLAY;
  }
  else
  {
    /* Send reply if we're an Access Point otherwise ignore the frame. */
    if ((SMPL_NOT_REPLY == replyType) && sJoinOK)
    {
      handleJoinRequest(frame);
    }
  }
#elif defined(RANGE_EXTENDER)
  else
  {