/************************************************************************************************** * @fn macTxDoneCallback * * @brief - * * @param - * * @return none ************************************************************************************************** */ void macTxDoneCallback(uint8 status) { if (status == MAC_TXDONE_SUCCESS) { /* see if ACK was requested */ if (!txAckReq) { /* ACK was not requested, transmit is complete */ txComplete(MAC_SUCCESS); } else { /* * ACK was requested - must wait to receive it. A timer is set * to expire after the timeout duration for waiting for an ACK. * If an ACK is received, the function macTxAckReceived() is called. * If an ACK is not received within the timeout period, * the function macTxAckTimeoutCallback() is called. */ macTxListenForAck = TRUE; MAC_RADIO_TX_REQUEST_ACK_TIMEOUT_CALLBACK(); } } else if (status == MAC_TXDONE_CHANNEL_BUSY) { MAC_ASSERT((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) || (macTxType == MAC_TX_TYPE_UNSLOTTED_CSMA)); /* clear channel assement failed, follow through with CSMA algorithm */ nb++; if (nb > macPib.maxCsmaBackoffs) { txComplete(MAC_CHANNEL_ACCESS_FAILURE); } else { macTxBe = MIN(macTxBe+1, macPib.maxBe); txCsmaPrep(); txCsmaGo(); } } else { MAC_ASSERT(status == MAC_TXDONE_INSUFFICIENT_TIME); txComplete(MAC_NO_TIME); } }
/************************************************************************************************** * @fn macTxAckReceivedCallback * * @brief This function is called by the receive logic when an ACK is received and * transmit logic is listening for an ACK. * * @param seqn - sequence number of received ACK * @param pendingFlag - set if pending flag of ACK is set, cleared otherwise * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macTxAckReceivedCallback(uint8 seqn, uint8 pendingFlag) { halIntState_t s; /* only process if listening for an ACK; critical section prevents race condition problems */ HAL_ENTER_CRITICAL_SECTION(s); if (macTxActive == MAC_TX_ACTIVE_LISTEN_FOR_ACK) { macTxActive = MAC_TX_ACTIVE_POST_ACK; MAC_RADIO_TX_CANCEL_ACK_TIMEOUT_CALLBACK(); HAL_EXIT_CRITICAL_SECTION(s); /* see if the sequence number of received ACK matches sequence number of packet just sent */ if (seqn == txSeqn) { /* * Sequence numbers match so transmit is successful. Return appropriate * status based on the pending flag of the received ACK. */ if (pendingFlag) { txComplete(MAC_ACK_PENDING); } else { txComplete(MAC_SUCCESS); } } else { /* sequence number did not match; per spec, transmit failed at this point */ txComplete(MAC_NO_ACK); } } else { HAL_EXIT_CRITICAL_SECTION(s); } }
/************************************************************************************************** * @fn macTxAckTimeoutCallback * * @brief This function is called if ACK is not received within timeout period. * * @param none * * @return none ************************************************************************************************** */ void macTxAckTimeoutCallback(void) { halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); if (macTxListenForAck) { macTxListenForAck = FALSE; HAL_EXIT_CRITICAL_SECTION(s); txComplete(MAC_NO_ACK); } HAL_EXIT_CRITICAL_SECTION(s); }
/************************************************************************************************** * @fn macTxAckReceived * * @brief Function called by interrupt routine when an ACK is received. * * @param seqn - sequence number of received ACK * @param pendingFlag - set to TRUE if pending flag of ACK is set, FALSE otherwise * * @return none ************************************************************************************************** */ void macTxAckReceived(uint8 seqn, uint8 pendingFlag) { halIntState_t s; /* only process the received ACK if the macTxListenForAck flag is set */ HAL_ENTER_CRITICAL_SECTION(s); if (macTxListenForAck) { macTxListenForAck = FALSE; HAL_EXIT_CRITICAL_SECTION(s); /* cancel the ACK callback */ MAC_RADIO_TX_CANCEL_ACK_TIMEOUT_CALLBACK(); /* see if the sequence number of received ACK matches sequence number of packet just sent */ if (seqn == txSeqn) { /* * Sequence numbers match so transmit is successful. Return appropriate * status based on the pending flag of the received ACK. */ if (pendingFlag) { txComplete(MAC_ACK_PENDING); } else { txComplete(MAC_SUCCESS); } } else { /* sequence number did not match; per spec, transmit failed at this point */ txComplete(MAC_NO_ACK); } } HAL_EXIT_CRITICAL_SECTION(s); }
/*================================================================================================= * @fn txCsmaGo * * @brief - * * @param none * * @return none *================================================================================================= */ static void txCsmaGo(void) { if (macTxType == MAC_TX_TYPE_SLOTTED_CSMA) { if (macTxCsmaBackoffDelay >= macDataTxTimeAvailable()) { txComplete(MAC_NO_TIME); return; } } MAC_RADIO_TX_GO_CSMA(); }
/************************************************************************************************** * @fn macTxDoneCallback * * @brief This callback is executed when transmit completes. * * @param none * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macTxDoneCallback(void) { halIntState_t s; /* * There is a small chance this function could be called twice for a single transmit. * To prevent logic from executing twice, the state variable macTxActive is used as * a gating mechanism to guarantee single time execution. */ HAL_ENTER_CRITICAL_SECTION(s); if (macTxActive == MAC_TX_ACTIVE_GO) { if (macRxActive) { /* RX was partly done just before TX. Reset the RX state. */ macTxCollisionWithRxCallback(); } /* see if ACK was requested */ if (!txAckReq) { macTxActive = MAC_TX_ACTIVE_DONE; HAL_EXIT_CRITICAL_SECTION(s); /* ACK was not requested, transmit is complete */ txComplete(MAC_SUCCESS); } else { /* * ACK was requested - must wait to receive it. A timer is set * to expire after the timeout duration for waiting for an ACK. * If an ACK is received, the function macTxAckReceived() is called. * If an ACK is not received within the timeout period, * the function macTxAckNotReceivedCallback() is called. */ macTxActive = MAC_TX_ACTIVE_LISTEN_FOR_ACK; MAC_RADIO_TX_REQUEST_ACK_TIMEOUT_CALLBACK(); HAL_EXIT_CRITICAL_SECTION(s); } } else { HAL_EXIT_CRITICAL_SECTION(s); } }
/************************************************************************************************** * @fn macTxAckNotReceivedCallback * * @brief This function is called by the receive logic when transmit is listening * for an ACK but something else is received. It is also called if the * listen-for-ACK timeout is reached. * * @brief * * @param none * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macTxAckNotReceivedCallback(void) { halIntState_t s; /* only process if listening for an ACK; critical section prevents race condition problems */ HAL_ENTER_CRITICAL_SECTION(s); if (macTxActive == MAC_TX_ACTIVE_LISTEN_FOR_ACK) { macTxActive = MAC_TX_ACTIVE_POST_ACK; MAC_RADIO_TX_CANCEL_ACK_TIMEOUT_CALLBACK(); HAL_EXIT_CRITICAL_SECTION(s); /* a non-ACK was received when expecting an ACK, per spec transmit is over at this point */ txComplete(MAC_NO_ACK); } else { HAL_EXIT_CRITICAL_SECTION(s); } }
/************************************************************************************************** * @fn macTxChannelBusyCallback * * @brief This callback is executed if a CSMA transmit was attempted but the channel * was busy. * * @param none * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macTxChannelBusyCallback(void) { MAC_ASSERT((macTxType == MAC_TX_TYPE_SLOTTED_CSMA) || (macTxType == MAC_TX_TYPE_UNSLOTTED_CSMA)); /* turn off receiver if allowed */ macTxActive = MAC_TX_ACTIVE_CHANNEL_BUSY; macRxOffRequest(); /* clear channel assement failed, follow through with CSMA algorithm */ nb++; if (nb > pMacPib->maxCsmaBackoffs) { txComplete(MAC_CHANNEL_ACCESS_FAILURE); } else { macTxBe = MIN(macTxBe+1, pMacPib->maxBe); txCsmaPrep(); macTxActive = MAC_TX_ACTIVE_GO; txCsmaGo(); } }
/************************************************************************************************** * @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); } } }