/*================================================================================================= * @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; } }
/************************************************************************************************** * @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); } } }
/************************************************************************************************** * @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); } } }