/************************************************************************************************** * @fn macRxOff * * @brief Turn off the receiver if it's not already off. * * @param none * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macRxOff(void) { halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); if (macRxOnFlag) { macRxOnFlag = 0; MAC_RADIO_RXTX_OFF(); MAC_DEBUG_TURN_OFF_RX_LED(); /* just in case a receive was about to start, flush the receive FIFO */ MAC_RADIO_FLUSH_RX_FIFO(); /* clear any receive interrupt that happened to squeak through */ MAC_RADIO_CLEAR_RX_THRESHOLD_INTERRUPT_FLAG(); } HAL_EXIT_CRITICAL_SECTION(s); }
/************************************************************************************************** * @fn macBackoffTimerCapture * * @brief Returns the most recently captured backoff count * * @param none * * @return last backoff count that was captured ************************************************************************************************** */ uint32 macBackoffTimerCapture(void) { halIntState_t s; uint32 backoffCapture; HAL_ENTER_CRITICAL_SECTION(s); backoffCapture = MAC_RADIO_BACKOFF_CAPTURE(); HAL_EXIT_CRITICAL_SECTION(s); #ifdef MAC_RADIO_FEATURE_HARDWARE_OVERFLOW_NO_ROLLOVER /* * See other instance of this #ifdef for detailed comments. * Those comments apply to the backoff capture value too. */ if (backoffCapture >= backoffTimerRollover) { return(0); } #endif return(backoffCapture); }
/********************************************************************* * @fn osal_pwrmgr_powerconserve * * @brief This function is called from the main OSAL loop when there are * no events scheduled and shouldn't be called from anywhere else. * * @param none. * * @return none. */ void osal_pwrmgr_powerconserve( void ){ uint32 next; halIntState_t intState; // Should we even look into power conservation if ( pwrmgr_attribute.pwrmgr_device != PWRMGR_ALWAYS_ON ) { // Are all tasks in agreement to conserve if ( pwrmgr_attribute.pwrmgr_task_state == 0 ) { // Hold off interrupts. HAL_ENTER_CRITICAL_SECTION( intState ); // Get next time-out next = osal_next_timeout(); // Re-enable interrupts. HAL_EXIT_CRITICAL_SECTION( intState ); // Put the processor into sleep mode OSAL_SET_CPU_INTO_SLEEP( next ); } } }
/************************************************************************************************** * @fn macMcuOverflowSetCount * * @brief Sets the value of the hardware overflow counter. * * @param count - new overflow count value * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macMcuOverflowSetCount(uint32 count) { halIntState_t s; MAC_ASSERT(! (count >> 24) ); /* illegal count value */ /* save the current overflow count */ accumulatedOverflowCount += macMcuOverflowCount(); /* deduct the initial count */ accumulatedOverflowCount -= count; HAL_ENTER_CRITICAL_SECTION(s); MAC_MCU_T2_ACCESS_OVF_COUNT_VALUE(); /* for efficiency, the 32-bit value is decoded using endian abstracted indexing */ /* T2OF2 must be written last */ T2MOVF0 = (uint32)((uint8 *)&count)[UINT32_NDX0]; T2MOVF1 = (uint32)((uint8 *)&count)[UINT32_NDX1]; T2MOVF2 = (uint32)((uint8 *)&count)[UINT32_NDX2]; HAL_EXIT_CRITICAL_SECTION(s); }
/************************************************************************************************** * @fn macMcuOverflowCount * * @brief Returns the value of the overflow counter which is a special hardware feature. * The overflow count actually is 24 bits of information. * * @param none * * @return value of overflow counter ************************************************************************************************** */ MAC_INTERNAL_API uint32 macMcuOverflowCount(void) { uint32 overflowCount; halIntState_t s; /* for efficiency, the 32-bit value is encoded using endian abstracted indexing */ HAL_ENTER_CRITICAL_SECTION(s); /* This T2 access macro allows accessing both T2MOVFx and T2Mx */ MAC_MCU_T2_ACCESS_OVF_COUNT_VALUE(); /* Latch the entire T2MOVFx first by reading T2M0. */ T2M0; ((uint8 *)&overflowCount)[UINT32_NDX0] = T2MOVF0; ((uint8 *)&overflowCount)[UINT32_NDX1] = T2MOVF1; ((uint8 *)&overflowCount)[UINT32_NDX2] = T2MOVF2; ((uint8 *)&overflowCount)[UINT32_NDX3] = 0; HAL_EXIT_CRITICAL_SECTION(s); return (overflowCount); }
/********************************************************************* * @fn osal_timer_num_active * * @brief * * This function counts the number of active timers. * * @return u8 - number of timers */ u8 osal_timer_num_active( void ) { halIntState_t intState; u8 num_timers = 0; osalTimerRec_t *srchTimer; HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts. // Head of the timer list srchTimer = timerHead; // Count timers in the list while ( srchTimer != NULL ) { num_timers++; srchTimer = srchTimer->next; } HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts. return num_timers; }
/******************************************************************************* * @fn RFHAL_NextDataEntryDone API * * @brief This function is used to mark the next System data entry on a * data entry queue as Pending so that the radio can once again * use it. It should be called after the user has processed the * data entry. * * input parameters * * @param dataEntryQueue_t - Pointer to data entry queue. * * output parameters * * @param None. * * @return None. */ void RFHAL_NextDataEntryDone( dataEntryQ_t *pDataEntryQ ) { halIntState_t cs; dataQ_t *pDataQueue; #ifdef DEBUG RFHAL_ASSERT( pDataEntryQ != NULL ); #endif // DEBUG // point to data queue pDataQueue = (dataQ_t *)pDataEntryQ; if ( pDataQueue->pNextDataEntry != NULL ) { HAL_ENTER_CRITICAL_SECTION(cs); // mark the next System data entry as Pending pDataQueue->pNextDataEntry->status = DATASTAT_PENDING; // advance to the next data entry in the data entry queue pDataQueue->pNextDataEntry = pDataQueue->pNextDataEntry->pNextEntry; HAL_EXIT_CRITICAL_SECTION(cs); // return pointer to next entry, or NULL if there isn't one // Note: For a ring buffer, there is always another. return; //( pDataQueue->pNextDataEntry ); } else // we are at the end of a linked list { // ALT: Could set pNextDataEntry to first entry, but could be problematic // if the radio data queue commands are being used to add/remove // data entries. } // return next data entry to may be processed by System software return; }
/************************************************************************************************** * @fn macRadioSetChannel * * @brief Set radio channel. * * @param channel - channel number, valid range is 11 through 26. Allow * channels 27 and 28 for some Japanese customers. * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macRadioSetChannel(uint8 channel) { halIntState_t s; MAC_ASSERT((channel >= 11) && (channel <= 28)); /* illegal channel */ /* critical section to make sure transmit does not start while updating channel */ HAL_ENTER_CRITICAL_SECTION(s); /* set requested channel */ reqChannel = channel; /* * If transmit is not active, update the radio hardware immediately. If transmit is active, * the channel will be updated at the end of the current transmit. */ if (!macTxActive) { macRadioUpdateChannel(); } HAL_EXIT_CRITICAL_SECTION(s); }
/************************************************************************************************** * @fn macMcuOverflowSetPeriod * * @brief Set overflow count period value. An interrupt is triggered when the overflow * count equals this period value. * * @param count - overflow count compare value * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macMcuOverflowSetPeriod(uint32 count) { halIntState_t s; uint8 enableCompareInt = 0; MAC_ASSERT( !(count >> 24) ); /* illegal count value */ HAL_ENTER_CRITICAL_SECTION(s); /* Disable overflow compare interrupts. */ if (T2IRQM & TIMER2_OVF_PERM) { enableCompareInt = 1; T2IRQM &= ~TIMER2_OVF_PERM; } MAC_MCU_T2_ACCESS_OVF_PERIOD_VALUE(); /* for efficiency, the 32-bit value is decoded using endian abstracted indexing */ T2MOVF0 = ((uint8 *)&count)[UINT32_NDX0]; T2MOVF1 = ((uint8 *)&count)[UINT32_NDX1]; T2MOVF2 = ((uint8 *)&count)[UINT32_NDX2]; /* * Now that new compare value is stored, clear the interrupt flag. This is important just * in case a false match was generated as the multi-byte compare value was written. */ T2IRQF &= ~TIMER2_OVF_PERF; /* re-enable overflow compare interrupts if they were previously enabled */ if (enableCompareInt) { T2IRQM |= TIMER2_OVF_PERM; } HAL_EXIT_CRITICAL_SECTION(s); }
/************************************************************************************************** * @fn macBackoffTimerICallPwrNotify * * @brief power state transition notify callback function * * @param pwrTrans power transition * @param data custom data - not used * * @return none ************************************************************************************************** */ static void macBackoffTimerICallPwrNotify(ICall_PwrTransition pwrTrans, ICall_PwrNotifyData *data) { if (pwrTrans == ICALL_PWR_AWAKE_FROM_STANDBY) { /* Wakeup must be handled from the thread context. * Signal the event to the OSAL thread. */ halIntState_t is; HAL_ENTER_CRITICAL_SECTION(is); macBackoffTimerEvents |= MAC_BACKOFF_TIMER_EVENT_POWER_WAKEUP; HAL_EXIT_CRITICAL_SECTION(is); ICall_signal(osal_semaphore); } else if (pwrTrans == ICALL_PWR_ENTER_STANDBY) { /* Stop RAT timer */ macRATValue = macStopRAT(); /* Park CM0 */ MAC_RADIO_POWER_DOWN(); /* The following calls are necessary to prevent a race condition in * pg2_leakage_workaround that causes CM3 to constantly firing up CPE1 * interrupts during power up until CM3 crashes. */ ICall_disableInt( INT_RF_CPE0 ); ICall_disableInt( INT_RF_CPE1 ); ICall_disableInt( INT_RF_HW ); ICall_disableInt( INT_RF_CMD_ACK ); } else if (pwrTrans == ICALL_PWR_ENTER_SHUTDOWN) { /* Park CM0 */ MAC_RADIO_POWER_DOWN(); } }
/************************************************************************************************** * @fn HalKeyPoll * * @brief This function is called by Hal_ProcessEvent() on a HAL_KEY_EVENT. * * input parameters * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ void HalKeyPoll(void) { uint8 newKeys; if (Hal_KeyIntEnable) { halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); newKeys = isrKeys; isrKeys = 0; HAL_EXIT_CRITICAL_SECTION(intState); } else { uint8 keys = HalKeyRead(); newKeys = (halKeys ^ keys) & keys; halKeys = keys; } if (newKeys && pHalKeyProcessFunction) { (pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL); } }
/****************************************************************************** * @fn HalSPIRead * * @brief Read from the external NV storage via SPI. * * @param addr - Offset into the external NV. * @param pBuf - Pointer to buffer to copy the bytes read from external NV. * @param len - Number of bytes to read from external NV. * * @return None. *****************************************************************************/ static void HalSPIRead(uint32 addr, uint8 *pBuf, uint16 len) { #if !HAL_OTA_BOOT_CODE uint8 shdw = P1DIR; halIntState_t his; HAL_ENTER_CRITICAL_SECTION(his); P1DIR |= BV(3); #endif XNV_SPI_BEGIN(); do { xnvSPIWrite(XNV_STAT_CMD); } while (XNV_SPI_RX() & XNV_STAT_WIP); XNV_SPI_END(); asm("NOP"); asm("NOP"); XNV_SPI_BEGIN(); xnvSPIWrite(XNV_READ_CMD); xnvSPIWrite(addr >> 16); xnvSPIWrite(addr >> 8); xnvSPIWrite(addr); xnvSPIWrite(0); while (len--) { xnvSPIWrite(0); *pBuf++ = XNV_SPI_RX(); } XNV_SPI_END(); #if !HAL_OTA_BOOT_CODE P1DIR = shdw; HAL_EXIT_CRITICAL_SECTION(his); #endif }
/** * Main entry function for the stack image */ int stack_main( void *arg ) { /* User reconfiguration of BLE Controller and Host variables */ setBleUserConfig( (bleUserCfg_t *)arg ); /* Establish OSAL for a stack service that requires accompanying * messaging service */ if (ICall_enrollService(ICALL_SERVICE_CLASS_BLE_MSG, (ICall_ServiceFunc) osal_service_entry, &osal_entity, &osal_semaphore) != ICALL_ERRNO_SUCCESS) { /* abort */ ICall_abort(); } halIntState_t state; HAL_ENTER_CRITICAL_SECTION(state); // Turn off interrupts //osal_int_disable( INTS_ALL ); // Initialize NV System osal_snv_init( ); // Initialize the operating system osal_init_system(); // Allow interrupts //osal_int_enable( INTS_ALL ); HAL_EXIT_CRITICAL_SECTION(state); osal_start_system(); // No Return from here return 0; // Shouldn't get here. }
/*********************************************************************************** * @fn halUartPollRx * * @brief Poll for data from USB. * * @param none * * @return none */ static void halUartPollRx(void) { uint8 cnt; uint8 ep = USBFW_GET_SELECTED_ENDPOINT(); USBFW_SELECT_ENDPOINT(4); // If the OUT endpoint has received a complete packet. if (USBFW_OUT_ENDPOINT_DISARMED()) { halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); // Get length of USB packet, this operation must not be interrupted. cnt = USBFW_GET_OUT_ENDPOINT_COUNT_LOW(); cnt += USBFW_GET_OUT_ENDPOINT_COUNT_HIGH() >> 8; HAL_EXIT_CRITICAL_SECTION(intState); while (cnt--) { halUartRxQ[halUartRxT++] = USBF4; } USBFW_ARM_OUT_ENDPOINT(); #if !defined HAL_SB_BOOT_CODE // If the USB has transferred in more Rx bytes, reset the Rx idle timer. // Re-sync the shadow on any 1st byte(s) received. if (rxTick == 0) { rxShdw = ST0; } rxTick = HAL_UART_USB_IDLE; #endif } #if !defined HAL_SB_BOOT_CODE else if (rxTick)
void macRadioSetTxPower(uint8 txPower) { halIntState_t s; /* if the selected dBm is out of range, use the closest available */ if (txPower > MAC_RADIO_TX_POWER_MAX_MINUS_DBM) { txPower = MAC_RADIO_TX_POWER_MAX_MINUS_DBM; } /* * Set the global variable reqTxPower. This variable is referenced * by the function macRadioUpdateTxPower() to write the radio register. * * A lookup table is used to translate the power level to the register * value. */ HAL_ENTER_CRITICAL_SECTION(s); reqTxPower = macRadioDefsTxPowerTable[txPower]; HAL_EXIT_CRITICAL_SECTION(s); /* update the radio power setting */ macRadioUpdateTxPower(); }
/******************************************************************************* * @fn RFHAL_FreeNextTxDataEntry API * * @brief This function is used to free the next TX data entry based on * the internal data entry queue pointer. This routine should be * used after the radio FW indicates a TX Entry Done interrupt. * Once freed, the internal data entry queue is updated to the * next entry. * * Note: It is assumed the data entry queue is really based on a * data queue. * * input parameters * * @param pDataEntryQ - Pointer to data entry queue. * * output parameters * * @param None. * * @return None. */ void RFHAL_FreeNextTxDataEntry( dataEntryQ_t *pDataEntryQ ) { halIntState_t cs; dataEntry_t *pNextEntry; HAL_ENTER_CRITICAL_SECTION(cs); // get next data entry to free (i.e. head of internal queue) pNextEntry = ((dataQ_t *)pDataEntryQ)->pNextDataEntry; // update the internal next data entry pointer based on pCurEntry // Note: If this was the last data entry on the queue, then pCurEntry would // be NULL, and so would pNextDataEntry. If this was not the last data // entry on the queue, then pNextDataEntry should point to the current // entry. So pNextEntry in either case. ((dataQ_t *)pDataEntryQ)->pNextDataEntry = pNextEntry->pNextEntry; // free the TX data entry given by the internal next data entry osal_bm_free( (void *)pNextEntry ); HAL_EXIT_CRITICAL_SECTION(cs); return; }
/*================================================================================================= * @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 MAC_MlmeSetReq * * @brief This direct execute function sets an attribute value * in the MAC PIB. * * input parameters * * @param pibAttribute - The attribute identifier. * @param pValue - pointer to the attribute value. * * output parameters * * None. * * @return The status of the request, as follows: * MAC_SUCCESS Operation successful. * MAC_UNSUPPORTED_ATTRIBUTE Attribute not found. * ************************************************************************************************** */ uint8 MAC_MlmeSetReq(uint8 pibAttribute, void *pValue) { uint8 i; halIntState_t intState; if (pibAttribute == MAC_BEACON_PAYLOAD) { macPib.pBeaconPayload = pValue; return MAC_SUCCESS; } /* look up attribute in PIB table */ if ((i = macPibIndex(pibAttribute)) == MAC_PIB_INVALID) { return MAC_UNSUPPORTED_ATTRIBUTE; } /* do range check; no range check if min and max are zero */ if ((macPibTbl[i].min != 0) || (macPibTbl[i].max != 0)) { /* if min == max, this is a read-only attribute */ if (macPibTbl[i].min == macPibTbl[i].max) { return MAC_READ_ONLY; } /* check for special cases */ if (pibAttribute == MAC_MAX_FRAME_TOTAL_WAIT_TIME) { if ((*((uint16 *) pValue) < MAC_MAX_FRAME_RESPONSE_MIN) || (*((uint16 *) pValue) > MAC_MAX_FRAME_RESPONSE_MAX)) { return MAC_INVALID_PARAMETER; } } /* range check for general case */ if ((*((uint8 *) pValue) < macPibTbl[i].min) || (*((uint8 *) pValue) > macPibTbl[i].max)) { return MAC_INVALID_PARAMETER; } } /* set value in PIB */ HAL_ENTER_CRITICAL_SECTION(intState); osal_memcpy((uint8 *) &macPib + macPibTbl[i].offset, pValue, macPibTbl[i].len); HAL_EXIT_CRITICAL_SECTION(intState); /* handle special cases */ switch (pibAttribute) { case MAC_PAN_ID: /* set pan id in radio */ macRadioSetPanID(macPib.panId); break; case MAC_SHORT_ADDRESS: /* set short address in radio */ macRadioSetShortAddr(macPib.shortAddress); break; case MAC_RX_ON_WHEN_IDLE: /* turn rx on or off */ if (macPib.rxOnWhenIdle) { macRxEnable(MAC_RX_WHEN_IDLE); } else { macRxDisable(MAC_RX_WHEN_IDLE); } break; case MAC_LOGICAL_CHANNEL: macRadioSetChannel(macPib.logicalChannel); break; case MAC_EXTENDED_ADDRESS: /* set ext address in radio */ macRadioSetIEEEAddr(macPib.extendedAddress.addr.extAddr); break; #ifndef MAC_OBSOLETE_PHY_TRANSMIT_POWER /* Define MAC_OBSOLETE_PHY_TRANSMIT_POWER to save some code */ case MAC_PHY_TRANSMIT_POWER: /* Legacy transmit power attribute */ #if !defined HAL_MAC_USE_REGISTER_POWER_VALUES && \ !defined HAL_PA_LNA && !defined HAL_PA_LNA_CC2590 /* Legacy transmit power attribute value for CC2530 alone, * or runtime selection support build means a negative absolute value. * However, when used as register power values or * with HAL_PA_LNAxxx definition (without runtime selection) * the attribute value is not a negative absolute value. */ macPib.phyTransmitPower = (uint8)(-(int8)macPib.phyTransmitPower); #endif /* !defined HAL_MAC_USE_REGISTER_POWER_VALUES && ... */ /* pass through to next case -- do not break*/ #endif /* MAC_OBSOLETE_PHY_TRANSMIT_POWER */ case MAC_PHY_TRANSMIT_POWER_SIGNED: (void)macRadioSetTxPower(macPib.phyTransmitPower); break; default: break; } return MAC_SUCCESS; }
/************************************************************************************************** * @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 MAC_MlmeSetReq * * @brief This direct execute function sets an attribute value * in the MAC PIB. * * input parameters * * @param pibAttribute - The attribute identifier. * @param pValue - pointer to the attribute value. * * output parameters * * None. * * @return The status of the request, as follows: * MAC_SUCCESS Operation successful. * MAC_UNSUPPORTED_ATTRIBUTE Attribute not found. * ************************************************************************************************** */ uint8 MAC_MlmeSetReq(uint8 pibAttribute, void *pValue) { uint8 i; halIntState_t intState; if (pibAttribute == MAC_BEACON_PAYLOAD) { pMacPib->pBeaconPayload = pValue; return MAC_SUCCESS; } /* look up attribute in PIB table */ if ((i = MAP_macPibIndex(pibAttribute)) == MAC_PIB_INVALID) { return MAC_UNSUPPORTED_ATTRIBUTE; } /* do range check; no range check if min and max are zero */ if ((macPibTbl[i].min != 0) || (macPibTbl[i].max != 0)) { /* if min == max, this is a read-only attribute */ if (macPibTbl[i].min == macPibTbl[i].max) { return MAC_READ_ONLY; } /* check for special cases */ if (pibAttribute == MAC_MAX_FRAME_TOTAL_WAIT_TIME) { if ((*((uint16 *) pValue) < MAC_MAX_FRAME_RESPONSE_MIN) || (*((uint16 *) pValue) > MAC_MAX_FRAME_RESPONSE_MAX)) { return MAC_INVALID_PARAMETER; } } /* range check for general case */ if ((*((uint8 *) pValue) < macPibTbl[i].min) || (*((uint8 *) pValue) > macPibTbl[i].max)) { return MAC_INVALID_PARAMETER; } } /* set value in PIB */ HAL_ENTER_CRITICAL_SECTION(intState); osal_memcpy((uint8 *) pMacPib + macPibTbl[i].offset, pValue, macPibTbl[i].len); HAL_EXIT_CRITICAL_SECTION(intState); /* handle special cases */ switch (pibAttribute) { case MAC_PAN_ID: /* set pan id in radio */ macRadioSetPanID(pMacPib->panId); break; case MAC_SHORT_ADDRESS: /* set short address in radio */ macRadioSetShortAddr(pMacPib->shortAddress); break; case MAC_RX_ON_WHEN_IDLE: /* turn rx on or off */ if (pMacPib->rxOnWhenIdle) { macRxEnable(MAC_RX_WHEN_IDLE); } else { macRxDisable(MAC_RX_WHEN_IDLE); } break; case MAC_LOGICAL_CHANNEL: macRadioSetChannel(pMacPib->logicalChannel); break; case MAC_EXTENDED_ADDRESS: /* set ext address in radio */ macRadioSetIEEEAddr(pMacPib->extendedAddress.addr.extAddr); break; case MAC_PHY_TRANSMIT_POWER_SIGNED: (void)macRadioSetTxPower(pMacPib->phyTransmitPower); break; case MAC_RF4CE_POWER_SAVINGS: pMacPib->rf4cepowerSavings = *(uint8 *)pValue; break; case MAC_FRAME_VERSION_SUPPORT: pMacPib->frameVersionSupport = *(uint8 *)pValue; break; default: break; } return MAC_SUCCESS; }
/********************************************************************* * @fn osal_mem_alloc * * @brief Implementation of the allocator functionality. * * @param size - number of bytes to allocate from the heap. * * @return void * - pointer to the heap allocation; NULL if error or failure. */ void *osal_mem_alloc( uint16 size ) { osalMemHdr_t *prev = NULL; osalMemHdr_t *hdr; halIntState_t intState; uint16 tmp; uint8 coal = 0; OSALMEM_ASSERT( size ); size += HDRSZ; // Calculate required bytes to add to 'size' to align to halDataAlign_t. if ( sizeof( halDataAlign_t ) == 2 ) { size += (size & 0x01); } else if ( sizeof( halDataAlign_t ) != 1 ) { const uint8 mod = size % sizeof( halDataAlign_t ); if ( mod != 0 ) { size += (sizeof( halDataAlign_t ) - mod); } } HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts. // Smaller allocations are first attempted in the small-block bucket. if ( size <= OSALMEM_SMALL_BLKSZ ) { hdr = ff1; } else { hdr = ff2; } tmp = *hdr; do { if ( tmp & OSALMEM_IN_USE ) { tmp ^= OSALMEM_IN_USE; coal = 0; } else { if ( coal != 0 ) { #if ( OSALMEM_METRICS ) blkCnt--; blkFree--; #endif *prev += *hdr; if ( *prev >= size ) { hdr = prev; tmp = *hdr; break; } } else { if ( tmp >= size ) { break; } coal = 1; prev = hdr; } } hdr = (osalMemHdr_t *)((uint8 *)hdr + tmp); tmp = *hdr; if ( tmp == 0 ) { hdr = NULL; break; } } while ( 1 ); if ( hdr != NULL ) { tmp -= size; // Determine whether the threshold for splitting is met. if ( tmp >= OSALMEM_MIN_BLKSZ ) { // Split the block before allocating it. osalMemHdr_t *next = (osalMemHdr_t *)((uint8 *)hdr + size); *next = tmp; *hdr = (size | OSALMEM_IN_USE); #if ( OSALMEM_METRICS ) blkCnt++; if ( blkMax < blkCnt ) { blkMax = blkCnt; } memAlo += size; #endif } else { #if ( OSALMEM_METRICS ) memAlo += *hdr; blkFree--; #endif *hdr |= OSALMEM_IN_USE; } #if ( OSALMEM_METRICS ) if ( memMax < memAlo ) { memMax = memAlo; } #endif #if ( OSALMEM_PROFILER ) { uint8 idx; size = *hdr ^ OSALMEM_IN_USE; for ( idx = 0; idx < OSALMEM_PROMAX; idx++ ) { if ( size <= proCnt[idx] ) { break; } } proCur[idx]++; if ( proMax[idx] < proCur[idx] ) { proMax[idx] = proCur[idx]; } proTot[idx]++; } #endif hdr++; #if ( OSALMEM_PROFILER ) (void)osal_memset( (uint8 *)hdr, OSALMEM_ALOC, (size - HDRSZ) ); /* A small-block could not be allocated in the small-block bucket. * When this occurs significantly frequently, increase the size of the * bucket in order to restore better worst case run times. Set the first * profiling bucket size in proCnt[] to the small-block bucket size and * divide proSmallBlkMiss by the corresponding proTot[] size to get % miss. * Best worst case time on TrasmitApp was achieved at a 0-15% miss rate * during steady state Tx load, 0% during idle and steady state Rx load. */ if ( (size <= OSALMEM_SMALL_BLKSZ) && (hdr > ff2) ) { proSmallBlkMiss++; } #endif } HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts. return (void *)hdr; }
/************************************************************************************************** * @fn spiParseRx * * @brief Parse all available bytes from the spiRxBuf[]; parse Rx data into the spiRxDat[]. * * input parameters * * None. * * output parameters * * None. * * @return None. */ static void spiParseRx(void) { uint8 done = 0; halIntState_t cs; #ifdef HAL_SPI_MASTER uint8 numNotSOF = 0; uint8 count = 0; SPI_SET_CSn_OUT(); #endif //HAL_SPI_MASTER while (!done) { if (!SPI_NEW_RX_BYTE(spiRxIdx)) { #if defined HAL_SPI_MASTER // Clock a byte from slave SPI_CLOCK_RX(1); #else break; #endif } uint8 ch = SPI_GET_RX_BYTE(spiRxIdx); SPI_CLR_RX_BYTE(spiRxIdx); SPI_LEN_T_INCR(spiRxIdx); #ifdef HAL_SPI_MASTER // If searching for a SOF byte limit the number of bytes searched // to SPI_FRM_LEN if ( spiRxPktState == SPIRX_STATE_SOF ) { count++; numNotSOF = ( ch == SPI_SOF ) ? numNotSOF : numNotSOF + 1; // Haven't recieved an SOF. Assume not a packet but only break if there is // no new bytes in the RX buffer to read if ( numNotSOF > SPI_FRM_LEN && numNotSOF == count ) { // Clear all Rx'ed NULL bytes while(SPI_NEW_RX_BYTE(spiRxIdx)) { SPI_CLR_RX_BYTE(spiRxIdx); SPI_LEN_T_INCR(spiRxIdx); } break; } } #endif //HAL_SPI_MASTER switch (spiRxPktState) { case SPIRX_STATE_SOF: if (ch == SPI_SOF) { spiRxPktState = SPIRX_STATE_LEN; /* At this point, the master has effected the protocol for ensuring that the SPI slave is * awake, so set the spiRxLen to non-zero to prevent the slave from re-entering sleep until * the entire packet is received - even if the master interrupts the sending of the packet * by de-asserting/re-asserting MRDY one or more times */ spiRxLen = 1; } break; case SPIRX_STATE_LEN: if ((ch == 0) || (ch > SPI_MAX_DAT_LEN)) { spiRxPktState = SPIRX_STATE_SOF; spiRxLen = 0; } else { spiRxFcs = spiRxLen = ch; spiRxTemp = spiRxTail; spiRxCnt = 0; spiRxPktState = SPIRX_STATE_DATA; #if defined HAL_SPI_MASTER if (!SPI_NEW_RX_BYTE(spiRxIdx)) /* Fix for simultaneous TX/RX to avoid extra clock pulses to SPI Slave */ { halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); SPI_CLOCK_RX(ch + 1); /* Clock out the SPI Frame Data bytes and FCS */ HAL_EXIT_CRITICAL_SECTION(intState); } #endif } break; case SPIRX_STATE_DATA: spiRxFcs ^= ch; spiRxDat[spiRxTemp] = ch; SPI_LEN_T_INCR(spiRxTemp); if (++spiRxCnt == spiRxLen) { spiRxPktState = SPIRX_STATE_FCS; } break; case SPIRX_STATE_FCS: spiRxPktState = SPIRX_STATE_SOF; #ifdef POWER_SAVING pktFound = TRUE; #endif if (ch == spiRxFcs) { spiRxTail = spiRxTemp; } else { dbgFcsByte = ch; #ifdef RBA_UART_TO_SPI badFcsPktCount++; #endif } spiRxCnt = spiRxLen = 0; #ifdef HAL_SPI_MASTER // Clear any trailing empty Rx'ed bytes // Should only receive one frame per MRDY assertion while(SPI_NEW_RX_BYTE(spiRxIdx)) { SPI_CLR_RX_BYTE(spiRxIdx); SPI_LEN_T_INCR(spiRxIdx); } done = 1; #endif //HAL_SPI_MASTER break; default: HAL_ASSERT(0); break; } } spiTxLen = 0; HAL_ENTER_CRITICAL_SECTION(cs); spiRdyIsr = 0; HAL_EXIT_CRITICAL_SECTION(cs); #ifdef HAL_SPI_MASTER SPI_CLR_CSn_OUT(); #ifdef RBA_UART_TO_SPI // Must wait until slave has ended transaction because // the RBA implementation cannot delay writes so this is a // forced delay.... while(SPI_RDY_IN()); #endif #else SPI_CLR_RDY_OUT(); #endif //HAL_SPI_MASTER }
/************************************************************************************************** * @fn HalUARTPollSPI * * @brief SPI Transport Polling Manager. * * input parameters * * None. * * output parameters * * None. * * @return None. */ static void HalUARTPollSPI(void) { #ifdef HAL_SPI_MASTER #else #if defined POWER_SAVING pktFound = FALSE; #endif if ( ( spiRdyIsr ) || (SPI_RDY_IN()) ) { CLEAR_SLEEP_MODE(); #if defined HAL_SBL_BOOT_CODE if(!spiTxLen) { UxDBUF = 0x00; /* Zero out garbage from UxDBUF */ HAL_DMA_ARM_CH(HAL_SPI_CH_RX); /* Arm RX DMA */ asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); SPI_SET_RDY_OUT(); /* SPI_RDYOut = 0 */ SPI_CLR_RDY_OUT(); /* SPI_RDYOut = 1 */ HAL_EXIT_CRITICAL_SECTION(intState); } #endif #if defined POWER_SAVING pktFound = TRUE; #endif } #endif #ifdef HAL_SPI_MASTER if ( spiRdyIsr && !writeActive) { spiParseRx(); } #else //SPI Slave if ( spiRdyIsr && !writeActive ) { if ( !(UxCSR & CSR_ACTIVE) ) { // MRDY has gone low to set spiRdyIsr // and has now gone high if SPI_RDY_IN // is false, read RXed bytes spiParseRx(); } else { // MRDY has gone low and is still low // Set SRDY low to signal ready to RX SPI_SET_RDY_OUT(); } } #endif //HAL_SPI_MASTER #if defined HAL_SPI_MASTER if( SPI_RX_RDY()) #else if (SPI_RX_RDY() && !spiTxLen) #endif { if (spiCB != NULL) { spiCB((HAL_UART_SPI - 1), HAL_UART_RX_TIMEOUT); } } #if defined POWER_SAVING if ( SPI_RDY_IN()|| SPI_RX_RDY() || spiRxLen || spiTxLen || spiRdyIsr || pktFound || SPI_RDY_OUT() ) { CLEAR_SLEEP_MODE(); } else if ( (!pktFound) && (!SPI_NEW_RX_BYTE(spiRxIdx)) ) { PxIEN |= SPI_RDYIn_BIT; SPI_CLR_RDY_OUT(); } #endif }
/************************************************************************************************** * @fn HalAdcRead * * @brief Read the ADC based on given channel and resolution * * @param channel - channel where ADC will be read * @param resolution - the resolution of the value * * @return 16 bit value of the ADC in offset binary format. * * Note that the ADC is "bipolar", which means the GND (0V) level is mid-scale. * Note2: This function assumes that ADCCON3 contains the voltage reference. **************************************************************************************************/ uint16 HalAdcRead (uint8 channel, uint8 resolution) { int16 reading = 0; #if (HAL_ADC == TRUE) uint8 i, resbits; uint8 adcChannel = 1; uint32 padConfig, dirConfig; halIntState_t s; /* * If Analog input channel is AIN0..AIN7, make sure corresponing PA pin is * setup. Only port A can be used as input to the ADC. If any pin on port A * is to be used as an ADC input, the appropriate register, IOC_PAx_OVER, * must be set to analog (that is, bit 0 must be set to 1). */ /* Hold off interrupts */ HAL_ENTER_CRITICAL_SECTION(s); switch (channel) { case HAL_ADC_CHN_AIN0: case HAL_ADC_CHN_AIN1: case HAL_ADC_CHN_AIN2: case HAL_ADC_CHN_AIN3: case HAL_ADC_CHN_AIN4: case HAL_ADC_CHN_AIN5: case HAL_ADC_CHN_AIN6: case HAL_ADC_CHN_AIN7: adcChannel <<= channel; break; case HAL_ADC_CHN_A0A1: adcChannel = HAL_BITS_CHN_A0A1; break; case HAL_ADC_CHN_A2A3: adcChannel = HAL_BITS_CHN_A2A3; break; case HAL_ADC_CHN_A4A5: adcChannel = HAL_BITS_CHN_A4A5; break; case HAL_ADC_CHN_A6A7: adcChannel = HAL_BITS_CHN_A6A7; break; default: adcChannel = 0; break; } /* save the current pad setting of the PortA pin */ padConfig = IOCPadConfigGet(GPIO_A_BASE, adcChannel); /* save the current gpio setting of the PortA pin */ dirConfig = GPIODirModeGet(GPIO_A_BASE, adcChannel); /* set the PortA pin to Analog */ IOCPadConfigSet(GPIO_A_BASE, adcChannel, IOC_OVERRIDE_ANA); /* set the PortA pin direction to input */ GPIODirModeSet(GPIO_A_BASE, adcChannel, GPIO_DIR_MODE_IN); /* Convert resolution to decimation rate */ switch (resolution) { case HAL_ADC_RESOLUTION_8: resbits = HAL_ADC_DEC_064; break; case HAL_ADC_RESOLUTION_10: resbits = HAL_ADC_DEC_128; break; case HAL_ADC_RESOLUTION_12: resbits = HAL_ADC_DEC_256; break; case HAL_ADC_RESOLUTION_14: default: resbits = HAL_ADC_DEC_512; break; } /* writing to this register starts the extra conversion */ ADCCON3 = channel | resbits | adcRef; /* Wait for the conversion to be done */ while (!(ADCCON1 & HAL_ADC_EOC)); /* Set the pad configuration to previous value*/ IOCPadConfigSet(GPIO_A_BASE, adcChannel, padConfig); /* Set the GPIO direction to previous value*/ GPIODirModeSet(GPIO_A_BASE, adcChannel, dirConfig); /* Read the result */ reading = (int16) (ADCL); reading |= (int16) (ADCH << 8); /* Enable interrupts */ HAL_EXIT_CRITICAL_SECTION(s); /* Treat small negative as 0 */ if (reading < 0) reading = 0; switch (resolution) { case HAL_ADC_RESOLUTION_8: reading >>= 8; break; case HAL_ADC_RESOLUTION_10: reading >>= 6; break; case HAL_ADC_RESOLUTION_12: reading >>= 4; break; case HAL_ADC_RESOLUTION_14: default: reading >>= 2; break; } #else /* unused arguments */ (void) channel; (void) resolution; #endif return ((uint16)reading); }
/************************************************************************************************** * @fn halMacTimerElapsed * * @brief Determine the number of MAC timer ticks elapsed during sleep. * * input parameters * * @param timeout - pointer to the 320us timeout. * * output parameters * * None. * * @return Number of timer ticks elapsed during sleep. ************************************************************************************************** */ uint32 halMacTimerElapsed( uint32 *timeout ) { volatile uint16 tar; volatile uint16 taccr; uint32 ticks; uint32 timeout_u320; /* 320 us timeout */ halIntState_t s; /* read the currrent count + compare count (MAC timer must be in CTC mode) */ HAL_ENTER_CRITICAL_SECTION(s); #ifdef __msp430x54x tar = HAL_MAC_SLEEP_TIMER_TAR(); #endif /* Halt timer before read anything */ HAL_MAC_SLEEP_TIMER_SPEED_UP(); HAL_EXIT_CRITICAL_SECTION(s); #ifdef __msp430x54x /* With MSP430F5438 RTM silicon, when the counter mode is moved from 'up' * to 'stop' and the timer counter equals the capture/compare value in a * TACCRx register the transition of modes will pull the TAR bits (15:4) * to a '1' momentarily. Re-initialize TAR as a temporary workaround until * the post-RTM revisions of silicon with this bug fixed. */ HAL_MAC_SLEEP_TIMER_TAR() = tar; #else /* Read timer registers */ tar = HAL_MAC_SLEEP_TIMER_TAR(); #endif taccr = HAL_MAC_SLEEP_TIMER_COMPARE(); /* store current timer count */ ticks = tar; /* calculate elapsed time or somthing else woke up the MCU */ if ( taccr <= halSleepTimerStart ) { ticks += taccr; } ticks -= halSleepTimerStart; /* adjust timeout for the next sleep cycle */ timeout_u320 = ticks * 3125 / 4096; if ( *timeout >= timeout_u320 ) { *timeout -= timeout_u320; } /* Restore current MAC count */ HAL_MAC_SLEEP_TIMER_SET_COMPARE(halSleepTimerCompareStart); HAL_MAC_SLEEP_TIMER_TAR() = halSleepTimerStart; /* Restart timer */ HAL_MAC_SLEEP_TIMER_RESTART(); /* Return elapsed time in units of 320us */ return ( timeout_u320 ); }
/************************************************************************************************** * @fn DMAExecCrc * * @brief This function assumes CRC has been initialized and sets up and * starts a dma tranfer from a flash page to the CRC HW module. * * @note This function assumes DMA channel 0 is available for use. * * input parameters * * @param page - A valid flash page number. * @param offset - A valid offset into the page. * @param len - A valid number of bytes to calculate crc of. * * @return None. ************************************************************************************************** */ void DMAExecCrc(uint8 page, uint16 offset, uint16 len) { uint8 memctr = MEMCTR; // Save to restore. // Calculate the offset into the containing flash bank as it gets mapped into XDATA. uint16 address = (offset + HAL_FLASH_PAGE_MAP) + ((page % HAL_FLASH_PAGE_PER_BANK) * HAL_FLASH_PAGE_SIZE); // Pointer to DMA config structure halDMADesc_t *dmaCh0_p = &dmaCh0; #if !defined HAL_OAD_BOOT_CODE halIntState_t is; #endif page /= HAL_FLASH_PAGE_PER_BANK; // Calculate the flash bank from the flash page. #if !defined HAL_OAD_BOOT_CODE HAL_ENTER_CRITICAL_SECTION(is); #endif // Calculate and map the containing flash bank into XDATA. MEMCTR = (MEMCTR & 0xF8) | page; // page is actually bank // Start address for CRC calculation in the XDATA mapped flash bank HAL_DMA_SET_SOURCE(dmaCh0_p, address); // Destination for data transfer, RNDH mapped to XDATA HAL_DMA_SET_DEST(dmaCh0_p, 0x70BD); // One whole page (or len) at a time HAL_DMA_SET_LEN(dmaCh0_p, len); // 8-bit, block, no trigger HAL_DMA_SET_WORD_SIZE(dmaCh0_p, HAL_DMA_WORDSIZE_BYTE); HAL_DMA_SET_TRIG_MODE(dmaCh0_p, HAL_DMA_TMODE_BLOCK); HAL_DMA_SET_TRIG_SRC(dmaCh0_p, HAL_DMA_TRIG_NONE); // SRC += 1, DST = constant, no IRQ, all 8 bits, high priority HAL_DMA_SET_SRC_INC(dmaCh0_p, HAL_DMA_SRCINC_1); HAL_DMA_SET_DST_INC(dmaCh0_p, HAL_DMA_DSTINC_0); HAL_DMA_SET_IRQ(dmaCh0_p, HAL_DMA_IRQMASK_DISABLE); HAL_DMA_SET_M8(dmaCh0_p, HAL_DMA_M8_USE_8_BITS); HAL_DMA_SET_PRIORITY(dmaCh0_p, HAL_DMA_PRI_HIGH); // Tell DMA Controller where above configuration can be found HAL_DMA_SET_ADDR_DESC0(&dmaCh0); // Arm the DMA channel (0) HAL_DMA_ARM_CH(0); // 9 cycles wait asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); // Start DMA tranfer HAL_DMA_MAN_TRIGGER(0); // Wait for dma to finish. while(DMAREQ & 0x1); // Restore bank mapping MEMCTR = memctr; #if !defined HAL_OAD_BOOT_CODE HAL_EXIT_CRITICAL_SECTION(is); #endif }
//------------------------------------------------------------------- // @fn halIntUnlock // @brief Set interrupt state back to the state it had before calling halIntLock(). // Should always be used together with halIntLock(). // @param key // @return none //------------------------------------------------------------------- void halIntUnlock(uint16 key) { HAL_EXIT_CRITICAL_SECTION(key); }
static #endif /* USE_ICALL */ void macBackoffTimerEventHandler(void) { halIntState_t is; uint8 events; HAL_ENTER_CRITICAL_SECTION(is); events = macBackoffTimerEvents; macBackoffTimerEvents = 0; HAL_EXIT_CRITICAL_SECTION(is); if (events & MAC_BACKOFF_TIMER_EVENT_POWER_WAKEUP) { // Wakeup radio // Turning on radio domain before clock set up seems to cause // unexpected interrupt. // Hence interrupt shall be disabled here. MB_DisableInts(); // Enable clocks for all radio internal modules. // Use Non-Buff access for safety and check for sanity HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = 0x7FF; /* Setup mailbox */ macSetupMailbox(); #ifdef DEBUG_SW_TRACE /* re-enable RF trace output for FPGA */ MB_SendCommand( BUILD_DIRECT_PARAM_EXT_CMD( CMD_ENABLE_DEBUG, 0x1D40 ) ); /* or 0x1940 for less trace */ DBG_PRINT0(DBGSYS, "RF Trace Resumes..."); #endif /* DEBUG_SW_TRACE */ /* Start off CM0. Patch it. */ macSetupRfHal(); /* Restore states */ MAC_RADIO_SET_CHANNEL(macPhyChannel); MAC_RADIO_SET_PAN_COORDINATOR(macPanCoordinator); MAC_RADIO_SET_PAN_ID(pMacPib->panId); MAC_RADIO_SET_SHORT_ADDR(pMacPib->shortAddress); MAC_RADIO_SET_IEEE_ADDR(pMacPib->extendedAddress.addr.extAddr); #if !defined( USE_FPGA ) #ifdef USE_ICALL // Switch back to HFOSC. while (!ICall_pwrIsStableXOSCHF()); ICall_pwrSwitchXOSCHF(); #endif /* USE_ICALL */ #ifdef OSAL_PORT2TIRTOS // Switches back to HFOSC. while (!Power_isStableXOSC_HF()); Power_switchXOSC_HF(); #endif /* OSAL_PORT2TIRTOS */ #endif /* !defined( USE_FPGA ) */ /* Synchronize RAT timer */ macSyncStartRAT(macRATValue); /* Turn on autoack */ MAC_RADIO_TURN_ON_AUTO_ACK(); /* Initialize SRCEXTPENDEN and SRCSHORTPENDEN to zeros */ MAC_RADIO_SRC_MATCH_INIT_EXTPENDEN(); MAC_RADIO_SRC_MATCH_INIT_SHORTPENDEN(); /* Start 15.4 Radio */ macSetupRadio(); /* Restore timer comparators */ MAC_RADIO_BACKOFF_SET_PERIOD(macBackoffTimerRollover); MAC_RADIO_BACKOFF_SET_COMPARE(backoffTimerTrigger); #if 0 /* Following code should be disabled normally */ /* Code for wakeup lead time calibration */ { static uint32 macBackoffTimerMinMargin = 0xffffffffu; uint32 delta = macPrevPeriodRatCount + backoffTimerTrigger * MAC_BACKOFF_TO_RAT_RATIO - MAC_RAT_COUNT; if (delta < macBackoffTimerMinMargin) { macBackoffTimerMinMargin = delta; } } #endif } /* Note that MAC_BACKOFF_TIMER_EVENT_POWER_TIMER_EXP handling must always * occur after handling of MAC_BACKOFF_TIMER_EVENT_POWER_WAKEUP event * because the device might be waking up upon the timer event itself * in which case, radio has to be turned on before updating the RAT timer. */ if (events & MAC_BACKOFF_TIMER_EVENT_POWER_TIMER_EXP) { /* Update wakeup schedule, which most likely would vote not to enter * sleep state. */ HAL_ENTER_CRITICAL_SECTION(is); MAC_BACKOFF_TIMER_UPDATE_WAKEUP(); HAL_EXIT_CRITICAL_SECTION(is); } }
/************************************************************************************************** * @fn osal_mem_alloc * * @brief This function implements the OSAL dynamic memory allocation functionality. * * input parameters * * @param size - the number of bytes to allocate from the HEAP. * * output parameters * * None. * * @return None. */ #ifdef DPRINTF_OSALHEAPTRACE void *osal_mem_alloc_dbg( uint16 size, const char *fname, unsigned lnum ) #else /* DPRINTF_OSALHEAPTRACE */ void *osal_mem_alloc( uint16 size ) #endif /* DPRINTF_OSALHEAPTRACE */ { osalMemHdr_t *prev = NULL; osalMemHdr_t *hdr; halIntState_t intState; uint8 coal = 0; size += OSALMEM_HDRSZ; // Calculate required bytes to add to 'size' to align to halDataAlign_t. if ( sizeof( halDataAlign_t ) == 2 ) { size += (size & 0x01); } else if ( sizeof( halDataAlign_t ) != 1 ) { const uint8 mod = size % sizeof( halDataAlign_t ); if ( mod != 0 ) { size += (sizeof( halDataAlign_t ) - mod); } } HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts. // Smaller allocations are first attempted in the small-block bucket, and all long-lived // allocations are channeled into the LL block reserved within this bucket. if ((osalMemStat == 0) || (size <= OSALMEM_SMALL_BLKSZ)) { hdr = ff1; } else { hdr = (theHeap + OSALMEM_BIGBLK_IDX); } do { if ( hdr->hdr.inUse ) { coal = 0; } else { if ( coal != 0 ) { #if ( OSALMEM_METRICS ) blkCnt--; blkFree--; #endif prev->hdr.len += hdr->hdr.len; if ( prev->hdr.len >= size ) { hdr = prev; break; } } else { if ( hdr->hdr.len >= size ) { break; } coal = 1; prev = hdr; } } hdr = (osalMemHdr_t *)((uint8 *)hdr + hdr->hdr.len); if ( hdr->val == 0 ) { hdr = NULL; break; } } while (1); if ( hdr != NULL ) { uint16 tmp = hdr->hdr.len - size; // Determine whether the threshold for splitting is met. if ( tmp >= OSALMEM_MIN_BLKSZ ) { // Split the block before allocating it. osalMemHdr_t *next = (osalMemHdr_t *)((uint8 *)hdr + size); next->val = tmp; // Set 'len' & clear 'inUse' field. hdr->val = (size | OSALMEM_IN_USE); // Set 'len' & 'inUse' field. #if ( OSALMEM_METRICS ) blkCnt++; if ( blkMax < blkCnt ) { blkMax = blkCnt; } memAlo += size; #endif } else { #if ( OSALMEM_METRICS ) memAlo += hdr->hdr.len; blkFree--; #endif hdr->hdr.inUse = TRUE; } #if ( OSALMEM_METRICS ) if ( memMax < memAlo ) { memMax = memAlo; } #endif #if ( OSALMEM_PROFILER ) #if !OSALMEM_PROFILER_LL if (osalMemStat != 0) // Don't profile until after the LL block is filled. #endif { uint8 idx; for ( idx = 0; idx < OSALMEM_PROMAX; idx++ ) { if ( hdr->hdr.len <= proCnt[idx] ) { break; } } proCur[idx]++; if ( proMax[idx] < proCur[idx] ) { proMax[idx] = proCur[idx]; } proTot[idx]++; /* A small-block could not be allocated in the small-block bucket. * When this occurs significantly frequently, increase the size of the * bucket in order to restore better worst case run times. Set the first * profiling bucket size in proCnt[] to the small-block bucket size and * divide proSmallBlkMiss by the corresponding proTot[] size to get % miss. * Best worst case time on TrasmitApp was achieved at a 0-15% miss rate * during steady state Tx load, 0% during idle and steady state Rx load. */ if ((hdr->hdr.len <= OSALMEM_SMALL_BLKSZ) && (hdr >= (theHeap + OSALMEM_BIGBLK_IDX))) { proSmallBlkMiss++; } } (void)osal_memset((uint8 *)(hdr+1), OSALMEM_ALOC, (hdr->hdr.len - OSALMEM_HDRSZ)); #endif if ((osalMemStat != 0) && (ff1 == hdr)) { ff1 = (osalMemHdr_t *)((uint8 *)hdr + hdr->hdr.len); } hdr++; } HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts. #pragma diag_suppress=Pe767 HAL_ASSERT(((halDataAlign_t)hdr % sizeof(halDataAlign_t)) == 0); #pragma diag_default=Pe767 #ifdef DPRINTF_OSALHEAPTRACE dprintf("osal_mem_alloc(%u)->%lx:%s:%u\n", size, (unsigned) hdr, fname, lnum); #endif /* DPRINTF_OSALHEAPTRACE */ return (void *)hdr; }
/****************************************************************************** * @fn HalUARTWriteDMA * * @brief Write a buffer to the UART, enforcing an all or none policy if the requested length * exceeds the space available. * * @param buf - pointer to the buffer that will be written, not freed * len - length of * * @return length of the buffer that was sent *****************************************************************************/ static uint16 HalUARTWriteDMA(uint8 *buf, uint16 len) { #if HAL_UART_TX_BY_ISR // Enforce all or none. if (HAL_UART_DMA_TX_AVAIL() < len) { return 0; } for (uint16 cnt = 0; cnt < len; cnt++) { dmaCfg.txBuf[dmaCfg.txTail] = *buf++; dmaCfg.txMT = 0; if (dmaCfg.txTail >= HAL_UART_DMA_TX_MAX-1) { dmaCfg.txTail = 0; } else { dmaCfg.txTail++; } // Keep re-enabling ISR as it might be keeping up with this loop due to other ints. IEN2 |= UTXxIE; } #else txIdx_t txIdx; uint8 txSel; halIntState_t his; HAL_ENTER_CRITICAL_SECTION(his); txSel = dmaCfg.txSel; txIdx = dmaCfg.txIdx[txSel]; HAL_EXIT_CRITICAL_SECTION(his); // Enforce all or none. if ((len + txIdx) > HAL_UART_DMA_TX_MAX) { return 0; } (void)memcpy(&(dmaCfg.txBuf[txSel][txIdx]), buf, len); HAL_ENTER_CRITICAL_SECTION(his); /* If an ongoing DMA Tx finished while this buffer was being *appended*, then another DMA Tx * will have already been started on this buffer, but it did not include the bytes just appended. * Therefore these bytes have to be re-copied to the start of the new working buffer. */ if (txSel != dmaCfg.txSel) { HAL_EXIT_CRITICAL_SECTION(his); txSel ^= 1; (void)memcpy(&(dmaCfg.txBuf[txSel][0]), buf, len); HAL_ENTER_CRITICAL_SECTION(his); dmaCfg.txIdx[txSel] = len; } else { dmaCfg.txIdx[txSel] = txIdx + len; } // If there is no ongoing DMA Tx, then the channel must be armed here. if (dmaCfg.txIdx[(txSel ^ 1)] == 0) { HAL_EXIT_CRITICAL_SECTION(his); HalUARTArmTxDMA(); } else { dmaCfg.txMT = FALSE; HAL_EXIT_CRITICAL_SECTION(his); } #endif return len; }