| /* |
| * Copyright (c) 2017, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements the OpenThread platform abstraction for radio communication. |
| * |
| */ |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include "fsl_device_registers.h" |
| #include "openthread-core-kw41z-config.h" |
| #include "fsl_xcvr.h" |
| #include "openthread/platform/radio.h" |
| #include "openthread/platform/diag.h" |
| #include <utils/code_utils.h> |
| |
| #define DOUBLE_BUFFERING (1) |
| #define DEFAULT_CHANNEL (11) |
| #define DEFAULT_CCA_MODE (XCVR_CCA_MODE1_c) |
| #define IEEE802154_ACK_REQUEST (1 << 5) |
| #define IEEE802154_MAX_LENGTH (127) |
| #define IEEE802154_MIN_LENGTH (5) |
| #define IEEE802154_ACK_LENGTH (IEEE802154_MIN_LENGTH) |
| #define IEEE802154_FRM_CTL_LO_OFFSET (0) |
| #define IEEE802154_DSN_OFFSET (2) |
| #define IEEE802154_FRM_TYPE_MASK (0x7) |
| #define IEEE802154_FRM_TYPE_ACK (0x2) |
| #define IEEE802154_TURNAROUND_LEN (12) |
| #define IEEE802154_CCA_LEN (8) |
| #define IEEE802154_PHY_SHR_LEN (10) |
| #define IEEE802154_ACK_WAIT (54) |
| #define ZLL_IRQSTS_TMR_ALL_MSK_MASK (ZLL_IRQSTS_TMR1MSK_MASK | \ |
| ZLL_IRQSTS_TMR2MSK_MASK | \ |
| ZLL_IRQSTS_TMR3MSK_MASK | \ |
| ZLL_IRQSTS_TMR4MSK_MASK ) |
| |
| typedef enum xcvr_state_tag |
| { |
| XCVR_Idle_c, |
| XCVR_RX_c, |
| XCVR_TX_c, |
| XCVR_CCA_c, |
| XCVR_TR_c, |
| XCVR_CCCA_c, |
| } xcvr_state_t; |
| |
| typedef enum xcvr_cca_type_tag |
| { |
| XCVR_ED_c, /* energy detect - CCA bit not active, not to be used for T and CCCA sequences */ |
| XCVR_CCA_MODE1_c, /* energy detect - CCA bit ACTIVE */ |
| SCVR_CCA_MODE2_c, /* 802.15.4 compliant signal detect - CCA bit ACTIVE */ |
| XCVR_CCA_MODE3_c /* 802.15.4 compliant signal detect and energy detect - CCA bit ACTIVE */ |
| } xcvr_cca_type_t; |
| |
| static otRadioState sState = OT_RADIO_STATE_DISABLED; |
| static uint16_t sPanId; |
| static uint8_t sExtSrcAddrBitmap[(RADIO_CONFIG_SRC_MATCH_ENTRY_NUM + 7) / 8]; |
| static uint8_t sChannel; |
| static int8_t sMaxED; |
| static int8_t sAutoTxPwrLevel = 0; |
| |
| /* ISR Signaling Flags */ |
| static bool sTxDone = false; |
| static bool sRxDone = false; |
| static bool sEdScanDone = false; |
| static otError sTxStatus; |
| |
| static otRadioFrame sTxFrame; |
| static otRadioFrame sRxFrame; |
| #if DOUBLE_BUFFERING |
| static uint8_t sRxData[OT_RADIO_FRAME_MAX_SIZE]; |
| #endif |
| |
| /* Private functions */ |
| static void rf_abort(void); |
| static xcvr_state_t rf_get_state(void); |
| static void rf_set_channel(uint8_t channel); |
| static void rf_set_tx_power(int8_t tx_power); |
| static uint8_t rf_lqi_adjust(uint8_t hwLqi); |
| static int8_t rf_lqi_to_rssi(uint8_t lqi); |
| static uint32_t rf_get_timestamp(void); |
| static void rf_set_timeout(uint32_t abs_timeout); |
| static uint16_t rf_get_addr_checksum(uint8_t *pAddr, bool ExtendedAddr, uint16_t PanId); |
| static otError rf_add_addr_table_entry(uint16_t checksum, bool extendedAddr); |
| static otError rf_remove_addr_table_entry(uint16_t checksum); |
| static otError rf_remove_addr_table_entry_index(uint8_t index); |
| static bool rf_process_rx_frame(void); |
| |
| otRadioState otPlatRadioGetState(otInstance *aInstance) |
| { |
| (void)aInstance; |
| return sState; |
| } |
| |
| void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) |
| { |
| (void)aInstance; |
| uint32_t addrLo; |
| uint32_t addrHi; |
| |
| if ((RSIM->MAC_LSB == 0xffffffff) && (RSIM->MAC_MSB == 0xff)) |
| { |
| addrLo = SIM->UIDL; |
| addrHi = SIM->UIDML; |
| } |
| else |
| { |
| addrLo = RSIM->MAC_LSB; |
| addrHi = RSIM->MAC_MSB; |
| } |
| |
| memcpy(aIeeeEui64, &addrLo, sizeof(addrLo)); |
| memcpy(aIeeeEui64 + sizeof(addrLo), &addrHi, sizeof(addrHi)); |
| } |
| |
| void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId) |
| { |
| (void) aInstance; |
| |
| sPanId = aPanId; |
| ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACPANID0_MASK; |
| ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACPANID0(aPanId); |
| } |
| |
| void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *aExtendedAddress) |
| { |
| (void) aInstance; |
| uint32_t addrLo; |
| uint32_t addrHi; |
| |
| memcpy(&addrLo, aExtendedAddress, sizeof(addrLo)); |
| memcpy(&addrHi, aExtendedAddress + sizeof(addrLo), sizeof(addrHi)); |
| |
| ZLL->MACLONGADDRS0_LSB = addrLo; |
| ZLL->MACLONGADDRS0_MSB = addrHi; |
| } |
| |
| void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) |
| { |
| (void) aInstance; |
| |
| ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK; |
| ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACSHORTADDRS0(aShortAddress); |
| } |
| |
| otError otPlatRadioEnable(otInstance *aInstance) |
| { |
| otEXPECT(!otPlatRadioIsEnabled(aInstance)); |
| |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TRCV_MSK_MASK; |
| NVIC_ClearPendingIRQ(Radio_1_IRQn); |
| NVIC_EnableIRQ(Radio_1_IRQn); |
| |
| sState = OT_RADIO_STATE_SLEEP; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioDisable(otInstance *aInstance) |
| { |
| otEXPECT(otPlatRadioIsEnabled(aInstance)); |
| |
| NVIC_DisableIRQ(Radio_1_IRQn); |
| rf_abort(); |
| sState = OT_RADIO_STATE_DISABLED; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| bool otPlatRadioIsEnabled(otInstance *aInstance) |
| { |
| (void) aInstance; |
| return sState != OT_RADIO_STATE_DISABLED; |
| } |
| |
| otError otPlatRadioSleep(otInstance *aInstance) |
| { |
| otError status = OT_ERROR_NONE; |
| (void) aInstance; |
| |
| otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && |
| (sState != OT_RADIO_STATE_DISABLED)), status = OT_ERROR_INVALID_STATE); |
| |
| rf_abort(); |
| sState = OT_RADIO_STATE_SLEEP; |
| |
| exit: |
| return status; |
| } |
| |
| otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) |
| { |
| otError status = OT_ERROR_NONE; |
| (void) aInstance; |
| |
| otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && |
| (sState != OT_RADIO_STATE_DISABLED)), status = OT_ERROR_INVALID_STATE); |
| |
| sState = OT_RADIO_STATE_RECEIVE; |
| |
| otEXPECT(rf_get_state() != XCVR_RX_c); |
| |
| rf_abort(); |
| |
| /* Set Power level for auto TX */ |
| rf_set_tx_power(sAutoTxPwrLevel); |
| rf_set_channel(aChannel); |
| sRxFrame.mChannel = aChannel; |
| |
| /* Clear all IRQ flags */ |
| ZLL->IRQSTS = ZLL->IRQSTS; |
| /* Start the RX sequence */ |
| ZLL->PHY_CTRL |= XCVR_RX_c ; |
| |
| /* Unmask SEQ interrupt */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; |
| |
| exit: |
| return status; |
| } |
| |
| void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) |
| { |
| (void) aInstance; |
| |
| if (aEnable) |
| { |
| ZLL->SAM_CTRL |= ZLL_SAM_CTRL_SAP0_EN_MASK; |
| } |
| else |
| { |
| ZLL->SAM_CTRL &= ~ZLL_SAM_CTRL_SAP0_EN_MASK; |
| } |
| } |
| |
| otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| (void) aInstance; |
| uint16_t checksum = sPanId + aShortAddress; |
| |
| return rf_add_addr_table_entry(checksum, false); |
| } |
| |
| otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) |
| { |
| (void) aInstance; |
| uint16_t checksum = rf_get_addr_checksum((uint8_t *)aExtAddress, true, sPanId); |
| |
| return rf_add_addr_table_entry(checksum, true); |
| } |
| |
| otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| (void) aInstance; |
| uint16_t checksum = sPanId + aShortAddress; |
| |
| return rf_remove_addr_table_entry(checksum); |
| } |
| |
| otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) |
| { |
| (void) aInstance; |
| uint16_t checksum = rf_get_addr_checksum((uint8_t *)aExtAddress, true, sPanId); |
| |
| return rf_remove_addr_table_entry(checksum); |
| } |
| |
| void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) |
| { |
| (void) aInstance; |
| uint32_t i; |
| |
| for (i = 0; i < RADIO_CONFIG_SRC_MATCH_ENTRY_NUM; i++) |
| { |
| /* Optimization: sExtSrcAddrBitmap[i / 8] & (1 << (i % 8)) */ |
| if (!(sExtSrcAddrBitmap[i >> 3] & (1 << (i & 7)))) |
| { |
| rf_remove_addr_table_entry_index(i); |
| } |
| } |
| } |
| |
| void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) |
| { |
| (void) aInstance; |
| uint32_t i; |
| |
| for (i = 0; i < RADIO_CONFIG_SRC_MATCH_ENTRY_NUM; i++) |
| { |
| /* Optimization: sExtSrcAddrBitmap[i / 8] & (1 << (i % 8))*/ |
| if (sExtSrcAddrBitmap[i >> 3] & (1 << (i & 7))) |
| { |
| rf_remove_addr_table_entry_index(i); |
| } |
| } |
| } |
| |
| otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) |
| { |
| (void)aInstance; |
| return &sTxFrame; |
| } |
| |
| otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| otError status = OT_ERROR_NONE; |
| uint32_t timeout; |
| |
| (void) aInstance; |
| |
| otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && |
| (sState != OT_RADIO_STATE_DISABLED)), status = OT_ERROR_INVALID_STATE); |
| |
| if (rf_get_state() != XCVR_Idle_c) |
| { |
| rf_abort(); |
| } |
| |
| rf_set_channel(aFrame->mChannel); |
| rf_set_tx_power(aFrame->mPower); |
| |
| *(uint8_t *)ZLL->PKT_BUFFER_TX = aFrame->mLength; |
| |
| /* Set CCA mode */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_CCATYPE_MASK; |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCATYPE(DEFAULT_CCA_MODE); |
| |
| /* Clear all IRQ flags */ |
| ZLL->IRQSTS = ZLL->IRQSTS; |
| |
| /* Perform automatic reception of ACK frame, if required */ |
| if (aFrame->mPsdu[IEEE802154_FRM_CTL_LO_OFFSET] & IEEE802154_ACK_REQUEST) |
| { |
| ZLL->PHY_CTRL |= XCVR_TR_c; |
| /* Set ACK wait time-out */ |
| timeout = rf_get_timestamp(); |
| timeout += (((XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_MASK) >> |
| XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_SHIFT) >> 4); |
| timeout += IEEE802154_CCA_LEN + IEEE802154_TURNAROUND_LEN + IEEE802154_PHY_SHR_LEN + |
| (1 + aFrame->mLength) * OT_RADIO_SYMBOLS_PER_OCTET + IEEE802154_ACK_WAIT; |
| rf_set_timeout(timeout); |
| } |
| else |
| { |
| ZLL->PHY_CTRL |= XCVR_TX_c; |
| } |
| |
| sTxStatus = OT_ERROR_NONE; |
| sState = OT_RADIO_STATE_TRANSMIT; |
| /* Unmask SEQ interrupt */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; |
| |
| exit: |
| return status; |
| } |
| |
| int8_t otPlatRadioGetRssi(otInstance *aInstance) |
| { |
| (void) aInstance; |
| return (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_RSSI_MASK) >> ZLL_LQI_AND_RSSI_RSSI_SHIFT; |
| } |
| |
| otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) |
| { |
| (void)aInstance; |
| return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_ENERGY_SCAN; |
| } |
| |
| bool otPlatRadioGetPromiscuous(otInstance *aInstance) |
| { |
| (void) aInstance; |
| return (ZLL->PHY_CTRL & ZLL_PHY_CTRL_PROMISCUOUS_MASK) == ZLL_PHY_CTRL_PROMISCUOUS_MASK; |
| } |
| |
| void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) |
| { |
| (void) aInstance; |
| |
| if (aEnable) |
| { |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_PROMISCUOUS_MASK; |
| /* FRM_VER[11:8] = b1111. Any FrameVersion accepted */ |
| ZLL->RX_FRAME_FILTER |= (ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | |
| ZLL_RX_FRAME_FILTER_ACK_FT_MASK | |
| ZLL_RX_FRAME_FILTER_NS_FT_MASK); |
| } |
| else |
| { |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_PROMISCUOUS_MASK; |
| /* FRM_VER[11:8] = b0011. Accept FrameVersion 0 and 1 packets, reject all others */ |
| /* Beacon, Data and MAC command frame types accepted */ |
| ZLL->RX_FRAME_FILTER &= ~(ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | |
| ZLL_RX_FRAME_FILTER_ACK_FT_MASK | |
| ZLL_RX_FRAME_FILTER_NS_FT_MASK | |
| ZLL_RX_FRAME_FILTER_ACTIVE_PROMISCUOUS_MASK); |
| ZLL->RX_FRAME_FILTER |= ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3); |
| } |
| } |
| |
| otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) |
| { |
| otError status = OT_ERROR_NONE; |
| uint32_t timeout; |
| (void) aInstance; |
| |
| otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && |
| (sState != OT_RADIO_STATE_DISABLED)), status = OT_ERROR_INVALID_STATE); |
| |
| if (rf_get_state() != XCVR_Idle_c) |
| { |
| rf_abort(); |
| } |
| |
| sMaxED = -128; |
| rf_set_channel(aScanChannel); |
| /* Set CCA type to ED - Energy Detect */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_CCATYPE_MASK; |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCATYPE(XCVR_ED_c); |
| /* Clear all IRQ flags */ |
| ZLL->IRQSTS = ZLL->IRQSTS; |
| /* Start ED sequence */ |
| ZLL->PHY_CTRL |= XCVR_CCA_c; |
| /* Unmask SEQ interrupt */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; |
| /* Set Scan time-out */ |
| timeout = rf_get_timestamp(); |
| timeout += (aScanDuration * 1000) / OT_RADIO_SYMBOL_TIME; |
| rf_set_timeout(timeout); |
| |
| exit: |
| return status; |
| } |
| |
| void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower) |
| { |
| (void)aInstance; |
| |
| sAutoTxPwrLevel = aPower; |
| } |
| |
| int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| return -100; |
| } |
| |
| /*************************************************************************************************/ |
| |
| static void rf_abort(void) |
| { |
| /* Mask SEQ interrupt */ |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_SEQMSK_MASK; |
| |
| /* Disable timer trigger (for scheduled XCVSEQ) */ |
| if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_TMRTRIGEN_MASK) |
| { |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMRTRIGEN_MASK; |
| |
| /* give the FSM enough time to start if it was triggered */ |
| while ((XCVR_MISC->XCVR_CTRL & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) == 0) |
| { |
| } |
| } |
| |
| /* If XCVR is not idle, abort current SEQ */ |
| if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) |
| { |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; |
| |
| /* wait for Sequence Idle (if not already) */ |
| while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) |
| { |
| } |
| } |
| |
| /* Stop timers */ |
| ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR2CMP_EN_MASK | ZLL_PHY_CTRL_TMR3CMP_EN_MASK); |
| /* Clear all PP IRQ bits to avoid unexpected interrupts( do not change TMR1 and TMR4 IRQ status ) */ |
| ZLL->IRQSTS &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); |
| } |
| |
| static xcvr_state_t rf_get_state(void) |
| { |
| return (xcvr_state_t)((ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) >> ZLL_PHY_CTRL_XCVSEQ_SHIFT); |
| } |
| |
| static void rf_set_channel(uint8_t channel) |
| { |
| if (sChannel != channel) |
| { |
| ZLL->CHANNEL_NUM0 = channel; |
| sChannel = channel; |
| } |
| } |
| |
| static void rf_set_tx_power(int8_t tx_power) |
| { |
| if (tx_power > 2) |
| { |
| ZLL->PA_PWR = 30; |
| } |
| else if (tx_power > 1) |
| { |
| ZLL->PA_PWR = 24; |
| } |
| else if (tx_power > -1) |
| { |
| ZLL->PA_PWR = 18; |
| } |
| else if (tx_power > -3) |
| { |
| ZLL->PA_PWR = 14; |
| } |
| else if (tx_power > -4) |
| { |
| ZLL->PA_PWR = 12; |
| } |
| else if (tx_power > -6) |
| { |
| ZLL->PA_PWR = 10; |
| } |
| else if (tx_power > -8) |
| { |
| ZLL->PA_PWR = 8; |
| } |
| else if (tx_power > -11) |
| { |
| ZLL->PA_PWR = 6; |
| } |
| else if (tx_power > -14) |
| { |
| ZLL->PA_PWR = 4; |
| } |
| else if (tx_power > -20) |
| { |
| ZLL->PA_PWR = 2; |
| } |
| else |
| { |
| ZLL->PA_PWR = 0; |
| } |
| } |
| |
| static uint16_t rf_get_addr_checksum(uint8_t *pAddr, bool ExtendedAddr, uint16_t PanId) |
| { |
| uint16_t checksum; |
| |
| /* Short address */ |
| checksum = PanId; |
| checksum += *pAddr++; |
| checksum += (uint16_t)(*pAddr++) << 8; |
| |
| if (ExtendedAddr) |
| { |
| /* Extended address */ |
| checksum += *pAddr++; |
| checksum += ((uint16_t)(*pAddr++)) << 8; |
| checksum += *pAddr++; |
| checksum += ((uint16_t)(*pAddr++)) << 8; |
| checksum += *pAddr++; |
| checksum += ((uint16_t)(*pAddr++)) << 8; |
| } |
| |
| return checksum; |
| } |
| |
| static otError rf_add_addr_table_entry(uint16_t checksum, bool extendedAddr) |
| { |
| uint8_t index; |
| otError status; |
| |
| /* Find first free index */ |
| ZLL->SAM_TABLE = ZLL_SAM_TABLE_FIND_FREE_IDX_MASK; |
| |
| while (ZLL->SAM_TABLE & ZLL_SAM_TABLE_SAM_BUSY_MASK) |
| { |
| } |
| |
| index = (ZLL->SAM_FREE_IDX & ZLL_SAM_FREE_IDX_SAP0_1ST_FREE_IDX_MASK) >> ZLL_SAM_FREE_IDX_SAP0_1ST_FREE_IDX_SHIFT; |
| |
| otEXPECT_ACTION((index < RADIO_CONFIG_SRC_MATCH_ENTRY_NUM), status = OT_ERROR_NO_BUFS); |
| |
| /* Insert the checksum at the index found */ |
| ZLL->SAM_TABLE = ((uint32_t)index << ZLL_SAM_TABLE_SAM_INDEX_SHIFT) | |
| ((uint32_t)checksum << ZLL_SAM_TABLE_SAM_CHECKSUM_SHIFT) | |
| ZLL_SAM_TABLE_SAM_INDEX_WR_MASK | ZLL_SAM_TABLE_SAM_INDEX_EN_MASK; |
| |
| if (extendedAddr) |
| { |
| /* Optimization: sExtSrcAddrBitmap[index / 8] |= 1 << (index % 8); */ |
| sExtSrcAddrBitmap[index >> 3] |= 1 << (index & 7); |
| } |
| |
| status = OT_ERROR_NONE; |
| |
| exit: |
| return status; |
| } |
| |
| static otError rf_remove_addr_table_entry(uint16_t checksum) |
| { |
| otError status = OT_ERROR_NO_ADDRESS; |
| uint32_t i, temp; |
| |
| /* Search for an entry to match the provided checksum */ |
| for (i = 0; i < RADIO_CONFIG_SRC_MATCH_ENTRY_NUM; i++) |
| { |
| ZLL->SAM_TABLE = i << ZLL_SAM_TABLE_SAM_INDEX_SHIFT; |
| /* Read checksum located at the specified index */ |
| temp = (ZLL->SAM_TABLE & ZLL_SAM_TABLE_SAM_CHECKSUM_MASK) >> ZLL_SAM_TABLE_SAM_CHECKSUM_SHIFT; |
| |
| if (temp == checksum) |
| { |
| /* Remove the entry from the table */ |
| status = rf_remove_addr_table_entry_index(i); |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| static otError rf_remove_addr_table_entry_index(uint8_t index) |
| { |
| otError status = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION(index < RADIO_CONFIG_SRC_MATCH_ENTRY_NUM, status = OT_ERROR_NO_ADDRESS); |
| |
| ZLL->SAM_TABLE = ((uint32_t)0xFFFF << ZLL_SAM_TABLE_SAM_CHECKSUM_SHIFT) | |
| ((uint32_t)index << ZLL_SAM_TABLE_SAM_INDEX_SHIFT) | |
| ZLL_SAM_TABLE_SAM_INDEX_INV_MASK | ZLL_SAM_TABLE_SAM_INDEX_WR_MASK; |
| |
| /* Clear bitmap */ |
| /* Optimization: sExtSrcAddrBitmap[index / 8] &= ~(1 << (index % 8)); */ |
| sExtSrcAddrBitmap[index >> 3] &= ~(1 << (index & 7)); |
| |
| exit: |
| return status; |
| } |
| |
| static uint8_t rf_lqi_adjust(uint8_t hwLqi) |
| { |
| if (hwLqi >= 220) |
| { |
| hwLqi = 255; |
| } |
| else |
| { |
| hwLqi = (51 * hwLqi) / 44; |
| } |
| |
| return hwLqi; |
| } |
| |
| static int8_t rf_lqi_to_rssi(uint8_t lqi) |
| { |
| int32_t rssi = (36 * lqi - 9836) / 109; |
| |
| return (int8_t)rssi; |
| } |
| |
| static uint32_t rf_get_timestamp(void) |
| { |
| return ZLL->EVENT_TMR >> ZLL_EVENT_TMR_EVENT_TMR_SHIFT; |
| } |
| |
| static void rf_set_timeout(uint32_t abs_timeout) |
| { |
| uint32_t irqSts; |
| |
| /* Disable TMR3 compare */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMR3CMP_EN_MASK; |
| /* Set time-out value */ |
| ZLL->T3CMP = abs_timeout; |
| /* Aknowledge and unmask TMR3 IRQ */ |
| irqSts = ZLL->IRQSTS & ZLL_IRQSTS_TMR_ALL_MSK_MASK; |
| irqSts &= ~ZLL_IRQSTS_TMR3MSK_MASK; |
| irqSts |= ZLL_IRQSTS_TMR3IRQ_MASK; |
| ZLL->IRQSTS = irqSts; |
| /* Enable TMR3 compare */ |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TMR3CMP_EN_MASK; |
| } |
| |
| static bool rf_process_rx_frame(void) |
| { |
| uint8_t temp; |
| bool status = true; |
| |
| /* Get Rx length */ |
| temp = (ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; |
| |
| /* Check if frame is valid */ |
| otEXPECT_ACTION((IEEE802154_MIN_LENGTH <= temp) && (temp <= IEEE802154_MAX_LENGTH), status = false); |
| |
| sRxFrame.mLength = temp; |
| temp = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_LQI_VALUE_MASK) >> ZLL_LQI_AND_RSSI_LQI_VALUE_SHIFT; |
| sRxFrame.mLqi = rf_lqi_adjust(temp); |
| sRxFrame.mPower = rf_lqi_to_rssi(sRxFrame.mLqi); |
| #if DOUBLE_BUFFERING |
| |
| for (temp = 0; temp < sRxFrame.mLength - 2; temp++) |
| { |
| sRxData[temp] = ((uint8_t *)ZLL->PKT_BUFFER_RX)[temp]; |
| } |
| |
| #endif |
| |
| exit: |
| return status; |
| } |
| |
| void Radio_1_IRQHandler(void) |
| { |
| xcvr_state_t state = rf_get_state(); |
| uint32_t irqStatus = ZLL->IRQSTS; |
| int8_t temp; |
| |
| ZLL->IRQSTS = irqStatus; |
| |
| /* TMR3 IRQ - time-out */ |
| if ((irqStatus & ZLL_IRQSTS_TMR3IRQ_MASK) && (!(irqStatus & ZLL_IRQSTS_TMR3MSK_MASK))) |
| { |
| /* Stop TMR3 */ |
| ZLL->IRQSTS = irqStatus | ZLL_IRQSTS_TMR3MSK_MASK; |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMR3CMP_EN_MASK; |
| |
| if (state == XCVR_CCA_c) |
| { |
| rf_abort(); |
| sEdScanDone = true; |
| } |
| else if ((state == XCVR_TR_c) && !(irqStatus & ZLL_IRQSTS_RXIRQ_MASK)) |
| { |
| rf_abort(); |
| sState = OT_RADIO_STATE_RECEIVE; |
| sTxStatus = OT_ERROR_NO_ACK; |
| sTxDone = true; |
| } |
| } |
| |
| /* Sequence done IRQ */ |
| if ((!(ZLL->PHY_CTRL & ZLL_PHY_CTRL_SEQMSK_MASK)) && (irqStatus & ZLL_IRQSTS_SEQIRQ_MASK)) |
| { |
| /* Cleanup */ |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; |
| ZLL->PHY_CTRL |= ZLL_PHY_CTRL_SEQMSK_MASK; |
| |
| switch (state) |
| { |
| case XCVR_RX_c: |
| sRxDone = rf_process_rx_frame(); |
| break; |
| |
| case XCVR_TR_c: |
| if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && (irqStatus & ZLL_IRQSTS_CCA_MASK)) |
| { |
| sTxStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| } |
| else if (!(irqStatus & ZLL_IRQSTS_RXIRQ_MASK) || |
| (rf_process_rx_frame() == false) || |
| (sRxFrame.mLength != IEEE802154_ACK_LENGTH) || |
| ((sRxFrame.mPsdu[IEEE802154_FRM_CTL_LO_OFFSET] & IEEE802154_FRM_TYPE_MASK) != IEEE802154_FRM_TYPE_ACK) || |
| (sRxFrame.mPsdu[IEEE802154_DSN_OFFSET] != sTxFrame.mPsdu[IEEE802154_DSN_OFFSET])) |
| { |
| sTxStatus = OT_ERROR_NO_ACK; |
| } |
| |
| sState = OT_RADIO_STATE_RECEIVE; |
| sTxDone = true; |
| break; |
| |
| case XCVR_TX_c: |
| if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && (irqStatus & ZLL_IRQSTS_CCA_MASK)) |
| { |
| sTxStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| } |
| |
| sState = OT_RADIO_STATE_RECEIVE; |
| sTxDone = true; |
| break; |
| |
| case XCVR_CCA_c: |
| temp = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >> ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT; |
| |
| if (temp > sMaxED) |
| { |
| sMaxED = temp; |
| } |
| |
| if (!sEdScanDone) |
| { |
| /* Restart ED */ |
| while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} |
| |
| ZLL->IRQSTS = (ZLL->IRQSTS & ZLL_IRQSTS_TMR_ALL_MSK_MASK) | ZLL_IRQSTS_SEQIRQ_MASK; |
| ZLL->PHY_CTRL |= XCVR_CCA_c; |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; |
| } |
| |
| break; |
| |
| default: |
| rf_abort(); |
| break; |
| } |
| } |
| |
| if ((sState == OT_RADIO_STATE_RECEIVE) && (rf_get_state() == XCVR_Idle_c)) |
| { |
| /* Restart RX */ |
| while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} |
| |
| ZLL->IRQSTS = ZLL->IRQSTS; |
| ZLL->PHY_CTRL |= XCVR_RX_c; |
| ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; |
| } |
| } |
| |
| void kw41zRadioInit(void) |
| { |
| XCVR_Init(ZIGBEE_MODE, DR_500KBPS); |
| |
| /* Disable all timers, enable AUTOACK and CCA before TX, mask all interrupts */ |
| ZLL->PHY_CTRL = ZLL_PHY_CTRL_CCATYPE(DEFAULT_CCA_MODE) | |
| ZLL_PHY_CTRL_CCABFRTX_MASK | |
| ZLL_PHY_CTRL_TSM_MSK_MASK | |
| ZLL_PHY_CTRL_WAKE_MSK_MASK | |
| ZLL_PHY_CTRL_CRC_MSK_MASK | |
| ZLL_PHY_CTRL_PLL_UNLOCK_MSK_MASK | |
| ZLL_PHY_CTRL_FILTERFAIL_MSK_MASK | |
| ZLL_PHY_CTRL_RX_WMRK_MSK_MASK | |
| ZLL_PHY_CTRL_CCAMSK_MASK | |
| ZLL_PHY_CTRL_RXMSK_MASK | |
| ZLL_PHY_CTRL_TXMSK_MASK | |
| ZLL_PHY_CTRL_SEQMSK_MASK | |
| ZLL_PHY_CTRL_AUTOACK_MASK | |
| ZLL_PHY_CTRL_TRCV_MSK_MASK; |
| |
| /* Clear all IRQ flags and disable all timer interrupts */ |
| ZLL->IRQSTS = ZLL->IRQSTS; |
| |
| /* Frame Filtering |
| FRM_VER[7:6] = b11. Accept FrameVersion 0 and 1 packets, reject all others */ |
| ZLL->RX_FRAME_FILTER &= ~ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK; |
| ZLL->RX_FRAME_FILTER = ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3) | |
| ZLL_RX_FRAME_FILTER_CMD_FT_MASK | |
| ZLL_RX_FRAME_FILTER_DATA_FT_MASK | |
| ZLL_RX_FRAME_FILTER_ACK_FT_MASK | |
| ZLL_RX_FRAME_FILTER_BEACON_FT_MASK; |
| |
| /* Set prescaller to obtain 1 symbol (16us) timebase */ |
| ZLL->TMR_PRESCALE = 0x05; |
| |
| /* Set CCA threshold to -75 dBm */ |
| ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK; |
| ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_CCA1_THRESH(-75); |
| |
| /* Adjust LQI compensation */ |
| ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP_MASK; |
| ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP(96); |
| |
| /* Adjust ACK delay to fulfill the 802.15.4 turnaround requirements */ |
| ZLL->ACKDELAY &= ~ZLL_ACKDELAY_ACKDELAY_MASK; |
| ZLL->ACKDELAY |= ZLL_ACKDELAY_ACKDELAY(-8); |
| |
| /* Clear HW indirect queue */ |
| ZLL->SAM_TABLE |= ZLL_SAM_TABLE_INVALIDATE_ALL_MASK; |
| |
| rf_set_channel(DEFAULT_CHANNEL); |
| rf_set_tx_power(0); |
| |
| sTxFrame.mLength = 0; |
| sTxFrame.mPsdu = (uint8_t *)ZLL->PKT_BUFFER_TX + 1; |
| sRxFrame.mLength = 0; |
| #if DOUBLE_BUFFERING |
| sRxFrame.mPsdu = sRxData; |
| #else |
| sRxFrame.mPsdu = (uint8_t *)ZLL->PKT_BUFFER_RX; |
| #endif |
| } |
| |
| void kw41zRadioProcess(otInstance *aInstance) |
| { |
| if (sTxDone) |
| { |
| if (sTxFrame.mPsdu[IEEE802154_FRM_CTL_LO_OFFSET] & IEEE802154_ACK_REQUEST) |
| { |
| otPlatRadioTxDone(aInstance, &sTxFrame, &sRxFrame, sTxStatus); |
| } |
| else |
| { |
| otPlatRadioTxDone(aInstance, &sTxFrame, NULL, sTxStatus); |
| } |
| |
| sTxDone = false; |
| } |
| |
| if (sRxDone) |
| { |
| #if OPENTHREAD_ENABLE_DIAG |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioReceiveDone(aInstance, &sRxFrame, OT_ERROR_NONE); |
| } |
| else |
| #endif |
| { |
| otPlatRadioReceiveDone(aInstance, &sRxFrame, OT_ERROR_NONE); |
| } |
| |
| sRxDone = false; |
| } |
| |
| if (sEdScanDone) |
| { |
| otPlatRadioEnergyScanDone(aInstance, sMaxED); |
| sEdScanDone = false; |
| } |
| } |