예제 #1
0
/*=================================================================================================
 * @fn          rxStartIsr
 *
 * @brief       First ISR state for receiving a packet - compute packet length, allocate
 *              buffer, initialize buffer.  Acknowledgements are handled immediately without
 *              allocating a buffer.
 *
 * @param       none
 *
 * @return      none
 *=================================================================================================
 */
static void rxStartIsr(void)
{
  uint8  addrLen;
  uint8  ackWithPending;
  uint8  dstAddrMode;
  uint8  srcAddrMode;
  uint8  mhrLen = 0;

  MAC_ASSERT(!macRxActive); /* receive on top of receive */

  /* indicate rx is active */
  macRxActive = MAC_RX_ACTIVE_STARTED;

  /*
   *  For bullet proof functionality, need to see if the receiver was just turned off.
   *  The logic to request turning off the receiver, disables interrupts and then checks
   *  the value of macRxActive.  If it is TRUE, the receiver will not be turned off.
   *
   *  There is a small hole though.  It's possible to attempt turning off the receiver
   *  in the window from when the receive interrupt fires and the point where macRxActive
   *  is set to TRUE.  To plug this hole, the on/off status must be tested *after*
   *  macRxActive has been set.  If the receiver is off at this point, there is nothing
   *  in the RX fifo and the receive is simply aborted.
   *
   *  Also, there are some considerations in case a hard disable just happened.  Usually,
   *  the receiver will just be off at this point after a hard disable.  The check described
   *  above will account for this case too.  However, if a hard disable were immediately
   *  followed by an enable, the receiver would be on.  To catch this case, the receive
   *  FIFO is also tested to see if it is empty.  Recovery is identical to the other cases.
   */
  if (!macRxOnFlag || MAC_RADIO_RX_FIFO_IS_EMPTY())
  {
    /* reset active flag */
    macRxActive = MAC_RX_ACTIVE_NO_ACTIVITY;

    /*
     *  To be absolutely bulletproof, must make sure no transmit queue'ed up during
     *  the tiny, tiny window when macRxActive was not zero.
     */
    rxPostRxUpdates();

    /* return immediately from here */
    return;
  }

  /*
   *  If interrupts are held off for too long it's possible the previous "transmit done"
   *  callback is pending.  If this is the case, it needs to be completed before
   *  continuing with the receive logic.
   */
  MAC_RADIO_FORCE_TX_DONE_IF_PENDING();

  /*
   *  It's possible receive logic is still waiting for confirmation of an ACK that went out
   *  for the previous receive.  This is OK but the callback needs to be canceled at this point.
   *  That callback execute receive cleanup logic that will run at the completion
   *  of *this* receive.  Also, it is important the flag for the outgoing ACK to be cleared.
   */
  MAC_RADIO_CANCEL_ACK_TX_DONE_CALLBACK();
  macRxOutgoingAckFlag = 0;

  /*
   *  Make a module-local copy of macRxFilter.  This prevents the selected
   *  filter from changing in the middle of a receive.
   */
  rxFilter = macRxFilter;

  /*-------------------------------------------------------------------------------
   *  Read initial frame information from FIFO.
   *
   *   This code is not triggered until the following are in the RX FIFO:
   *     frame length          - one byte containing length of MAC frame (excludes this field)
   *     frame control field   - two bytes defining frame type, addressing fields, control flags
   *     sequence number       - one byte unique sequence identifier
   *     additional two bytes  - these bytes are available in case the received frame is an ACK,
   *                             if so, the frame can be verified and responded to immediately,
   *                             if not an ACK, these bytes will be processed normally
   */

  /* read frame length, frame control field, and sequence number from FIFO */
  MAC_RADIO_READ_RX_FIFO(rxBuf, MAC_PHY_PHR_LEN + MAC_FCF_FIELD_LEN + MAC_SEQ_NUM_FIELD_LEN);

  /* bytes to read from FIFO equals frame length minus length of MHR fields just read from FIFO */
  rxUnreadLen = (rxBuf[0] & PHY_PACKET_SIZE_MASK) - MAC_FCF_FIELD_LEN - MAC_SEQ_NUM_FIELD_LEN;

  /*
   *  Workaround for chip bug #1547.  The receive buffer can sometimes be corrupted by hardware.
   *  This usually occurs under heavy traffic.  If a corrupted receive buffer is detected
   *  the entire receive buffer is flushed.
   */
  if ((rxUnreadLen > (MAC_A_MAX_PHY_PACKET_SIZE - MAC_FCF_FIELD_LEN - MAC_SEQ_NUM_FIELD_LEN)) ||
      (MAC_FRAME_TYPE(&rxBuf[1]) > MAC_FRAME_TYPE_MAX_VALID))
  {
    MAC_RADIO_FLUSH_RX_FIFO();
    rxDone();
    return;
  }


  /*-------------------------------------------------------------------------------
   *  Process ACKs.
   *
   *  If this frame is an ACK, process it immediately and exit from here.
   *  If this frame is not an ACK and transmit is listening for an ACK, let
   *  the transmit logic know an non-ACK was received so transmit can complete.
   *
   *  In promiscuous mode ACKs are treated like any other frame.
   */
  if ((MAC_FRAME_TYPE(&rxBuf[1]) == MAC_FRAME_TYPE_ACK) && (rxPromiscuousMode == PROMISCUOUS_MODE_OFF))
  {
    halIntState_t  s;
    uint8 fcsBuf[MAC_FCF_FIELD_LEN];
    /*
     *  There are guaranteed to be two unread bytes in the FIFO.  By defintion, for ACK frames
     *  these two bytes will be the FCS.
     */

    /* read FCS from FIFO (threshold set so bytes are guaranteed to be there) */
    MAC_RADIO_READ_RX_FIFO(fcsBuf, MAC_FCS_FIELD_LEN);

    /*
     *  This critical section ensures that the ACK timeout won't be triggered in the
     *  middle of receiving the ACK frame.
     */
    HAL_ENTER_CRITICAL_SECTION(s);

    /* see if transmit is listening for an ACK */
    if (macTxActive == MAC_TX_ACTIVE_LISTEN_FOR_ACK)
    {
      MAC_ASSERT(pMacDataTx != NULL); /* transmit buffer must be present */

      /* record link quality metrics for the receive ACK */
      {
        int8 rssiDbm;
        uint8 corr;

        rssiDbm = PROPRIETARY_FCS_RSSI(fcsBuf) + MAC_RADIO_RSSI_OFFSET;
        MAC_RADIO_RSSI_LNA_OFFSET(rssiDbm);
        corr = PROPRIETARY_FCS_CORRELATION_VALUE(fcsBuf);

        pMacDataTx->internal.mpduLinkQuality = macRadioComputeLQI(rssiDbm, corr);
        pMacDataTx->internal.correlation = corr;
        pMacDataTx->internal.rssi= rssiDbm;
      }

      /*
       *  It's okay if the ACK timeout is triggered here. The callbacks for ACK received
       *  or ACK not received will check "macTxActive" flag before taking any actions.
       */
      HAL_EXIT_CRITICAL_SECTION(s);

      /*
       *  An ACK was received so transmit logic needs to know.  If the FCS failed,
       *  the transmit logic still needs to know.  In that case, treat the frame
       *  as a non-ACK to complete the active transmit.
       */
      if (PROPRIETARY_FCS_CRC_OK(fcsBuf))
      {
        /* call transmit logic to indicate ACK was received */
        macTxAckReceivedCallback(MAC_SEQ_NUMBER(&rxBuf[1]), MAC_FRAME_PENDING(&rxBuf[1]));
      }
      else
      {
        macTxAckNotReceivedCallback();
      }
    }
    else
    {
      HAL_EXIT_CRITICAL_SECTION(s);
    }

    /* receive is done, exit from here */
    rxDone();
    return;
  }
  else if (macTxActive == MAC_TX_ACTIVE_LISTEN_FOR_ACK)
  {
    macTxAckNotReceivedCallback();
  }

  /*-------------------------------------------------------------------------------
   *  Apply filtering.
   *
   *  For efficiency, see if filtering is even 'on' before processing.  Also test
   *  to make sure promiscuous mode is disabled.  If promiscuous mode is enabled,
   *  do not apply filtering.
   */
  if ((rxFilter != RX_FILTER_OFF) && !rxPromiscuousMode)
  {
    if (/* filter all frames */
         (rxFilter == RX_FILTER_ALL) ||

         /* filter non-beacon frames */
         ((rxFilter == RX_FILTER_NON_BEACON_FRAMES) &&
          (MAC_FRAME_TYPE(&rxBuf[1]) != MAC_FRAME_TYPE_BEACON)) ||

         /* filter non-command frames */
         ((rxFilter == RX_FILTER_NON_COMMAND_FRAMES) &&
          ((MAC_FRAME_TYPE(&rxBuf[1]) != MAC_FRAME_TYPE_COMMAND))))
    {
      /* discard rest of frame */
      rxDiscardFrame();
      return;
    }
  }

  /*-------------------------------------------------------------------------------
   *  Compute length of addressing fields.  Compute payload length.
   */

  /* decode addressing modes */
  dstAddrMode = MAC_DEST_ADDR_MODE(&rxBuf[1]);
  srcAddrMode = MAC_SRC_ADDR_MODE(&rxBuf[1]);

  /*
  *  Workaround for chip bug #1547.  The receive buffer can sometimes be corrupted by hardware.
   *  This usually occurs under heavy traffic.  If a corrupted receive buffer is detected
   *  the entire receive buffer is flushed.
   */
  if (macChipVersion == REV_A)
  {
    if ((srcAddrMode == ADDR_MODE_RESERVERED) || (dstAddrMode == ADDR_MODE_RESERVERED))
    {
      MAC_RADIO_FLUSH_RX_FIFO();
      rxDone();
      return;
    }
  }

  /*
   *  Compute the addressing field length.  A lookup table based on addressing
   *  mode is used for efficiency.  If the source address is present and the
   *  frame is intra-PAN, the PAN Id is not repeated.  In this case, the address
   *  length is adjusted to match the smaller length.
   */
  addrLen = macRxAddrLen[dstAddrMode] + macRxAddrLen[srcAddrMode];
  if ((srcAddrMode != SADDR_MODE_NONE) && MAC_INTRA_PAN(&rxBuf[1]))
  {
    addrLen -= MAC_PAN_ID_FIELD_LEN;
  }

  /*
   *  If there are not enough unread bytes to include the computed address
   *  plus FCS field, the frame is corrupted and must be discarded.
   */
  if ((addrLen + MAC_FCS_FIELD_LEN) > rxUnreadLen)
  {
    /* discard frame and exit */
    rxDiscardFrame();
    return;
  }

  /* aux security header plus payload length is equal to unread bytes minus
   * address length, minus the FCS
   */
  rxPayloadLen = rxUnreadLen - addrLen - MAC_FCS_FIELD_LEN;

  /*-------------------------------------------------------------------------------
   *  Allocate memory for the incoming frame.
   */
  if (MAC_SEC_ENABLED(&rxBuf[1]))
  {
    /* increase the allocation size of MAC header for security */
    mhrLen = MAC_MHR_LEN;
  }

  pRxBuf = (macRx_t *) MEM_ALLOC(sizeof(macRx_t) + mhrLen + rxPayloadLen);
  if (pRxBuf == NULL)
  {
    /* Cancel the outgoing TX ACK */
    MAC_RADIO_CANCEL_TX_ACK();

    /* buffer allocation failed, discard the frame and exit*/
    rxDiscardFrame();
    return;
  }

  /*-------------------------------------------------------------------------------
   *  Set up to process ACK request.  Do not ACK if in promiscuous mode.
   */
  ackWithPending = 0;
  if (!rxPromiscuousMode)
  {
    macRxOutgoingAckFlag = MAC_ACK_REQUEST(&rxBuf[1]);
  }

  /*-------------------------------------------------------------------------------
   *  Process any ACK request.
   */
  if (macRxOutgoingAckFlag)
  {
    halIntState_t  s;

    /*
     *  This critical section ensures that the callback ISR is initiated within time
     *  to guarantee correlation with the strobe.
     */
    HAL_ENTER_CRITICAL_SECTION(s);

    /* Do not ack data packet with pending more data */
    if( MAC_FRAME_TYPE(&rxBuf[1]) == MAC_FRAME_TYPE_COMMAND )
    {
      if( macRxCheckMACPendingCallback())
      {
        /* Check is any mac data pending for end devices */
        ackWithPending = MAC_RX_FLAG_ACK_PENDING;
      }
      else
      {
        if( macSrcMatchIsEnabled )
        {
          /* When autopend is enabled, check if allpending is set to true */
          if( MAC_SrcMatchCheckAllPending() == MAC_AUTOACK_PENDING_ALL_ON )
          {
            ackWithPending = MAC_RX_FLAG_ACK_PENDING;
          }
        }
        else
        {
          /* When autopend is disabled, check the application pending callback */
          if( macRxCheckPendingCallback() )
          {
            ackWithPending = MAC_RX_FLAG_ACK_PENDING;
          }
        }
      }
    }

    if( ackWithPending == MAC_RX_FLAG_ACK_PENDING )
    {
      MAC_RADIO_TX_ACK_PEND();
    }
    else
    {
      MAC_RADIO_TX_ACK();
    }


    /* request a callback to macRxAckTxDoneCallback() when the ACK transmit has finished */
    MAC_RADIO_REQUEST_ACK_TX_DONE_CALLBACK();
    HAL_EXIT_CRITICAL_SECTION(s);
  }

 /*-------------------------------------------------------------------------------
  *  Populate the receive buffer going up to high-level.
  */

  /* configure the payload buffer
   * save MAC header pointer regardless of security status.
   */
  pRxBuf->mhr.p   = pRxBuf->msdu.p   = (uint8 *) (pRxBuf + 1);
  pRxBuf->mhr.len = pRxBuf->msdu.len =  rxPayloadLen;

  if (MAC_SEC_ENABLED(&rxBuf[1]))
  {
    /* Copy FCF and sequence number to RX buffer */
    pRxBuf->mhr.len = MAC_FCF_FIELD_LEN + MAC_SEQ_NUM_FIELD_LEN;
    osal_memcpy(pRxBuf->mhr.p, &rxBuf[1], pRxBuf->mhr.len);
    pRxBuf->mhr.p += pRxBuf->mhr.len;
  }

  /* set internal values */
  pRxBuf->mac.srcAddr.addrMode  = srcAddrMode;
  pRxBuf->mac.dstAddr.addrMode  = dstAddrMode;
  pRxBuf->mac.timestamp         = MAC_RADIO_BACKOFF_CAPTURE();
  pRxBuf->mac.timestamp2        = MAC_RADIO_TIMER_CAPTURE();

  /* Special Case for Enhanced Beacon Request which has a different
   * frame version
   */
#ifdef FEATURE_ENHANCED_BEACON
  if( MAC_FRAME_VERSION(&rxBuf[1]) == 2 )
  {
      pRxBuf->internal.frameType  = MAC_FRAME_TYPE_INTERNAL_MAC_VERSION_E | \
                                  MAC_FRAME_TYPE(&rxBuf[1]);
  }
  else
#endif
  {
    pRxBuf->internal.frameType  = MAC_FRAME_TYPE(&rxBuf[1]);
  }

  pRxBuf->mac.dsn               = MAC_SEQ_NUMBER(&rxBuf[1]);
  pRxBuf->internal.flags        = INTERNAL_FCF_FLAGS(&rxBuf[1]) | ackWithPending;

  /*-------------------------------------------------------------------------------
   *  If the processing the addressing fields does not require more bytes from
   *  the FIFO go directly address processing function.  Otherwise, configure
   *  interrupt to jump there once bytes are received.
   */
  if (addrLen == 0)
  {
    /* no addressing fields to read, prepare for payload interrupts */
    pFuncRxState = &rxPayloadIsr;
    rxPrepPayload();
  }
  else
  {
    /* need to read and process addressing fields, prepare for address interrupt */
    rxNextLen = addrLen;
    if (MAC_SEC_ENABLED(&rxBuf[1]))
    {
      /* When security is enabled, read off security control field as well */
      MAC_RADIO_SET_RX_THRESHOLD(rxNextLen + MAC_SEC_CONTROL_FIELD_LEN);
    }
    else
    {
      MAC_RADIO_SET_RX_THRESHOLD(rxNextLen);
    }
    pFuncRxState = &rxAddrIsr;
  }
}
예제 #2
0
파일: mac_tx.c 프로젝트: LILCMU/WRATIOT
/**************************************************************************************************
 * @fn          macTxFrame
 *
 * @brief       Transmit the frame pointed to by pMacDataTx with the specified type.
 *              NOTE! It is not legal to call this function from interrupt context.
 *
 * @param       txType - type of transmit
 *
 * @return      none
 **************************************************************************************************
 */
MAC_INTERNAL_API void macTxFrame(uint8 txType)
{
  MAC_ASSERT(!macTxActive);            /* transmit on top of transmit */

  /* mark transmit as active */
  macTxActive = MAC_TX_ACTIVE_INITIALIZE;

  /*
   *  The MAC will not enter sleep mode if there is an active transmit.  However, if macSleep() is
   *  ever called from interrupt context, it possible to enter sleep state after a transmit is
   *  intiated but before macTxActive is set.  To recover from this, the transmit must be aborted
   *  and proper notificiation given to high-level.
   */
  if (macSleepState != MAC_SLEEP_STATE_AWAKE)
  {
    /* notify high-level that transmit had to be aborted */
    txComplete(MAC_TX_ABORTED);

    /* exit from transmit logic */
    return;
  }

  /* save transmit type */
  macTxType = txType;

  /*-------------------------------------------------------------------------------
   *  Prepare for transmit.
   */
  if (macTxType == MAC_TX_TYPE_SLOTTED)
  {
    MAC_RADIO_TX_PREP_SLOTTED();
  }

#ifdef FEATURE_GREEN_POWER
  else if (macTxType == MAC_TX_TYPE_GREEN_POWER)
  {
    txGreenPowerPrep();
  }
#endif /* #ifdef FEATURE_GREEN_POWER */

  else
  {
    MAC_ASSERT((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) || (macTxType == MAC_TX_TYPE_UNSLOTTED_CSMA));

    nb = 0;
    macTxBe = (pMacDataTx->internal.txOptions & MAC_TXOPTION_ALT_BE) ? pMacPib->altBe : pMacPib->minBe;

    if ((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) && (pMacPib->battLifeExt))
    {
      macTxBe = MIN(2, macTxBe);
    }

    txCsmaPrep();
  }

  /*-------------------------------------------------------------------------------
   *  Load transmit FIFO unless this is a retransmit.  No need to write
   *  the FIFO again in that case.
   */
  if (!txRetransmitFlag)
  {
    uint8 * p;
    uint8   lenMhrMsdu;

    MAC_ASSERT(pMacDataTx != NULL); /* must have data to transmit */

    /* save needed parameters */
    txAckReq = MAC_ACK_REQUEST(pMacDataTx->msdu.p);
    txSeqn   = MAC_SEQ_NUMBER(pMacDataTx->msdu.p);

    /* set length of frame (note: use of term msdu is a misnomer, here it's actually mhr + msdu) */
    lenMhrMsdu = pMacDataTx->msdu.len;

    /* calling code guarantees an unused prepended byte  */
    p = pMacDataTx->msdu.p - PREPENDED_BYTE_LEN;

    /* first byte of buffer is length of MPDU */
    *p = lenMhrMsdu + MFR_LEN;

    /*
     *  Flush the TX FIFO.  This is necessary in case the previous transmit was never
     *  actually sent (e.g. CSMA failed without strobing TXON).  If bytes are written to
     *  the FIFO but not transmitted, they remain in the FIFO to be transmitted whenever
     *  a strobe of TXON does happen.
     */
    MAC_RADIO_FLUSH_TX_FIFO();

    /* write bytes to FIFO, prepended byte is included, MFR is not (it's generated by hardware) */
    MAC_RADIO_WRITE_TX_FIFO(p, PREPENDED_BYTE_LEN + lenMhrMsdu);
  }

  /*-------------------------------------------------------------------------------
   *  If not receiving, start the transmit.  If receive is active
   *  queue up the transmit.
   *
   *  Critical sections around the state change prevents any sort of race condition
   *  with  macTxStartQueuedFrame().  This guarantees function txGo() will only be
   *  called once.
   */
  {
    halIntState_t  s;

    HAL_ENTER_CRITICAL_SECTION(s);
    if (!macRxActive && !macRxOutgoingAckFlag)
    {
      macTxActive = MAC_TX_ACTIVE_GO;
      HAL_EXIT_CRITICAL_SECTION(s);
      txGo();
    }
    else
    {
      macTxActive = MAC_TX_ACTIVE_QUEUED;
      HAL_EXIT_CRITICAL_SECTION(s);
    }
  }
}
예제 #3
0
/**************************************************************************************************
 * @fn          macTxFrame
 *
 * @brief       Transmit the frame pointed to by pMacDataTx with unslotted CSMA.
 *              NOTE! It is not legal to call this function from interrupt context.
 *
 * @param       none
 *
 * @return      none
 **************************************************************************************************
 */
void macTxFrame(uint8 txType)
{
  MAC_ASSERT(!macTxActive);                                 /* transmit on top of transmit */
  MAC_ASSERT(!macTxFrameInQueueFlag);                       /* already a queued transmit */
  MAC_ASSERT(macSleepState == MAC_SLEEP_STATE_AWAKE);       /* radio must be awake */

  /* initialize */
  macTxActive = TRUE;
  macTxType   = txType;

  /*-------------------------------------------------------------------------------
   *  Prepare for transmit.
   */
  if (macTxType == MAC_TX_TYPE_SLOTTED)
  {
    MAC_RADIO_TX_PREP_SLOTTED();
  }
  else
  {
    MAC_ASSERT((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) || (macTxType == MAC_TX_TYPE_UNSLOTTED_CSMA));

    nb = 0;
    macTxBe = (pMacDataTx->internal.txOptions & MAC_TXOPTION_ALT_BE) ? macPib.altBe : macPib.minBe;

    if ((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) && (macPib.battLifeExt))
    {
      macTxBe = MIN(2, macTxBe);
    }

    txCsmaPrep();
  }


  /*-------------------------------------------------------------------------------
   *  Load transmit FIFO unless this is a retransmit.  No need to write
   *  the FIFO again in that case.
   */
  if (!txRetransmitFlag)
  {
    uint8 * p;
    uint8   lenMhrMsdu;

    MAC_ASSERT(pMacDataTx != NULL); /* must have data to transmit */

    /* save needed parameters */
    txAckReq = MAC_ACK_REQUEST(pMacDataTx->msdu.p);
    txSeqn   = MAC_SEQ_NUMBER(pMacDataTx->msdu.p);

    /* set length of frame (note: use of term msdu is a misnomer, here it's actually mhr + msdu) */
    lenMhrMsdu = pMacDataTx->msdu.len;

    /* calling code guarantees an unused prepended byte  */
    p = pMacDataTx->msdu.p - PREPENDED_BYTE_LEN;

    /* first byte of buffer is length of MPDU */
    *p = lenMhrMsdu + MFR_LEN;

    /*
     *  Flush the TX FIFO.  This is necessary in case the previous transmit was never
     *  actually sent (e.g. CSMA failed without strobing TXON).  If bytes are written to
     *  the FIFO but not transmitted, they remain in the FIFO to be transmitted whenever
     *  a strobe of TXON does happen.
     */
    MAC_RADIO_FLUSH_TX_FIFO();

    /* write bytes to FIFO, prepended byte is included, MFR is not (it's generated by hardware) */
    MAC_RADIO_WRITE_TX_FIFO(p, PREPENDED_BYTE_LEN + lenMhrMsdu);
  }

  /*-------------------------------------------------------------------------------
   *  If not receiving, start the transmit.  If receive is active
   *  queue up the transmit.
   */
  {
    halIntState_t  s;

    HAL_ENTER_CRITICAL_SECTION(s);
    if (!macRxActive)
    {
      HAL_EXIT_CRITICAL_SECTION(s);
      if (macTxType == MAC_TX_TYPE_SLOTTED)
      {
        MAC_RADIO_TX_GO_SLOTTED();
      }
      else
      {
        txCsmaGo();
      }
    }
    else
    {
      macTxFrameInQueueFlag = TRUE;
      HAL_EXIT_CRITICAL_SECTION(s);
    }
  }
}