/** * \file miwi_p2p_star.c * * \brief MiWi P2P & STAR Protocol Implementation * * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * * \page License * * Subject to your compliance with these terms, you may use Microchip * software and any derivatives exclusively with Microchip products. * It is your responsibility to comply with third party license terms applicable * to your use of third party software (including open source software) that * may accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * \asf_license_stop * */ /* * Support and FAQ: visit Microchip Support */ /*********************************** HEADERS ******************************************/ #include "system.h" /* MiWi Application layer configuration file */ #include "miwi_config.h" /* MiWi Protocol layer configuration file */ #include "miwi_config_p2p.h" #include "miwi_p2p_star.h" #include "mimac_at86rf.h" #include "mimem.h" #include "delay.h" #if defined(ENABLE_NETWORK_FREEZER) #include "pdsDataServer.h" #include "wlPdsTaskManager.h" #endif #include "miqueue.h" #include "string.h" /********************************* Macro Definitions *************************************/ #define PROTOCOL_TIMER_INTERVAL 1000 #if defined(PROTOCOL_STAR) #define DATA_TIMER_INTERVAL 100 #endif #if defined(ENABLE_SLEEP_FEATURE) #define RECEIVE_ON_WHEN_IDLE 0x00 #else #define RECEIVE_ON_WHEN_IDLE 0x01 #endif #if defined(ENABLE_SECURITY) #define SECURITY_INFO 0x80 #else #define SECURITY_INFO 0x00 #endif /* Maximum number of failure in transmitting link status before reporting failure link */ #define MAX_LINK_STATUS_FAILURES 3 /******************************* Type Definitions *************************************/ typedef struct _gEstConnection { uint8_t addrLen; miwi_status_t status; p2pStarState_t backupState; /* Connection Retry manipulation Variable */ uint8_t connectionRetries; uint8_t address[LONG_ADDR_LEN]; connectionConf_callback_t confCallback; } gEstConnection_t; typedef struct _resyncConnection { miwi_status_t status; /* Resync Retry manipulation Variable */ uint8_t resyncTimes; p2pStarState_t backupState; uint32_t channelMap; uint8_t connectionIndex; resyncConnection_callback_t confCallback; } resyncConnection_t; typedef struct _gSearchConnection { uint8_t activeScanResultIndex; uint8_t scanDuration; uint32_t channelMap; SearchConnectionConf_callback_t gSearchConfCallback; } gSearchConnection_t; /* Frame Transmit Structures */ typedef struct _TxFrameEntry_t { API_UINT64_UNION frameDstAddr; DataConf_callback_t frameConfCallback; uint8_t *frame; MAC_TRANS_PARAM frameParam; uint8_t frameLength; uint8_t frameHandle; } TxFrameEntry_t; typedef struct _TxFrame_t { TxFrameEntry_t *nextFrame; TxFrameEntry_t txFrameEntry; } TxFrame_t; /********************* Global and Static Variables *************************************/ /* The PAN Identifier for the device */ API_UINT16_UNION myPANID; /* current operating channel for the device */ uint8_t currentChannel = 0xFF; /* Holds the current connection mode info of device */ uint8_t ConnMode = DISABLE_ALL_CONN; /* Capability Information of the device */ uint8_t P2PCapacityInfo = RECEIVE_ON_WHEN_IDLE | SECURITY_INFO; /* Indicates the status of P2P stack */ volatile P2P_STATUS P2PStatus; /* State of the Mesh stack */ p2pStarState_t p2pStarCurrentState = INITIAL_STATE; /* Establishment connection info */ gEstConnection_t gEstConnectionInfo; /* Resync info */ resyncConnection_t resyncInfo; /* Search connection info */ gSearchConnection_t gSearchConnectionInfo; MAC_RECEIVED_PACKET MACRxPacket; /* Long Address of the Device */ uint8_t myLongAddress[MY_ADDRESS_LENGTH] = {EUI_0,EUI_1,EUI_2,EUI_3, EUI_4, EUI_5,EUI_6,EUI_7}; /* Tx Frame Queue Parameters */ MiQueue_t frameTxQueue; bool txCallbackReceived = true; TxFrame_t *sentFrame; static MIWI_TICK lastTxFrameTick; static uint32_t transaction_duration_us = 0; #ifdef ENABLE_ED_SCAN static bool noiseDetectionInProgress = false; #endif static SYS_Timer_t protocolTimer; uint8_t backupChannel = 0xFF; /* Time interval parameter for active scan */ uint16_t connectionTimeInterval = 0; defaultParametersRomOrRam_t *miwiDefaultRomOrRamParams; defaultParametersRamOnly_t *miwiDefaultRamOnlyParams; uint8_t MyindexinPC; uint8_t myConnectionIndex_in_PanCo; uint8_t LatestConnection; #if defined(PROTOCOL_STAR) /* Role of Device in Star Network */ DeviceRole_t role; /* Used by END_DEVICES to store total no of end_devices in network */ uint8_t end_nodes = 0; END_DEVICES_Unique_Short_Address END_DEVICES_Short_Address[CONNECTION_SIZE]; LinkFailureCallback_t linkFailureCallback; #if defined(ENABLE_PERIODIC_CONNECTIONTABLE_SHARE) /* Time interval parameter for broadcasting dev info */ uint16_t sharePeerDevInfoTimeInterval = 0; #endif #if defined(ENABLE_LINK_STATUS) uint16_t inActiveDeviceCheckTimeInterval = 0; uint16_t linkStatusTimeInterval = 0; uint8_t linkStatusFailureCount = 0; #endif #endif #ifdef ENABLE_SLEEP_FEATURE SYS_Timer_t rfdDataWaitTimer; #endif #if defined(ENABLE_NETWORK_FREEZER) ReconnectionCallback_t reconnectionCallback; #endif /* structure to store information for the received packet */ RECEIVED_MESSAGE rxMessage; /* total no of devices-In star used only by PAN COR to calculate no of END Devices */ uint8_t conn_size = 0; #if defined(PROTOCOL_STAR) /* Queue to store frame requiring application level ack from destination */ MiQueue_t appAckWaitDataQueue; #endif #if defined(ENABLE_ED_SCAN) /* Time interval parameter for active scan */ uint16_t edScanDurationTimeInterval = 0; #endif #if defined(ENABLE_ACTIVE_SCAN) /* Time interval parameter for active scan */ uint16_t activeScanDurationTimeInterval = 0; #endif /* Queue to store frame with MAC level ack from destination */ MiQueue_t macAckOnlyFrameQueue; /* Queue to store frame destined for sleeping devices */ MiQueue_t indirectFrameQueue; #ifdef ENABLE_SLEEP_FEATURE uint16_t dataRequestInterval = 0; #endif #ifdef ENABLE_FREQUENCY_AGILITY /* Optimial Channel Choosen for Channel Hopping */ uint8_t optimalChannel = 0xFF; uint16_t freqAgilityBroadcastInterval = 0; uint8_t freqAgilityRetries = 0; bool channelChangeInProgress = false; #endif PacketIndCallback_t pktRxcallback = NULL; #if defined(ENABLE_SECURITY) API_UINT32_UNION IncomingFrameCounter[CONNECTION_SIZE]; // If authentication is used, IncomingFrameCounter can prevent replay attack #endif /************************************** Function Prototypes****************************************************/ bool frameTransmit(INPUT bool Broadcast,API_UINT16_UNION DestinationPANID,INPUT uint8_t *DestinationAddress,INPUT bool isCommand,INPUT bool SecurityEnabled, INPUT uint8_t msgLen, INPUT uint8_t* msgPtr, INPUT uint8_t msghandle, INPUT bool ackReq, INPUT DataConf_callback_t ConfCallback); static void CommandConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer); static void frameTxCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer); static void frameParse(MAC_RECEIVED_PACKET *macRxPacket); #ifndef ENABLE_SLEEP_FEATURE static void connectionRespConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer); #endif static void connReqConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer); uint8_t AddConnection(uint8_t capacityInfo); static miwi_status_t sendConnectionRequest(void); static void protocolTimerInit(void); static void protocolTimerHandler(SYS_Timer_t *timer); #ifdef ENABLE_ACTIVE_SCAN static uint8_t ScanChannel(void); #endif #if defined(ENABLE_ED_SCAN) || defined(ENABLE_ACTIVE_SCAN) || defined(ENABLE_FREQUENCY_AGILITY) static inline uint32_t miwi_scan_duration_ticks(uint8_t scan_duration); #endif static void removeConnection(uint8_t index); #if defined(PROTOCOL_STAR) static SYS_Timer_t dataTimer; static void dataTimerHandler(SYS_Timer_t *timer); static void store_connection_tb(uint8_t *payload); static uint8_t Find_Index (uint8_t *DestAddr); static void startCompleteProcedure(bool timeronly); static void startLinkStatusTimer(void); static void sendLinkStatus(void); static void findInActiveDevices(void); static void handleLostConnection(void); void appAckWaitDataCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer); static void MiApp_BroadcastConnectionTable(void); #endif #ifdef ENABLE_SLEEP_FEATURE static void sendDataRequest(void); static void dataRequestConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer); static void rfdDataWaitTimerExpired(struct SYS_Timer_t *timer); #endif void macAckOnlyDataCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer); #ifdef ENABLE_FREQUENCY_AGILITY static void StartChannelHopping(void); static void channelHopCmdCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer); #endif /********************* Function Definitions *******************************************/ miwi_status_t MiApp_ProtocolInit(defaultParametersRomOrRam_t *defaultRomOrRamParams, defaultParametersRamOnly_t *defaultRamOnlyParams) { uint8_t i = 0; uint16_t broadcastAddress = 0xFFFF; miwi_status_t initStatus = SUCCESS; MACINIT_PARAM initValue; /* Clear all status bits */ P2PStatus.Val = 0;//TODO /* Store the ROmRAM parameter references */ miwiDefaultRomOrRamParams = defaultRomOrRamParams; miwiDefaultRamOnlyParams = defaultRamOnlyParams; /* Initialize PANID */ myPANID.Val = 0; #if defined(ENABLE_NETWORK_FREEZER) /* Restore Network Information Base from Non Volatile Memory - If it fails, initialize the PDS Items */ if (defaultRomOrRamParams->networkFreezerRestore) { if (!PDS_IsAbleToRestore(MIWI_ALL_MEMORY_MEM_ID) || !PDS_Restore(MIWI_ALL_MEMORY_MEM_ID)) { PDS_InitItems(); } } /* If Restored properly, valid PanId will be available */ if (myPANID.Val != 0x0000 && myPANID.Val != 0xFFFF) { p2pStarCurrentState = IN_NETWORK_STATE; initStatus = RECONNECTED; /* Inform application reconnection success */ if (NULL != reconnectionCallback) reconnectionCallback(SUCCESS); } else #endif { /* Stack is initialized, change state to INIT_STATE */ p2pStarCurrentState = INIT_STATE; /* Initialize PANID */ myPANID.Val = MY_PAN_ID; #if defined(ENABLE_NETWORK_FREEZER) for(i = 0; i < CONNECTION_SIZE; i++) { defaultRomOrRamParams->ConnectionTable[i].status.Val = 0; memset(&defaultRomOrRamParams->ConnectionTable[i].Address, 0, LONG_ADDR_LEN); } #endif #if defined(ENABLE_SECURITY) for(i = 0; i < CONNECTION_SIZE; i++) { IncomingFrameCounter[i].Val = 0; } #endif } initValue.PAddress = myLongAddress; initValue.actionFlags.bits.CCAEnable = 1; initValue.actionFlags.bits.PAddrLength = MY_ADDRESS_LENGTH; #if defined(ENABLE_NETWORK_FREEZER) initValue.actionFlags.bits.NetworkFreezer = 1; #else initValue.actionFlags.bits.NetworkFreezer = 0; #endif initValue.actionFlags.bits.RepeaterMode = 0; MiMAC_Init(initValue); if (currentChannel != 0xFF) MiApp_Set(CHANNEL, ¤tChannel); MiMAC_SetAltAddress((uint8_t *)&broadcastAddress, (uint8_t *)&myPANID.Val); P2PCapacityInfo |= (ConnMode << 4); /* Initialize Protocol Timer */ protocolTimerInit(); #if defined(PROTOCOL_STAR) miQueueInit(&appAckWaitDataQueue); #endif miQueueInit(&macAckOnlyFrameQueue); miQueueInit(&indirectFrameQueue); if (IN_NETWORK_STATE == p2pStarCurrentState) { #if defined(PROTOCOL_STAR) if (PAN_COORD == role) { /* Procedures for PAN Coordinator*/ startCompleteProcedure(true); } else { /* start Link Status Timer */ startLinkStatusTimer(); #ifdef ENABLE_SLEEP_FEATURE /* Start data request timer upon network freezer restore */ dataRequestInterval = RFD_WAKEUP_INTERVAL; #endif } #else #ifdef ENABLE_SLEEP_FEATURE /* Start data request timer upon network freezer restore */ dataRequestInterval = RFD_WAKEUP_INTERVAL; #endif #endif } (void)i; return initStatus; } bool MiApp_Get(miwi_params_t id, uint8_t *value) { switch(id) { case CHANNEL: { *value = currentChannel; return true; } break; case PANID: { value[0] = myPANID.Val; value[1] = myPANID.Val >> 8; return true; } break; default: break; } return false; } bool MiApp_Set(miwi_params_t id, uint8_t *value) { switch(id) { case CHANNEL: { if( MiMAC_Set(MAC_CHANNEL, value)) { currentChannel = *value; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CURRENT_CHANNEL_ID); #endif return true; } } break; default: break; } return false; } /************************************************************************************ * Function: * void MiApp_ConnectionMode(uint8_t Mode) * * Summary: * This function set the current connection mode. * * Description: * This is the primary user interface function for the application layer to * configure the way that the host device accept connection request. * * PreCondition: * Protocol initialization has been done. * * Parameters: * uint8_t Mode - The mode to accept connection request. The privilege for those modes * decreases gradually as defined. The higher privilege mode has all the * rights of the lower privilege modes. * The possible modes are * * ENABLE_ALL_CONN Enable response to all connection request * * ENABLE_PREV_CONN Enable response to connection request * from device already in the connection * table. * * ENABLE_ACTIVE_SCAN_RSP Enable response to active scan only * * DISABLE_ALL_CONN Disable response to connection request, including * an acitve scan request. * * Returns: * None * * Example: * * // Enable all connection request * MiApp_ConnectionMode(ENABLE_ALL_CONN); * * * Remarks: * None * *****************************************************************************************/ void MiApp_ConnectionMode(INPUT uint8_t Mode) { if( Mode > 3 ) { return; } ConnMode = Mode; P2PCapacityInfo = (P2PCapacityInfo & 0x0F) | (ConnMode << 4); #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CONNECTION_MODE_ID); #endif } #ifdef ENABLE_ACTIVE_SCAN static void sendScanConfirm(void) { /* Revert back working channel */ currentChannel = backupChannel; if(currentChannel!= 0xFF) { MiApp_Set(CHANNEL, &backupChannel); } if (p2pStarCurrentState == SEARCHING_NETWORK) { /* Change state while completing search */ p2pStarCurrentState = SEARCH_COMPLETE; /* All channels were scanned. Send confirmation to the upper layer. */ gSearchConnectionInfo.gSearchConfCallback(gSearchConnectionInfo.activeScanResultIndex, (uint8_t*)miwiDefaultRomOrRamParams->ActiveScanResults); gSearchConnectionInfo.gSearchConfCallback = NULL; } else if (p2pStarCurrentState == RESYNC_IN_PROGRESS) { if (resyncInfo.resyncTimes > 0) { --resyncInfo.resyncTimes; gSearchConnectionInfo.channelMap = resyncInfo.channelMap; ScanChannel(); } else { /* Change state while completing search */ p2pStarCurrentState = resyncInfo.backupState; resyncInfo.confCallback(backupChannel, FAILURE); resyncInfo.confCallback = NULL; } } } /************************************************************************************ * Function: * static void scanDurationExpired(void) * * Summary: * This callback function is called when scan timer is expired, so * it tries to scan the next channel if available * *****************************************************************************************/ static void scanDurationExpired(void) { uint8_t status; /* Remove the scanned channel from unscanned channels list */ gSearchConnectionInfo.channelMap &= ~(1UL << currentChannel); status = ScanChannel(); if ( status != SUCCESS) { /* Send Scan Confirmation to upper layer */ sendScanConfirm(); } } /************************************************************************************ * Function: * static void ActiveScanReqConfcb(uint8_t handle, miwi_status_t status, uint8_t* msgPointer) * * Summary: * This callback function starts the search timer if beacon is sent successfully * * Parameters/Returns: * uint8_t handle, miwi_status_t status, uint8_t* msgPointer *****************************************************************************************/ static void ActiveScanReqConfcb(uint8_t handle, miwi_status_t confstatus, uint8_t* msgPointer) { MiMem_Free(msgPointer); if (SUCCESS == confstatus) { /* Start the timer for scan Duration time */ activeScanDurationTimeInterval = (miwi_scan_duration_ticks(gSearchConnectionInfo.scanDuration)/1000000) + 1; } else { /* Remove the scanned channel from unscanned channels list */ gSearchConnectionInfo.channelMap &= ~(1UL << currentChannel); /* Beacon request is not sent successfully , then try again */ uint8_t status = ScanChannel(); if ( status != SUCCESS) { /* Send Scan Confirmation to upper layer */ sendScanConfirm(); } } } /************************************************************************************ * Function: * static uint8_t initiateBeaconReq(void) * * Summary: * This function sends the beacon request * * Parameters/Returns: * status of the request *****************************************************************************************/ static uint8_t initiateActiveScanReq(void) { uint8_t dataLen = 0; uint8_t *dataPtr = NULL; API_UINT16_UNION broadcastPANID; dataPtr = MiMem_Alloc(PACKETLEN_P2P_ACTIVE_SCAN_REQUEST); if (NULL == dataPtr) return MEMORY_UNAVAILABLE; /* Construct the P2P Active Scan Request */ dataPtr[dataLen++] = CMD_P2P_ACTIVE_SCAN_REQUEST; dataPtr[dataLen++] = currentChannel; broadcastPANID.Val = 0xFFFF; /* Initiate the frame transmission */ if (SEARCHING_NETWORK == p2pStarCurrentState) { frameTransmit(true, broadcastPANID, NULL, true, false, dataLen, dataPtr,0, true, ActiveScanReqConfcb); } else if (RESYNC_IN_PROGRESS == p2pStarCurrentState) { frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[resyncInfo.connectionIndex].Address, true, false, dataLen, dataPtr,0, true, ActiveScanReqConfcb); } else { return FAILURE; } return SUCCESS; } /************************************************************************************ * Function: * static uint8_t ScanChannel(searchConf_t* searchConfirm) * * Summary: * This function does the channel scanning by initiating beacon req * * Parameters: * searchConf_t* searchConfirm - confirm of search * Returns: * uint8_t - status of operation *****************************************************************************************/ static uint8_t ScanChannel(void) { bool channelSetStatus = false; uint8_t curChannel = 0; /* Find the next unscanned channel to scan and initiate the scan, */ for (curChannel = currentChannel; curChannel <= 26; curChannel++) { if ((gSearchConnectionInfo.channelMap & (1UL << curChannel)) != 0) { /* Set the selected channel */ currentChannel = curChannel; channelSetStatus = MiApp_Set(CHANNEL, ¤tChannel); if (channelSetStatus) { return (initiateActiveScanReq()); } } } /* Send Scan Confirmation to upper layer */ sendScanConfirm(); return SUCCESS; } /************************************************************************************ * Function: * uint8_t MiApp_SearchConnection(uint8_t ScanDuartion, uint32_t ChannelMap, * SearchConnectionConf_callback_t ConfCallback) * * Summary: * This function perform an active scan to locate operating PANs in the * neighborhood. * * Description: * This is the primary user interface function for the application layer to * perform an active scan. After this function call, all active scan response * will be stored in the global variable ActiveScanResults in the format of * structure ACTIVE_SCAN_RESULT. The return value indicates the total number * of valid active scan response in the active scan result array. * * PreCondition: * Protocol initialization has been done. * * Parameters: * uint8_t ScanDuration - The maximum time to perform scan on single channel. The * value is from 5 to 14. The real time to perform scan can * be calculated in following formula from IEEE 802.15.4 * specification * 960 * (2^ScanDuration + 1) * 10^(-6) second * uint32_t ChannelMap - The bit map of channels to perform noise scan. The 32-bit * double word parameter use one bit to represent corresponding * channels from 0 to 31. For instance, 0x00000003 represent to * scan channel 0 and channel 1. * SearchConnectionConf_callback_t ConfCallback - The callback routine which will be called upon * the initiated connection procedure is performed * * Returns: * The number of valid active scan response stored in the global variable ActiveScanResults. * * Example: * * // Perform an active scan on all possible channels * NumOfActiveScanResponse = MiApp_SearchConnection(10, 0xFFFFFFFF, callback); * * * Remarks: * None * *****************************************************************************************/ uint8_t MiApp_SearchConnection(INPUT uint8_t ScanDuration, INPUT uint32_t ChannelMap, SearchConnectionConf_callback_t ConfCallback) { uint32_t supportedChannelMap = 0; uint32_t index = 1; /* Check the given scan duration is within the range and callback is not NULL*/ if ((ScanDuration > MAX_SCAN_DURATION) || (NULL == ConfCallback)) return FAILURE; /* If state is not Initial state or search complete , don't process further */ if ((p2pStarCurrentState == INITIAL_STATE)) { return FAILURE; } /* Change state while processing start connection */ p2pStarCurrentState = SEARCHING_NETWORK; /* Store scan duration */ gSearchConnectionInfo.scanDuration = ScanDuration; /* Store scan channels */ gSearchConnectionInfo.channelMap = ChannelMap; /* Allocate memory for scan and confirmation */ gSearchConnectionInfo.gSearchConfCallback = ConfCallback; /* Initialize the scan confirm parameters */ for(uint8_t i = 0; i < ACTIVE_SCAN_RESULT_SIZE; i++) { miwiDefaultRomOrRamParams->ActiveScanResults[i].Channel = 0xFF; } gSearchConnectionInfo.activeScanResultIndex = 0; /* Get the supported channel and find the first channel to start scan */ supportedChannelMap = MiMAC_GetPHYChannelInfo(); /* Backup currentChannel */ backupChannel = currentChannel; currentChannel = 0; while (!(index & supportedChannelMap)) { // Unset current bit and set the next bit in 'i' index = index << 1; // increment position ++currentChannel; } /* Start the scan procedure */ return (ScanChannel()); } #endif #if defined(ENABLE_ED_SCAN) || defined(ENABLE_ACTIVE_SCAN) || defined(ENABLE_FREQUENCY_AGILITY) /* Scan Duration formula for P2P Connection: 60 * 16 * (2 ^ n + 1) symbols */ static inline uint32_t miwi_scan_duration_ticks(uint8_t scan_duration) { uint32_t scan_symbols; scan_symbols = ABASESUPERFRAMEDURATION *((1< maxRSSI ) { maxRSSI = RSSIcheck; } if( false == noiseDetectionInProgress) { // if scan time exceed scan duration, prepare to scan the next channel break; } SYS_TimerTaskHandler(); } if( maxRSSI < minRSSI ) { minRSSI = maxRSSI; OptimalChannel = i; if( NoiseLevel ) { *NoiseLevel = minRSSI; } } } i++; } return OptimalChannel; } #endif static void startCompleteProcedure(bool timeronly) { if (false == timeronly) { #if defined (PROTOCOL_STAR) /* Set Role to PANC */ role = PAN_COORD; #endif /* Set the state to in network state*/ p2pStarCurrentState = IN_NETWORK_STATE; #if defined(ENABLE_NETWORK_FREEZER) /*Store Network Information in Persistent Data Server */ PDS_Store(MIWI_ALL_MEMORY_MEM_ID); #endif } #if defined (PROTOCOL_STAR) #if defined(ENABLE_LINK_STATUS) /* Start the timer for Finding in active devices and initiating remove connection if found any */ inActiveDeviceCheckTimeInterval = FIND_INACTIVE_DEVICE_TIMEOUT; #endif #if defined(ENABLE_PERIODIC_CONNECTIONTABLE_SHARE) /* Start the timer for sharing the connection table periodically */ sharePeerDevInfoTimeInterval = SHARE_PEER_DEVICE_INFO_TIMEOUT; #endif #endif } /************************************************************************************************ * Function: * bool MiApp_StartConnection(uint8_t Mode, uint8_t ScanDuration, uint32_t ChannelMap, * connectionConf_callback_t ConfCallback) * * Summary: * This function start a PAN without connected to any other devices * * Description: * This is the primary user interface function for the application layer to * a PAN. Usually, this function is called by the PAN Coordinator who is the * first in the PAN. The PAN Coordinator may start the PAN after a noise scan * if specified in the input mode. * * PreCondition: * Protocol initialization has been done. * * Parameters: * uint8_t Mode - Whether to start a PAN after a noise scan. Possible modes are * * START_CONN_DIRECT Start PAN directly without noise scan * * START_CONN_ENERGY_SCN Perform an energy scan first, then * start the PAN on the channel with least * noise. * * START_CONN_CS_SCN Perform a carrier-sense scan first, * then start the PAN on the channel with * least noise. * uint8_t ScanDuration - The maximum time to perform scan on single channel. The * value is from 5 to 14. The real time to perform scan can * be calculated in following formula from IEEE 802.15.4 * specification: * 960 * (2^ScanDuration + 1) * 10^(-6) second * ScanDuration is discarded if the connection mode is * START_CONN_DIRECT. * uint32_t ChannelMap - The bit map of channels to perform noise scan. The 32-bit * double word parameter use one bit to represent corresponding * channels from 0 to 31. For instance, 0x00000003 represent to * scan channel 0 and channel 1. ChannelMap is discarded if the * connection mode is START_CONN_DIRECT. * * connectionConf_callback_t ConfCallback - The callback routine which will be called upon * the initiated connection procedure is performed * Returns: * a boolean to indicate if PAN has been started successfully. * * Example: * * // start the PAN on the least noisy channel after scanning all possible channels. * MiApp_StartConnection(START_CONN_ENERGY_SCN, 10, 0xFFFFFFFF, callback); * * * Remarks: * None * *************************************************************************************************/ bool MiApp_StartConnection(uint8_t Mode, uint8_t ScanDuration, uint32_t ChannelMap, connectionConf_callback_t ConfCallback) { /* If state is not correct state , don't process further */ if ((p2pStarCurrentState != INIT_STATE) && (p2pStarCurrentState != SEARCH_COMPLETE)) return false; /* Change state while processing start connection */ p2pStarCurrentState = STARTING_NETWORK; switch(Mode) { case START_CONN_DIRECT: { uint8_t channel = 0; uint32_t index = 1; uint16_t tmp = 0xFFFF; #if MY_PAN_ID == 0xFFFF myPANID.Val = PHY_RandomReq(); #else myPANID.Val = MY_PAN_ID; #endif MiMAC_SetAltAddress((uint8_t *)&tmp, (uint8_t *)&myPANID.Val); while (!(index & ChannelMap)) { // Unset current bit and set the next bit in 'i' index = index << 1; // increment position ++channel; } /* Set the best channel */ MiApp_Set(CHANNEL, &channel); /* Procedures if start operation is success */ startCompleteProcedure(false); /* Send Confirmation to caller */ ConfCallback(SUCCESS); return true; } break; case START_CONN_ENERGY_SCN: #if defined(ENABLE_ED_SCAN) { uint8_t channel; uint8_t RSSIValue; uint16_t tmp = 0xFFFF; #if MY_PAN_ID == 0xFFFF myPANID.Val = PHY_RandomReq(); #else myPANID.Val = MY_PAN_ID; #endif /* Set Short Address and PAN ID */ MiMAC_SetAltAddress((uint8_t *)&tmp, (uint8_t *)&myPANID.Val); /* Identify the best channel by doing energy detection scan */ channel = MiApp_NoiseDetection(ChannelMap, ScanDuration, NOISE_DETECT_ENERGY, &RSSIValue); /* Set the best channel */ MiApp_Set(CHANNEL, &channel); /* Procedures if start operation is success */ startCompleteProcedure(false); /* Send Confirmation to caller */ ConfCallback(SUCCESS); return true; } #else /* Send Confirmation to caller */ ConfCallback(FAILURE); return false; #endif case START_CONN_CS_SCN: { /* Carrier sense scan is not supported for current available transceivers */ ConfCallback(FAILURE); /* Set the state to Initial since failure to start network */ p2pStarCurrentState = INIT_STATE; return false; } break; default: break; } ConfCallback(FAILURE); /* Set the state to Initial since failure to start network */ p2pStarCurrentState = INIT_STATE; return false; } /************************************************************************************ * Function: * static void connReqConfCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer) * * Summary: * This callback function frees the memory used for the command sent * and starts connection response timer if packet sent successfully otherwise it post confirm * * Parameters/Returns: * uint8_t handle, miwi_status_t status, uint8_t* msgPointer *****************************************************************************************/ static void connReqConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { MiMem_Free(msgPointer); --gEstConnectionInfo.connectionRetries; if (SUCCESS == status) { /* Start the timer to wait for connection response */ connectionTimeInterval = CONNECTION_INTERVAL; #ifdef ENABLE_SLEEP_FEATURE rfdDataWaitTimer.handler = rfdDataWaitTimerExpired; rfdDataWaitTimer.timeout = RFD_DATA_WAIT / 1000; rfdDataWaitTimer.interval = RFD_DATA_WAIT / 1000; rfdDataWaitTimer.mode = SYS_TIMER_INTERVAL_MODE; #endif } else { sendConnectionRequest(); } } /************************************************************************************ * Function: * miwi_status_t sendConnectionRequest(void) * * Summary: * This function sends a connection request * * Parameters/Returns: * miwi_status_t - the status of the send operation *****************************************************************************************/ static miwi_status_t sendConnectionRequest(void) { if ((gEstConnectionInfo.connectionRetries > 0) && (ESTABLISHING_NETWORK == p2pStarCurrentState)) { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; dataPtr = MiMem_Alloc(PACKETLEN_P2P_CONNECTION_REQUEST); if (NULL == dataPtr) return MEMORY_UNAVAILABLE; /* Construct the full frame with command id and payload */ dataPtr[dataLen++] = CMD_P2P_CONNECTION_REQUEST; dataPtr[dataLen++] = currentChannel; dataPtr[dataLen++] = P2PCapacityInfo; #if defined(PROTOCOL_STAR) && defined(MAKE_ENDDEVICE_PERMANENT) dataPtr[dataLen++] = 0xAA; #endif #if ADDITIONAL_NODE_ID_SIZE > 0 for(uint8_t i = 0; i < ADDITIONAL_NODE_ID_SIZE; i++) { dataPtr[dataLen++] = miwiDefaultRomOrRamParams->AdditionalNodeID[i]; } #endif #if defined(ENABLE_ACTIVE_SCAN) uint16_t DestinationAddress16 = ((gEstConnectionInfo.address[1] << 8) + gEstConnectionInfo.address[0]); if( DestinationAddress16 == 0xFFFF ) { if(frameTransmit(true, myPANID, NULL, true, false, dataLen, dataPtr,0, true, connReqConfCallback)) return SUCCESS; else return MEMORY_UNAVAILABLE; } else { uint8_t i,j; bool deviceFound = false; for(i = 0; i < ACTIVE_SCAN_RESULT_SIZE; i++) { deviceFound = true; for(j = 0; j < MY_ADDRESS_LENGTH; j++) { if (gEstConnectionInfo.address[j] != miwiDefaultRomOrRamParams->ActiveScanResults[i].Address[j]) { deviceFound = false; break; } } if (deviceFound) { break; } } if (deviceFound) { if (frameTransmit(false, miwiDefaultRomOrRamParams->ActiveScanResults[i].PANID, miwiDefaultRomOrRamParams->ActiveScanResults[i].Address, true, false, dataLen, dataPtr,0, true, connReqConfCallback)) return SUCCESS; else return MEMORY_UNAVAILABLE; } else { /* Free the allocated memory */ MiMem_Free(dataPtr); /* Change back state */ p2pStarCurrentState = gEstConnectionInfo.backupState; /* Post Confirmation since address not found in active scan results list */ gEstConnectionInfo.confCallback(ADDR_NOT_FOUND_IN_SCANNED_LIST); gEstConnectionInfo.confCallback = NULL; return ADDR_NOT_FOUND_IN_SCANNED_LIST; } } #else if(frameTransmit(true, myPANID, NULL, true, false, dataLen, dataPtr,0, true, connReqConfCallback)) return SUCCESS; else return MEMORY_UNAVAILABLE; #endif } else { if ((gEstConnectionInfo.status == ALREADY_EXISTS) || (gEstConnectionInfo.status == SUCCESS)) { /* Set the state to in network state*/ p2pStarCurrentState = IN_NETWORK_STATE; /* Post Confirmation */ gEstConnectionInfo.confCallback(gEstConnectionInfo.status); } else { /* Change back state */ p2pStarCurrentState = gEstConnectionInfo.backupState; /* Post Confirmation since no connection response received */ gEstConnectionInfo.confCallback(FAILURE); } gEstConnectionInfo.confCallback = NULL; return TIMER_EXPIRED; } } uint8_t MiApp_EstablishConnection(uint8_t Channel, uint8_t addr_len, uint8_t *addr, uint8_t Capability_info, connectionConf_callback_t ConfCallback) { /* If state is initial state , don't process further */ if (p2pStarCurrentState == INITIAL_STATE) return 0; ConnMode = ENABLE_ALL_CONN; /* Store the Establish connection Informations */ gEstConnectionInfo.confCallback = ConfCallback; gEstConnectionInfo.addrLen = addr_len; memcpy(gEstConnectionInfo.address, addr, addr_len); gEstConnectionInfo.backupState = p2pStarCurrentState; gEstConnectionInfo.status = SCAN_NO_BEACON; /* Assign connection retries */ gEstConnectionInfo.connectionRetries = CONNECTION_RETRY_TIMES; /* Change state while processing establish connection */ p2pStarCurrentState = ESTABLISHING_NETWORK; MiApp_Set(CHANNEL, &Channel); return sendConnectionRequest(); } #if !defined(TARGET_SMALL) static void removeConnection(uint8_t index) { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; dataPtr = MiMem_Alloc(PACKETLEN_P2P_CONNECTION_REMOVAL_REQUEST); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_P2P_CONNECTION_REMOVAL_REQUEST; frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[index].Address, true, false, dataLen, dataPtr,0, true, CommandConfCallback); } /********************************************************************* * Function: * void MiApp_RemoveConnection(uint8_t ConnectionIndex) * * Summary: * This function remove connection(s) in connection table * * Description: * This is the primary user interface function to disconnect connection(s). * For a P2P protocol, it simply remove the connection. For a network protocol, * if the device referred by the input parameter is the parent of the device * calling this function, the calling device will get out of network along with * its children. If the device referred by the input parameter is children of * the device calling this function, the target device will get out of network. * * PreCondition: * Transceiver has been initialized. Node has establish * one or more connections * * Parameters: * uint8_t ConnectionIndex - The index of the connection in the * connection table to be removed * * Returns: * None * * Example: * * MiApp_RemoveConnection(0x00); * * * Remarks: * None * ********************************************************************/ void MiApp_RemoveConnection(INPUT uint8_t ConnectionIndex) { if( ConnectionIndex == 0xFF ) { uint8_t i; for(i = 0; i < CONNECTION_SIZE; i++) { if( miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid ) { removeConnection(i); } miwiDefaultRomOrRamParams->ConnectionTable[i].status.Val = 0; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CONNECTION_TABLE_ID); #endif } } else if( miwiDefaultRomOrRamParams->ConnectionTable[ConnectionIndex].status.bits.isValid ) { removeConnection(ConnectionIndex); miwiDefaultRomOrRamParams->ConnectionTable[ConnectionIndex].status.Val = 0; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CONNECTION_TABLE_ID); #endif } } #endif bool MiApp_SubscribeDataIndicationCallback(PacketIndCallback_t callback) { if (NULL != callback) { pktRxcallback = callback; return true; } return false; } #ifdef ENABLE_ACTIVE_SCAN bool MiApp_ResyncConnection(INPUT uint8_t ConnectionIndex, INPUT uint32_t ChannelMap, resyncConnection_callback_t callback) { /* Backup the current channel */ backupChannel = currentChannel; /* Store the resync connection Informations */ resyncInfo.confCallback = callback; resyncInfo.channelMap = ChannelMap; resyncInfo.connectionIndex = ConnectionIndex; resyncInfo.backupState = p2pStarCurrentState; /* Assign connection retries */ resyncInfo.resyncTimes = RESYNC_TIMES; /* Change state while processing resync connection */ p2pStarCurrentState = RESYNC_IN_PROGRESS; /* Store channelMap and scan duration in searchinfo used for manipulation */ gSearchConnectionInfo.channelMap = ChannelMap; gSearchConnectionInfo.scanDuration = 9; --resyncInfo.resyncTimes; /* Start the scan procedure */ return (ScanChannel()); } #endif bool MiApp_SendData(uint8_t addr_len, uint8_t *addr, uint8_t msglen, uint8_t *msgpointer, uint8_t msghandle, bool ackReq, DataConf_callback_t ConfCallback) { P2PStarDataFrame_t *dataFramePtr = NULL; if (IN_NETWORK_STATE == p2pStarCurrentState && MAX_PAYLOAD >= msglen) { bool broadcast = false; uint16_t DestinationAddress16 = ((addr[1] << 8) + addr[0]); /* Check if the frame is broadcast */ if(addr_len == 2 && (DestinationAddress16 == 0xFFFF)) { broadcast = true; #ifdef ENABLE_INDIRECT_MESSAGE uint8_t i; /* Add individual indirect entry for each sleeping device in connection table */ for(i = 0; i < CONNECTION_SIZE; i++) { if(miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid && miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.RXOnWhenIdle == 0 && 50 < MiMem_PercentageOfFreeBuffers()) { dataFramePtr = (P2PStarDataFrame_t *)MiMem_Alloc(sizeof(P2PStarDataFrame_t)); if (NULL == dataFramePtr) { return false; } dataFramePtr->dataFrame.confCallback = ConfCallback; memcpy(&(dataFramePtr->dataFrame.destAddress), miwiDefaultRomOrRamParams->ConnectionTable[i].Address, MY_ADDRESS_LENGTH); dataFramePtr->dataFrame.msghandle = msghandle; dataFramePtr->dataFrame.msgLength = msglen; memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); dataFramePtr->dataFrame.timeout = INDIRECT_MESSAGE_TIMEOUT; dataFramePtr->dataFrame.ackReq = 0; dataFramePtr->dataFrame.broadcast = 1; miQueueAppend(&indirectFrameQueue, (miQueueBuffer_t*)dataFramePtr); } } #endif /* Also send the broadcast for all the Non-sleeping end devices */ dataFramePtr = (P2PStarDataFrame_t *)MiMem_Alloc(sizeof(P2PStarDataFrame_t)); if (NULL == dataFramePtr) { return false; } dataFramePtr->dataFrame.confCallback = ConfCallback; dataFramePtr->dataFrame.msghandle = msghandle; dataFramePtr->dataFrame.msgLength = msglen; dataFramePtr->dataFrame.timeout = 0; memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); frameTransmit(broadcast, myPANID, addr, false, false, msglen, dataFramePtr->dataFrame.msg, msghandle, 0, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t*)dataFramePtr); return true; } else { #ifdef ENABLE_INDIRECT_MESSAGE uint8_t i; for(i = 0; i < CONNECTION_SIZE; i++) { // check if RX on when idle if(miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid && (miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.RXOnWhenIdle == 0) && isSameAddress(addr, miwiDefaultRomOrRamParams->ConnectionTable[i].Address) ) { dataFramePtr = (P2PStarDataFrame_t *)MiMem_Alloc(sizeof(P2PStarDataFrame_t)); if (NULL == dataFramePtr) { return false; } dataFramePtr->dataFrame.confCallback = ConfCallback; memcpy(&(dataFramePtr->dataFrame.destAddress), addr, MY_ADDRESS_LENGTH); dataFramePtr->dataFrame.msghandle = msghandle; dataFramePtr->dataFrame.msgLength = msglen; memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); dataFramePtr->dataFrame.timeout = INDIRECT_MESSAGE_TIMEOUT; dataFramePtr->dataFrame.ackReq = ackReq; miQueueAppend(&indirectFrameQueue, (miQueueBuffer_t*)dataFramePtr); return true; } } #endif } #if defined(ENABLE_ENHANCED_DATA_REQUEST) && defined(ENABLE_SLEEP_FEATURE) if( P2PStatus.bits.Sleeping ) { P2PStatus.bits.Enhanced_DR_SecEn = 0; return true; } #endif dataFramePtr = (P2PStarDataFrame_t *)MiMem_Alloc(sizeof(P2PStarDataFrame_t)); if (NULL == dataFramePtr) { return false; } dataFramePtr->dataFrame.confCallback = ConfCallback; memcpy(&(dataFramePtr->dataFrame.destAddress), addr, MY_ADDRESS_LENGTH); dataFramePtr->dataFrame.msghandle = msghandle; dataFramePtr->dataFrame.msgLength = msglen; dataFramePtr->dataFrame.timeout = 0; dataFramePtr->dataFrame.ackReq = ackReq; #if defined(PROTOCOL_STAR) if (END_DEVICE == role) { if (MY_ADDRESS_LENGTH == addr_len && isSameAddress(addr, miwiDefaultRomOrRamParams->ConnectionTable[0].Address)) { memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); frameTransmit(broadcast, myPANID, addr, false, false, msglen, dataFramePtr->dataFrame.msg, msghandle, ackReq, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t*)dataFramePtr); } else { // packet forward dataFramePtr->dataFrame.msg[0] = CMD_FORWRD_PACKET; dataFramePtr->dataFrame.msg[1] = addr[0]; dataFramePtr->dataFrame.msg[2] = addr[1]; dataFramePtr->dataFrame.msg[3] = addr[2]; memcpy(&(dataFramePtr->dataFrame.msg[4]), msgpointer, msglen); dataFramePtr->dataFrame.msgLength = msglen + 4; if (ackReq) { dataFramePtr->dataFrame.timeout = SW_ACK_TIMEOUT + 1; frameTransmit(broadcast, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[0].Address, true, false, dataFramePtr->dataFrame.msgLength, dataFramePtr->dataFrame.msg, msghandle, ackReq, appAckWaitDataCallback); miQueueAppend(&appAckWaitDataQueue, (miQueueBuffer_t*)dataFramePtr); SYS_TimerStart(&dataTimer); } else { frameTransmit(broadcast, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[0].Address, true, false, dataFramePtr->dataFrame.msgLength, dataFramePtr->dataFrame.msg, msghandle, ackReq, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t*)dataFramePtr); } } } else { memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); frameTransmit(broadcast, myPANID, addr, false, false, msglen, dataFramePtr->dataFrame.msg, msghandle, ackReq, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t*)dataFramePtr); } #else memcpy(&(dataFramePtr->dataFrame.msg), msgpointer, msglen); frameTransmit(broadcast, myPANID, addr, false, false, msglen, dataFramePtr->dataFrame.msg, msghandle, ackReq, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t*)dataFramePtr); #endif } return true; } void CommandConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { MiMem_Free(msgPointer); } static void frameTxCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer) { txCallbackReceived = true; DataConf_callback_t callback = sentFrame->txFrameEntry.frameConfCallback; if (NULL != callback) { callback(handle, status, msgPointer); } else { MiMem_Free(msgPointer); } MiMem_Free((uint8_t *)sentFrame); } /* Evaluate Total No of Peer Connection on a Node */ uint8_t Total_Connections(void) { uint8_t count=0 , i; for (i=0;iConnectionTable[i].Address[0] != 0x00 || miwiDefaultRomOrRamParams->ConnectionTable[i].Address[1] != 0x00 || miwiDefaultRomOrRamParams->ConnectionTable[i].Address[2] != 0x00) { count++; } #else if (miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid) { count++; } #endif } return count; } #if defined(ENABLE_HAND_SHAKE) /********************************************************************* * uint8_t AddConnection(void) * * Overview: This function create a new P2P connection entry * * PreCondition: A P2P Connection Request or Response has been * received and stored in rxMessage structure * * Input: None * * Output: * The index of the P2P Connection Entry for the newly added * connection * * Side Effects: A new P2P Connection Entry is created. The search * connection operation ends if an entry is added * successfully * ********************************************************************/ uint8_t AddConnection(uint8_t capacityInfo) { uint8_t i; uint8_t status = STATUS_SUCCESS; uint8_t connectionSlot = 0xFF; /* if no peerinfo attached, this is only an active scan request, so do not save the source device's info */ #ifdef ENABLE_ACTIVE_SCAN if( rxMessage.PayloadSize < 3 ) { return STATUS_ACTIVE_SCAN; } #endif /* loop through all entry and locate an proper slot */ for(i = 0; i < CONNECTION_SIZE; i++) { /* check if the entry is valid */ if( miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid ) { // check if the entry address matches source address of current received packet if( isSameAddress(rxMessage.SourceAddress, miwiDefaultRomOrRamParams->ConnectionTable[i].Address) ) { connectionSlot = i; status = STATUS_EXISTS; break; } } else if( connectionSlot == 0xFF ) { /* store the first empty slot */ connectionSlot = i; } } if( connectionSlot == 0xFF ) { return STATUS_NOT_ENOUGH_SPACE; } else { if( ConnMode >= ENABLE_PREV_CONN ) { return status; } MyindexinPC = connectionSlot; /* store the source address */ for(i = 0; i < 8; i++) { miwiDefaultRomOrRamParams->ConnectionTable[connectionSlot].Address[i] = rxMessage.SourceAddress[i]; } /* store the capacity info and validate the entry */ miwiDefaultRomOrRamParams->ConnectionTable[connectionSlot].status.bits.isValid = 1; miwiDefaultRomOrRamParams->ConnectionTable[connectionSlot].status.bits.RXOnWhenIdle = (capacityInfo & 0x01); /* store possible additional connection payload */ #if ADDITIONAL_NODE_ID_SIZE > 0 for(i = 0; i < ADDITIONAL_NODE_ID_SIZE; i++) { miwiDefaultRomOrRamParams->ConnectionTable[connectionSlot].PeerInfo[i] = rxMessage.Payload[3+i]; } #endif #ifdef ENABLE_SECURITY /* if security is enabled, clear the incoming frame control */ IncomingFrameCounter[connectionSlot].Val = 0; #endif LatestConnection = connectionSlot; } conn_size = Total_Connections(); #if defined (ENABLE_NETWORK_FREEZER) PDS_Store(PDS_EDC_ID); #endif return status; } #endif /********************************************************************* * BOOL isSameAddress(uint8_t *Address1, uint8_t *Address2) * * Overview: This function compares two long addresses and returns * the boolean to indicate if they are the same * * PreCondition: * * Input: * Address1 - Pointer to the first long address to be compared * Address2 - Pointer to the second long address to be compared * * Output: * If the two address are the same * * Side Effects: * ********************************************************************/ bool isSameAddress(INPUT uint8_t *Address1, INPUT uint8_t *Address2) { uint8_t i; for(i = 0; i < MY_ADDRESS_LENGTH; i++) { if( Address1[i] != Address2[i] ) { return false; } } return true; } #ifndef ENABLE_SLEEP_FEATURE static void connectionRespConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { /* Free the Frame Memory */ MiMem_Free(msgPointer); #if defined(PROTOCOL_STAR) /* Broadcast connection table upon a device join */ MiApp_BroadcastConnectionTable(); #endif } #endif #if defined(PROTOCOL_STAR) #if defined(ENABLE_LINK_STATUS) static void linkStatusConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { MiMem_Free(msgPointer); if (SUCCESS != status) { if (linkStatusFailureCount >= MAX_LINK_STATUS_FAILURES) { /* Stop Timers */ linkStatusTimeInterval = 0; #ifdef ENABLE_SLEEP_FEATURE dataRequestInterval = 0; #endif if ((NULL != linkFailureCallback) && (p2pStarCurrentState != DISCONNECTED)) { linkFailureCallback(); } p2pStarCurrentState = DISCONNECTED; } ++linkStatusFailureCount; } else { linkStatusFailureCount = 0; } } static void sendLinkStatus(void) { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; /* Allocate memory for link status command */ dataPtr = MiMem_Alloc(PACKETLEN_CMD_IAM_ALIVE); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_IAM_ALIVE; /* Pan Co is @ index 0 of connection table of END_Device in a Star Network */ frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[0].Address, true, false, dataLen, dataPtr,0, true, linkStatusConfCallback); } static void findInActiveDevices(void) { uint8_t i; for (i = 0;i < CONNECTION_SIZE; i++) { if (miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid) { if (miwiDefaultRomOrRamParams->ConnectionTable[i].link_status == 0 && miwiDefaultRomOrRamParams->ConnectionTable[i].permanent_connections != 0xFF) { MiApp_RemoveConnection(i); } else { miwiDefaultRomOrRamParams->ConnectionTable[i].link_status = 0; } } } } static void startLinkStatusTimer(void) { /* Start the timer for sending link status periodically */ linkStatusTimeInterval = LINK_STATUS_TIMEOUT; } #endif #endif #ifdef ENABLE_SLEEP_FEATURE static void dataRequestConfCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { MiMem_Free(msgPointer); rfdDataWaitTimer.handler = rfdDataWaitTimerExpired; rfdDataWaitTimer.timeout = RFD_DATA_WAIT / 1000; rfdDataWaitTimer.interval = RFD_DATA_WAIT / 1000; rfdDataWaitTimer.mode = SYS_TIMER_INTERVAL_MODE; SYS_TimerStart(&rfdDataWaitTimer); } static void sendDataRequest(void) { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; /* Allocate memory for link status command */ dataPtr = MiMem_Alloc(PACKETLEN_MAC_DATA_REQUEST); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_MAC_DATA_REQUEST; /* Pan Co is @ index 0 of connection table of END_Device in a Star Network */ frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[0].Address, true, false, dataLen, dataPtr,0, true, dataRequestConfCallback); P2PStatus.bits.DataRequesting = 1; } #endif /****************************************************************************** * Function: * void frameParse(MAC_RECEIVED_PACKET *macRxPacket) * * Summary: * This function parses all the received frames * * Parameters: * MAC_RECEIVED_PACKET *macRxPacket - Pointer of the received MAC frame * * Returns: * None ******************************************************************************/ void frameParse(MAC_RECEIVED_PACKET *macRxPacket) { uint8_t i; /* Reject if Source PANID is not from my PANID if (macRxPacket->SourcePANID.Val != MY_PAN_ID) { return; } */ /* Parse the message and Fill the rxMessage structure */ rxMessage.flags.Val = 0; rxMessage.flags.bits.broadcast = MACRxPacket.flags.bits.broadcast; rxMessage.flags.bits.secEn = MACRxPacket.flags.bits.secEn; rxMessage.flags.bits.command = (MACRxPacket.flags.bits.packetType == PACKET_TYPE_COMMAND) ? 1:0; rxMessage.flags.bits.srcPrsnt = MACRxPacket.flags.bits.sourcePrsnt; /* Read Source Address if source present bit is set in flags */ if( MACRxPacket.flags.bits.sourcePrsnt ) { rxMessage.SourceAddress = MACRxPacket.SourceAddress; } rxMessage.SourcePANID.Val = MACRxPacket.SourcePANID.Val; rxMessage.PayloadSize = MACRxPacket.PayloadLen; rxMessage.Payload = MACRxPacket.Payload; rxMessage.PacketLQI = MACRxPacket.LQIValue; rxMessage.PacketRSSI = MACRxPacket.RSSIValue; /* Command Frames Handling */ if( rxMessage.flags.bits.command ) { switch( rxMessage.Payload[0] ) { case CMD_P2P_CONNECTION_REQUEST: { #ifndef ENABLE_SLEEP_FEATURE uint8_t* dataPtr = NULL; uint8_t dataLen = 0; #endif #if defined(PROTOCOL_STAR) if(PAN_COORD != role) { /* Ignore If not PANCoordinator -Important for star network */ return; } #endif #ifdef ENABLE_SLEEP_FEATURE /* If a device goes to sleep, it can only have one connection, as the result, it cannot accept new connection request */ return; #else uint8_t status = STATUS_SUCCESS; /* if new connection is not allowed, ignore the request */ if( ConnMode > ENABLE_PREV_CONN ) { return; } /* if channel does not math, it may be a sub-harmonics signal, ignore the request */ if( currentChannel != rxMessage.Payload[1] ) { return; } #if !defined(TARGET_SMALL) && defined(IEEE_802_15_4) /* if PANID does not match, ignore the request */ if( (rxMessage.SourcePANID.Val != 0xFFFF) && (rxMessage.SourcePANID.Val != myPANID.Val) && (rxMessage.PayloadSize > 2)) { status = STATUS_NOT_SAME_PAN; } else #endif { /* Request accepted, try to add the requesting device into P2P Connection Entry */ status = AddConnection(rxMessage.Payload[2]); } #if defined(PROTOCOL_STAR) && defined(ENABLE_LINK_STATUS) if (rxMessage.Payload[3] == 0xAA) { for (uint8_t p = 0 ;p ConnectionTable[p].Address) ) { miwiDefaultRomOrRamParams->ConnectionTable[p].permanent_connections = 0xFF; } } } #endif if( (ConnMode == ENABLE_PREV_CONN) && (status != STATUS_EXISTS && status != STATUS_ACTIVE_SCAN) ) { status = STATUS_NOT_PERMITTED; } if( (status == STATUS_SUCCESS || status == STATUS_EXISTS ) && MiApp_CB_AllowConnection(LatestConnection) == false ) { miwiDefaultRomOrRamParams->ConnectionTable[LatestConnection].status.Val = 0; status = STATUS_NOT_PERMITTED; } /* Prepare the P2P_CONNECTION_RESPONSE command */ dataPtr = MiMem_Alloc(TX_BUFFER_SIZE); if (NULL == dataPtr) return; /* Fill Connection Response Structure */ dataPtr[dataLen++] = CMD_P2P_CONNECTION_RESPONSE; dataPtr[dataLen++] = status; #if defined(PROTOCOL_STAR) dataPtr[dataLen++] = MyindexinPC; #endif if( status == STATUS_SUCCESS || status == STATUS_EXISTS ) { dataPtr[dataLen++] = P2PCapacityInfo; #if ADDITIONAL_NODE_ID_SIZE > 0 for(i = 0; i < ADDITIONAL_NODE_ID_SIZE; i++) { dataPtr[dataLen++] = miwiDefaultRomOrRamParams->AdditionalNodeID[i]; } #endif } /* Unicast the P2P_CONNECTION_RESPONSE to the requesting device */ #ifdef TARGET_SMALL frameTransmit(false, myPANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr, 0, true, connectionRespConfCallback); #else frameTransmit(false, rxMessage.SourcePANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr, 0, true, connectionRespConfCallback); #endif #if defined(ENABLE_NETWORK_FREEZER) if( status == STATUS_SUCCESS ) { PDS_Store(PDS_CONNECTION_TABLE_ID); } #endif #endif } break; case CMD_P2P_CONNECTION_RESPONSE: { if(ESTABLISHING_NETWORK != p2pStarCurrentState) { /* Ignore If not in establish state */ return; } switch( rxMessage.Payload[1] ) { case STATUS_SUCCESS: case STATUS_EXISTS: if( myPANID.Val == 0xFFFF ) { uint16_t broadcastAddr = 0xFFFF; myPANID.Val = rxMessage.SourcePANID.Val; MiMAC_SetAltAddress((uint8_t *)&broadcastAddr, (uint8_t *)&myPANID.Val); #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_PANID_ID); #endif } if (rxMessage.Payload[1] == STATUS_EXISTS) { gEstConnectionInfo.status = ALREADY_EXISTS; } else if (rxMessage.Payload[1] == STATUS_SUCCESS) { gEstConnectionInfo.status = SUCCESS; } else { gEstConnectionInfo.status = FAILURE; } /* Retry is not needed since already response received */ gEstConnectionInfo.connectionRetries = 0; { /* Add Entry in the connection Table */ #if defined(PROTOCOL_STAR) uint8_t status = AddConnection(rxMessage.Payload[3]); #else uint8_t status = AddConnection(rxMessage.Payload[2]); #endif if ((status == STATUS_SUCCESS) || (status == STATUS_EXISTS)) { #if defined(PROTOCOL_STAR) /* Role is end node */ role = END_DEVICE; /* Initiate Link Status Timer */ startLinkStatusTimer(); #endif #ifdef ENABLE_SLEEP_FEATURE dataRequestInterval = RFD_WAKEUP_INTERVAL; #endif } #if defined(PROTOCOL_STAR) myConnectionIndex_in_PanCo = rxMessage.Payload[2]; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_MYINDEX_ID); #endif #endif } #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(MIWI_ALL_MEMORY_MEM_ID); #endif break; default: break; } } break; case CMD_P2P_ACTIVE_SCAN_REQUEST: { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; if(ConnMode > ENABLE_ACTIVE_SCAN_RSP) { return; } if( currentChannel != rxMessage.Payload[1] ) { return; } /* Prepare Active Scan Response */ dataPtr = MiMem_Alloc(PACKETLEN_P2P_ACTIVE_SCAN_RESPONSE); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_P2P_ACTIVE_SCAN_RESPONSE; dataPtr[dataLen++] = P2PCapacityInfo; #if ADDITIONAL_NODE_ID_SIZE > 0 for(i = 0; i < ADDITIONAL_NODE_ID_SIZE; i++) { dataPtr[dataLen++] = (miwiDefaultRomOrRamParams->AdditionalNodeID[i]); } #endif /* unicast the response to the requesting device */ #ifdef TARGET_SMALL frameTransmit(false, myPANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr, 0, true, CommandConfCallback); #else frameTransmit(false, rxMessage.SourcePANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr, 0, true, CommandConfCallback); #endif } break; case CMD_P2P_ACTIVE_SCAN_RESPONSE: { if(RESYNC_IN_PROGRESS == p2pStarCurrentState) { resyncInfo.resyncTimes = 0; #ifdef ENABLE_ACTIVE_SCAN activeScanDurationTimeInterval = 0; #endif p2pStarCurrentState = IN_NETWORK_STATE; resyncInfo.confCallback(currentChannel, SUCCESS); resyncInfo.confCallback = NULL; } #ifdef ENABLE_ACTIVE_SCAN else { i = 0; for(; i < gSearchConnectionInfo.activeScanResultIndex; i++) { if( (miwiDefaultRomOrRamParams->ActiveScanResults[i].Channel == currentChannel) && #if defined(IEEE_802_15_4) (miwiDefaultRomOrRamParams->ActiveScanResults[i].PANID.Val == rxMessage.SourcePANID.Val) && #endif isSameAddress(miwiDefaultRomOrRamParams->ActiveScanResults[i].Address, rxMessage.SourceAddress) ) { break; } } if( i == gSearchConnectionInfo.activeScanResultIndex && (i < ACTIVE_SCAN_RESULT_SIZE)) { miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].Channel = currentChannel; miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].RSSIValue = rxMessage.PacketRSSI; miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].LQIValue = rxMessage.PacketLQI; miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].PANID.Val = rxMessage.SourcePANID.Val; for(i = 0; i < MY_ADDRESS_LENGTH; i++) { miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].Address[i] = rxMessage.SourceAddress[i]; } miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].Capability.Val = rxMessage.Payload[1]; #if ADDITIONAL_NODE_ID_SIZE > 0 for(i = 0; i < ADDITIONAL_NODE_ID_SIZE; i++) { miwiDefaultRomOrRamParams->ActiveScanResults[gSearchConnectionInfo.activeScanResultIndex].PeerInfo[i] = rxMessage.Payload[2+i]; } #endif gSearchConnectionInfo.activeScanResultIndex++; } } #endif } break; #if defined (PROTOCOL_STAR) case CMD_SHARE_CONNECTION_TABLE: { if (END_DEVICE == role) { /* END_devices FFD|| RFD process this Packet */ end_nodes = rxMessage.Payload[1]; store_connection_tb(rxMessage.Payload); } } break; case CMD_DATA_TO_ENDDEV_SUCCESS: { P2PStarDataFrame_t *dataFramePtr = NULL; dataFramePtr = (P2PStarDataFrame_t *) miQueueRemove(&appAckWaitDataQueue, NULL); if (NULL == dataFramePtr) { return; } DataConf_callback_t callback = dataFramePtr->dataFrame.confCallback; if (NULL != callback) { callback(dataFramePtr->dataFrame.msghandle, SUCCESS, dataFramePtr->dataFrame.msg); } MiMem_Free((uint8_t *)dataFramePtr); } break; case CMD_FORWRD_PACKET: { /* If the role is PANC, the data has to be forwarded to corresponding enddevice */ if (PAN_COORD == role) { /* Based on the end device short address, the index in connection table is retrieved */ uint8_t ed_index = Find_Index(&(rxMessage.Payload[1])); if (0xFF != ed_index) { /* Allocate buffer for data forward and update */ P2PStarDataFrame_t* dataPtr = NULL; uint8_t dataLen = 0; dataPtr = (P2PStarDataFrame_t*)MiMem_Alloc(sizeof(P2PStarDataFrame_t)); if (NULL == dataPtr) { return; } memcpy(dataPtr->dataFrame.destAddress, miwiDefaultRomOrRamParams->ConnectionTable[ed_index].Address, LONG_ADDR_LEN); /* first 3 bytes in payload is updated with short address of source end device */ dataPtr->dataFrame.msg[dataLen++] = rxMessage.SourceAddress[0]; // Unique address of EDy (DEST ED) dataPtr->dataFrame.msg[dataLen++] = rxMessage.SourceAddress[1]; // Unique address of EDy (DEST ED) dataPtr->dataFrame.msg[dataLen++] = rxMessage.SourceAddress[2]; // Unique address of EDy (DEST ED) for(i = 4; i < rxMessage.PayloadSize; i++) { dataPtr->dataFrame.msg[dataLen++] = rxMessage.Payload[i]; } dataPtr->dataFrame.msgLength = dataLen; dataPtr->dataFrame.fromEDToED = 1; /* If the destination end device is sleeping device, place the data in indirect queue or transmit directly */ if(miwiDefaultRomOrRamParams->ConnectionTable[ed_index].status.bits.isValid && miwiDefaultRomOrRamParams->ConnectionTable[ed_index].status.bits.RXOnWhenIdle == 0) { if (50 < MiMem_PercentageOfFreeBuffers()) { dataPtr->dataFrame.confCallback = NULL; dataPtr->dataFrame.timeout = INDIRECT_MESSAGE_TIMEOUT; dataPtr->dataFrame.ackReq = true; miQueueAppend(&indirectFrameQueue, (miQueueBuffer_t*)dataPtr); } } else { frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[ed_index].Address, false, false, dataLen, dataPtr->dataFrame.msg, 1, true, appAckWaitDataCallback); miQueueAppend(&appAckWaitDataQueue, (miQueueBuffer_t*)dataPtr); } } } } break; #if defined(ENABLE_LINK_STATUS) case CMD_IAM_ALIVE: { if (PAN_COORD == role) { // PAN CP processes this packet to qualify it as alive , increments the link stat uint8_t p; for (p=0 ; p < CONNECTION_SIZE ; p++) { if (miwiDefaultRomOrRamParams->ConnectionTable[p].Address[0] == rxMessage.SourceAddress[0] && miwiDefaultRomOrRamParams->ConnectionTable[p].Address[1] == rxMessage.SourceAddress[1] && miwiDefaultRomOrRamParams->ConnectionTable[p].Address[2] == rxMessage.SourceAddress[2]) { miwiDefaultRomOrRamParams->ConnectionTable[p].link_status++; break; } } } } break; #endif #endif #if defined(ENABLE_FREQUENCY_AGILITY) case CMD_CHANNEL_HOPPING: { if((true == channelChangeInProgress) || (rxMessage.Payload[1] != currentChannel)) { return; } channelChangeInProgress = true; freqAgilityRetries = 0; optimalChannel = rxMessage.Payload[2]; StartChannelHopping(); } break; #endif #if defined(ENABLE_INDIRECT_MESSAGE) case CMD_MAC_DATA_REQUEST: { if (indirectFrameQueue.size) { uint8_t loopIndex; for (loopIndex =0; loopIndex < indirectFrameQueue.size; loopIndex++) { P2PStarDataFrame_t *dataFramePtr = (P2PStarDataFrame_t *)miQueueRemove(&indirectFrameQueue, NULL); if (NULL != dataFramePtr) { if (isSameAddress(rxMessage.SourceAddress, dataFramePtr->dataFrame.destAddress)) { frameTransmit(dataFramePtr->dataFrame.broadcast, myPANID, dataFramePtr->dataFrame.destAddress, false, false, dataFramePtr->dataFrame.msgLength, dataFramePtr->dataFrame.msg, dataFramePtr->dataFrame.msghandle, dataFramePtr->dataFrame.ackReq, macAckOnlyDataCallback); miQueueAppend(&macAckOnlyFrameQueue, (miQueueBuffer_t *)dataFramePtr); break; } else { miQueueAppend(&indirectFrameQueue, (miQueueBuffer_t *)dataFramePtr); } } } } } break; #endif #ifndef TARGET_SMALL case CMD_P2P_CONNECTION_REMOVAL_REQUEST: { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; dataPtr = MiMem_Alloc(PACKETLEN_P2P_CONNECTION_REMOVAL_RESPONSE); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_P2P_CONNECTION_REMOVAL_RESPONSE; for(i = 0; i < CONNECTION_SIZE; i++) { /* if the record is valid */ if( miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid ) { /* if the record is the same as the requesting device */ if( isSameAddress(rxMessage.SourceAddress, miwiDefaultRomOrRamParams->ConnectionTable[i].Address) ) { /* Find the record. disable the record and set status to be SUCCESS */ miwiDefaultRomOrRamParams->ConnectionTable[i].status.Val = 0; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CONNECTION_TABLE_ID); #endif dataPtr[dataLen++] = STATUS_SUCCESS; break; } } } if( i == CONNECTION_SIZE ) { /* not found, the requesting device is not my peer */ dataPtr[dataLen++] = STATUS_ENTRY_NOT_EXIST; } #ifdef TARGET_SMALL frameTransmit(false, myPANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr,0, true, CommandConfCallback); #else frameTransmit(false, rxMessage.SourcePANID, rxMessage.SourceAddress, true, rxMessage.flags.bits.secEn, dataLen, dataPtr,0, true, CommandConfCallback); #endif } break; case CMD_P2P_CONNECTION_REMOVAL_RESPONSE: { if( rxMessage.Payload[1] == STATUS_SUCCESS ) { for(i = 0; i < CONNECTION_SIZE; i++) { // if the record is valid if( miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid ) { // if the record address is the same as the requesting device if( isSameAddress(rxMessage.SourceAddress, miwiDefaultRomOrRamParams->ConnectionTable[i].Address) ) { // invalidate the record miwiDefaultRomOrRamParams->ConnectionTable[i].status.Val = 0; #if defined(ENABLE_NETWORK_FREEZER) PDS_Store(PDS_CONNECTION_TABLE_ID); #endif break; } } } } } break; #endif default: break; } } else { if (IN_NETWORK_STATE == p2pStarCurrentState) { pktRxcallback(&rxMessage); } } } void P2PTasks(void) { MIWI_TICK currentTick; currentTick.Val = MiWi_TickGet(); /* Transmission Queue Handling */ if (frameTxQueue.size && txCallbackReceived && (MiWi_TickGetDiff(currentTick, lastTxFrameTick) > (transaction_duration_us))) { TxFrame_t *txFramePtr = NULL; txFramePtr = (TxFrame_t *)miQueueRemove(&frameTxQueue, NULL); if (NULL != txFramePtr) { uint16_t transaction_duration_sym = 0; sentFrame = txFramePtr; /* Calculate Transaction Duration Time which is based on Frame Size either minLIFSPeriod or minSIFSPeriod*/ if ( (txFramePtr->txFrameEntry.frameLength + MAC_OVERHEAD + PHY_OVERHEAD) > aMaxSIFSFrameSize) { transaction_duration_sym = macMinLIFSPeriod_def; } else { transaction_duration_sym = macMinSIFSPeriod_def; } /* Turn around time and unit back off period added for MAC acknowledgment reception */ transaction_duration_sym += aTurnaroundTime + aUnitBackoffPeriod; transaction_duration_us = MiMAC_SymbolToTicks(transaction_duration_sym); /* Store current transmitted frame tick information */ lastTxFrameTick = currentTick; MiMAC_SendPacket(txFramePtr->txFrameEntry.frameParam, txFramePtr->txFrameEntry.frame, txFramePtr->txFrameEntry.frameLength, txFramePtr->txFrameEntry.frameHandle, frameTxCallback); txCallbackReceived = false; } } /* Check for New frame Reception, Parse and handle the frame if received */ if(MiMAC_ReceivedPacket()) { frameParse(&MACRxPacket); MiMAC_DiscardPacket(); } /* MiMAC Task Handler */ MiMAC_Task(); /* System Software Timer Handler */ SYS_TimerTaskHandler(); #ifdef ENABLE_SLEEP_FEATURE if(!(P2PStatus.bits.DataRequesting || P2PStatus.bits.RxHasUserData || (frameTxQueue.size) || (!txCallbackReceived)) && (p2pStarCurrentState == IN_NETWORK_STATE)) { MiMAC_PowerState(POWER_STATE_DEEP_SLEEP); } #endif } /********************************************************************* * BOOL frameTransmit(BOOL Broadcast, * uint16_t_VAL DestinationPANID, * uint8_t *DestinationAddress, * BOOL isCommand, * BOOL SecurityEnabled) * * Overview: This function sends the packet * * PreCondition: Transceiver is initialized * * Input: * BOOL Broadcast If packet to send needs to be broadcast * uint16_t_VAL DestinationPANID Destination PAN Identifier * uint8_t * DestinationAddress Pointer to destination long address * BOOL isCommand If packet to send is a command packet * BOOL SecurityEnabled If packet to send needs encryption * * Output: * BOOL If operation successful * * Side Effects: Transceiver is triggered to transmit a packet * ********************************************************************/ bool frameTransmit(INPUT bool Broadcast, API_UINT16_UNION DestinationPANID, INPUT uint8_t *DestinationAddress, INPUT bool isCommand, INPUT bool SecurityEnabled, INPUT uint8_t msgLen, INPUT uint8_t* msgPtr, INPUT uint8_t msghandle, INPUT bool ackReq, INPUT DataConf_callback_t ConfCallback) { MAC_TRANS_PARAM *tParam; TxFrame_t *txFramePtr = NULL; txFramePtr = (TxFrame_t *) MiMem_Alloc(sizeof(TxFrame_t)); if (NULL == txFramePtr) { return false; } tParam = &(txFramePtr->txFrameEntry.frameParam); tParam->flags.Val = 0; tParam->flags.bits.packetType = (isCommand) ? PACKET_TYPE_COMMAND : PACKET_TYPE_DATA; tParam->flags.bits.ackReq = (Broadcast) ? 0 : ackReq; tParam->flags.bits.broadcast = Broadcast; tParam->flags.bits.secEn = SecurityEnabled; #if defined(IEEE_802_15_4) tParam->altSrcAddr = 0; tParam->altDestAddr = (Broadcast) ? true : false; #endif #if defined(INFER_DEST_ADDRESS) tParam->flags.bits.destPrsnt = 0; #else tParam->flags.bits.destPrsnt = (Broadcast) ? 0:1; #endif #if defined(SOURCE_ADDRESS_ABSENT) if( tParam->flags.bits.packetType == PACKET_TYPE_COMMAND ) { tParam->flags.bits.sourcePrsnt = 1; } else { tParam->flags.bits.sourcePrsnt = 0; } #else tParam->flags.bits.sourcePrsnt = 1; #endif tParam->DestAddress = DestinationAddress; #if defined(IEEE_802_15_4) tParam->DestPANID.Val = DestinationPANID.Val; #endif if (NULL != DestinationAddress) { if (false == tParam->flags.bits.broadcast) { memcpy(&(txFramePtr->txFrameEntry.frameDstAddr), DestinationAddress, LONG_ADDR_LEN); } else { memcpy(&(txFramePtr->txFrameEntry.frameDstAddr), DestinationAddress, SHORT_ADDR_LEN); } } txFramePtr->txFrameEntry.frame = msgPtr; tParam->DestAddress = (uint8_t*)&(txFramePtr->txFrameEntry.frameDstAddr); txFramePtr->txFrameEntry.frameConfCallback = ConfCallback; txFramePtr->txFrameEntry.frameHandle = msghandle; txFramePtr->txFrameEntry.frameLength = msgLen; miQueueAppend(&frameTxQueue, (miQueueBuffer_t *)txFramePtr); return true; } static void protocolTimerInit(void) { protocolTimer.interval = PROTOCOL_TIMER_INTERVAL; protocolTimer.mode = SYS_TIMER_PERIODIC_MODE; protocolTimer.handler = protocolTimerHandler; SYS_TimerStart(&protocolTimer); #if defined(PROTOCOL_STAR) dataTimer.interval = DATA_TIMER_INTERVAL; dataTimer.mode = SYS_TIMER_PERIODIC_MODE; dataTimer.handler = dataTimerHandler; #endif } static void protocolTimerHandler(SYS_Timer_t *timer) { #ifdef ENABLE_SLEEP_FEATURE if((0 != dataRequestInterval) && ((--dataRequestInterval) == 0)) { sendDataRequest(); } #endif #ifdef ENABLE_ACTIVE_SCAN if((0 != activeScanDurationTimeInterval) && ((--activeScanDurationTimeInterval) == 0)) { scanDurationExpired(); } #endif #ifdef ENABLE_ED_SCAN if((0 != edScanDurationTimeInterval) && ((--edScanDurationTimeInterval) == 0)) { edScanDurationExpired(); } #endif if((0 != connectionTimeInterval) && ((--connectionTimeInterval) == 0)) { sendConnectionRequest(); } #ifdef ENABLE_FREQUENCY_AGILITY if((0 != freqAgilityBroadcastInterval) && ((--freqAgilityBroadcastInterval) == 0)) { StartChannelHopping(); } #endif #ifdef ENABLE_PERIODIC_CONNECTIONTABLE_SHARE if((0 != sharePeerDevInfoTimeInterval) && ((--sharePeerDevInfoTimeInterval) == 0)) { /* Reload time interval since it is periodic timer */ sharePeerDevInfoTimeInterval = SHARE_PEER_DEVICE_INFO_TIMEOUT; MiApp_BroadcastConnectionTable(); } #endif #ifdef ENABLE_LINK_STATUS if((0 != inActiveDeviceCheckTimeInterval) && ((--inActiveDeviceCheckTimeInterval) == 0)) { /* Reload time interval since it is periodic timer */ inActiveDeviceCheckTimeInterval = FIND_INACTIVE_DEVICE_TIMEOUT; findInActiveDevices(); } if((0 != linkStatusTimeInterval) && ((--linkStatusTimeInterval) == 0)) { /* Reload time interval since it is periodic timer */ linkStatusTimeInterval = LINK_STATUS_TIMEOUT; sendLinkStatus(); } #endif #ifdef ENABLE_INDIRECT_MESSAGE if (indirectFrameQueue.size) { uint8_t loopIndex; for (loopIndex =0; loopIndex < indirectFrameQueue.size; loopIndex++) { P2PStarDataFrame_t *dataFramePtr = (P2PStarDataFrame_t *)miQueueRemove(&indirectFrameQueue, NULL); if (NULL != dataFramePtr) { if ((0 != dataFramePtr->dataFrame.timeout) && ((--dataFramePtr->dataFrame.timeout) == 0)) { DataConf_callback_t callback = dataFramePtr->dataFrame.confCallback; if (NULL != callback && 1 != dataFramePtr->dataFrame.broadcast) { callback(dataFramePtr->dataFrame.msghandle, TRANSACTION_EXPIRED, dataFramePtr->dataFrame.msg); } MiMem_Free((uint8_t *)dataFramePtr); } else { miQueueAppend(&indirectFrameQueue, (miQueueBuffer_t *)dataFramePtr); } } } } #endif } #if defined(PROTOCOL_STAR) static void dataTimerHandler(SYS_Timer_t *timer) { uint8_t loopIndex; P2PStarDataFrame_t *dataFramePtr = NULL; for (loopIndex = 0; loopIndex < appAckWaitDataQueue.size; loopIndex++) { dataFramePtr = (P2PStarDataFrame_t *) miQueueRemove(&appAckWaitDataQueue, NULL); if (NULL == dataFramePtr) { return; } if((0 != dataFramePtr->dataFrame.timeout) && (--dataFramePtr->dataFrame.timeout) == 0) { DataConf_callback_t callback = dataFramePtr->dataFrame.confCallback; if (NULL != callback) { callback(dataFramePtr->dataFrame.msghandle, NO_ACK, (uint8_t*)&(dataFramePtr->dataFrame.msg)); } MiMem_Free((uint8_t *)dataFramePtr); } else { miQueueAppend(&appAckWaitDataQueue, (miQueueBuffer_t *)dataFramePtr); } } if (0 == appAckWaitDataQueue.size) { SYS_TimerStop(&dataTimer); } } /************************************************************************************ * Function: * void MiApp_BroadcastConnectionTable(void) * * Summary: * This function is used for command type packet TX (Only used by PAN CO) * * Description: * This function is used by only PAN CO in a Star network and is a cmd * type packet. PAN CO in Star Network , holds the responsibility to Share * peer end devices connection table. * * PreCondition: * Protocol initialization has been done. * * Returns: * None. * * Remarks: * None * *****************************************************************************************/ static void MiApp_BroadcastConnectionTable(void) { uint8_t i,j , k , count; // Based on Connection Size in Network broadcast the connection details Multiple Times // so that all the END_DEVICES in Star Network Receive the packet uint8_t broadcast_count = 0; uint8_t* dataPtr = NULL; uint8_t dataLen = 0; if ((conn_size * 4 ) + 4 < TX_BUFFER_SIZE) { broadcast_count = 1; } else { broadcast_count = ((conn_size * 4) + 4 )/ TX_BUFFER_SIZE; if ((conn_size *4) + 4 % TX_BUFFER_SIZE != 0) { broadcast_count = broadcast_count + ((conn_size *4) + 4 )% TX_BUFFER_SIZE; } } for (i = 0 ; i < broadcast_count ; i++) { dataPtr = MiMem_Alloc(TX_BUFFER_SIZE); if (NULL == dataPtr) return; dataPtr[dataLen++] = CMD_SHARE_CONNECTION_TABLE; dataPtr[dataLen++] = conn_size; // No of end devices in network dataPtr[dataLen++] = (((TX_BUFFER_SIZE-4)/4)*i); dataPtr[dataLen++] = (((TX_BUFFER_SIZE-4)/4)*(i+1)); count = 4; for (j= ((TX_BUFFER_SIZE-4)/4)*i ;j<((TX_BUFFER_SIZE-4)/4)*(i+1);j++) { if (j < conn_size) { if (miwiDefaultRomOrRamParams->ConnectionTable[j].status.bits.isValid) { dataPtr[dataLen++] = (miwiDefaultRomOrRamParams->ConnectionTable[j].Address[0]); dataPtr[dataLen++] = (miwiDefaultRomOrRamParams->ConnectionTable[j].Address[1]); dataPtr[dataLen++] = (miwiDefaultRomOrRamParams->ConnectionTable[j].Address[2]); dataPtr[dataLen++] = j; } else { dataPtr[dataLen++] = 0xff; dataPtr[dataLen++] = 0xff; dataPtr[dataLen++] = 0xff; dataPtr[dataLen++] = j; } count = count + 4; } } // Fill the remaining buffer with garbage value for (k=count;k PAN CO --> EDy PAN Coordinator will forward the data to EDy , In order to know */ static uint8_t Find_Index (uint8_t *DestAddr) { uint8_t i; uint8_t return_val; for (i = 0;i < conn_size; i++) { if (miwiDefaultRomOrRamParams->ConnectionTable[i].status.bits.isValid) { if (DestAddr[0] == miwiDefaultRomOrRamParams->ConnectionTable[i].Address[0] && DestAddr[1] == miwiDefaultRomOrRamParams->ConnectionTable[i].Address[1] && DestAddr[2] == miwiDefaultRomOrRamParams->ConnectionTable[i].Address[2] ) { return_val = i; break; } } } if (i == conn_size) { return_val = 0xff; } return return_val; } bool MiApp_SubscribeLinkFailureCallback(LinkFailureCallback_t callback) { if (NULL != callback) { linkFailureCallback = callback; return true; } return false; } static void handleLostConnection(void) { uint8_t i ; bool stat = false; if (END_DEVICE == role) { for (i = 0; i < end_nodes; i++) { if (myLongAddress[0] == END_DEVICES_Short_Address[i].Address[0] && myLongAddress[1] == END_DEVICES_Short_Address[i].Address[1]) { stat = true; } } if (!stat) { /* Stop Timers */ linkStatusTimeInterval = 0; #ifdef ENABLE_SLEEP_FEATURE dataRequestInterval = 0; #endif if ((NULL != linkFailureCallback) && (p2pStarCurrentState != DISCONNECTED)) { linkFailureCallback(); } p2pStarCurrentState = DISCONNECTED; } } } /* Function to store the Connection Table Information which is Broadcasted by PAN Coordinator Used by END_DEVICES (FFD || RFD) only */ static void store_connection_tb(uint8_t *payload) { uint8_t i , j ; for (i = 4; i < RX_BUFFER_SIZE; i+=4) { j = payload[i+3]; if (0xFF != j) { END_DEVICES_Short_Address[j].connection_slot = j; END_DEVICES_Short_Address[j].Address[0] = payload[i]; END_DEVICES_Short_Address[j].Address[1] = payload[i+1]; END_DEVICES_Short_Address[j].Address[2] = payload[i+2]; } } handleLostConnection(); } #endif #if defined(ENABLE_NETWORK_FREEZER) bool MiApp_SubscribeReConnectionCallback(ReconnectionCallback_t callback) { if (NULL != callback) { reconnectionCallback = callback; return true; } return false; } #endif void macAckOnlyDataCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer) { P2PStarDataFrame_t *dataFramePtr = NULL; dataFramePtr = (P2PStarDataFrame_t *) miQueueRemove(&macAckOnlyFrameQueue, NULL); if (NULL != dataFramePtr) { DataConf_callback_t callback = dataFramePtr->dataFrame.confCallback; if (NULL != callback && 1 != dataFramePtr->dataFrame.broadcast) { callback(handle, status, msgPointer); } #if defined(PROTOCOL_STAR) if (dataFramePtr->dataFrame.fromEDToED) { uint8_t ed_index = Find_Index(dataFramePtr->dataFrame.msg); if (0xFF != ed_index) { uint8_t* dataPtr; dataPtr = MiMem_Alloc(PACKETLEN_CMD_DATA_TO_ENDDEV_SUCCESS); if (NULL == dataPtr) return; dataPtr[0] = CMD_DATA_TO_ENDDEV_SUCCESS; frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[ed_index].Address, true, true, 1, dataPtr, 0, true, CommandConfCallback); } } #endif MiMem_Free((uint8_t *)dataFramePtr); } } #if defined(PROTOCOL_STAR) void appAckWaitDataCallback(uint8_t handle, miwi_status_t status, uint8_t* msgPointer) { if (PAN_COORD == role) { uint8_t loopIndex; P2PStarDataFrame_t *dataFramePtr = NULL; for (loopIndex = 0; loopIndex < appAckWaitDataQueue.size; loopIndex++) { dataFramePtr = (P2PStarDataFrame_t *) miQueueRemove(&appAckWaitDataQueue, NULL); if (NULL == dataFramePtr) { return; } if(msgPointer == (uint8_t*)&(dataFramePtr->dataFrame.msg)) { if (SUCCESS == status) { uint8_t* dataPtr = NULL; uint8_t ed_index = Find_Index(dataFramePtr->dataFrame.msg); if (0xFF != ed_index) { dataPtr = MiMem_Alloc(PACKETLEN_CMD_DATA_TO_ENDDEV_SUCCESS); if (NULL == dataPtr) return; dataPtr[0] = CMD_DATA_TO_ENDDEV_SUCCESS; frameTransmit(false, myPANID, miwiDefaultRomOrRamParams->ConnectionTable[ed_index].Address, true, true, 1, dataPtr, 0, true, CommandConfCallback); } } MiMem_Free(dataFramePtr); } else { miQueueAppend(&appAckWaitDataQueue, (miQueueBuffer_t *)dataFramePtr); } } } else if (SUCCESS != status) { uint8_t loopIndex; P2PStarDataFrame_t *dataFramePtr = NULL; for (loopIndex = 0; loopIndex < appAckWaitDataQueue.size; loopIndex++) { dataFramePtr = (P2PStarDataFrame_t *) miQueueRemove(&appAckWaitDataQueue, NULL); if (NULL == dataFramePtr) { return; } if(handle == dataFramePtr->dataFrame.msghandle && msgPointer == (uint8_t*)&(dataFramePtr->dataFrame.msg)) { DataConf_callback_t callback = dataFramePtr->dataFrame.confCallback; if (NULL != callback) { callback(handle, status, msgPointer); } MiMem_Free((uint8_t *)dataFramePtr); } else { miQueueAppend(&appAckWaitDataQueue, (miQueueBuffer_t *)dataFramePtr); } } } } #endif #ifdef ENABLE_SLEEP_FEATURE static void rfdDataWaitTimerExpired(struct SYS_Timer_t *timer) { P2PStatus.bits.DataRequesting = 0; dataRequestInterval = RFD_WAKEUP_INTERVAL; } #endif #ifdef ENABLE_FREQUENCY_AGILITY static void channelHopCmdCallback(uint8_t msgConfHandle, miwi_status_t status, uint8_t* msgPointer) { MiMem_Free(msgPointer); ++freqAgilityRetries; /* If all the broadcasts are done, then set the optimal channel */ if (freqAgilityRetries >= FA_BROADCAST_TIME) { MiApp_Set(CHANNEL, &optimalChannel); channelChangeInProgress = false; return; } /* Start the timer for broadcast retries */ freqAgilityBroadcastInterval = (miwi_scan_duration_ticks(9)/1000000)+1; } /********************************************************************* * static void StartChannelHopping(uint8_t OptimalChannel) * * Overview: This function broadcast the channel hopping command * and after that, change operating channel to the * input optimal channel * * PreCondition: Transceiver has been initialized * * Input: None * * Output: * None * * Side Effects: The operating channel for current device will change * to the specified channel * ********************************************************************/ static void StartChannelHopping(void) { uint8_t* dataPtr = NULL; uint8_t dataLen = 0; /* Prepare the Channel Hopping Message */ dataPtr = MiMem_Alloc(PACKETLEN_CMD_CHANNEL_HOPPING); if (NULL == dataPtr) return; /* Prepare channel hop command */ dataPtr[dataLen++] = CMD_CHANNEL_HOPPING; dataPtr[dataLen++] = currentChannel; dataPtr[dataLen++] = optimalChannel; /* Initiate the transmission */ frameTransmit(true, myPANID, NULL, true, false, dataLen, dataPtr,0, true, channelHopCmdCallback); } /******************************************************************************************* * Function: * BOOL MiApp_InitChannelHopping(uint32_t ChannelMap) * * Summary: * * This function tries to start a channel hopping (frequency agility) procedure * * Description: * This is the primary user interface function for the application to do energy * scan to locate the channel with least noise. If the channel is not current * operating channel, process of channel hopping will be started. * * PreCondition: * Transceiver has been initialized * * Parameters: * uint32_t ChannelMap - The bit map of the candicate channels * which can be hopped to * * Returns: * a boolean to indicate if channel hopping is initiated * * Example: * * // if condition meets, scan all possible channels and hop * // to the one with least noise * MiApp_InitChannelHopping(0xFFFFFFFF); * * * Remark: The operating channel will change to the optimal * channel with least noise * ******************************************************************************************/ bool MiApp_InitChannelHopping( INPUT uint32_t ChannelMap) { uint8_t RSSIValue; /* Backup the channel and connection mode */ uint8_t backupChannell = currentChannel; uint8_t backupConnMode = ConnMode; /* Disable allowing connections before NoiseDetectioScan*/ MiApp_ConnectionMode(DISABLE_ALL_CONN); /* Find the optimal channel to switch using noise detection scan */ optimalChannel = MiApp_NoiseDetection(ChannelMap, 3, NOISE_DETECT_ENERGY, &RSSIValue); /* Restore back the connection mode*/ MiApp_ConnectionMode(backupConnMode); /* Set the original channel so that channel hop command is transmitted */ MiApp_Set(CHANNEL, &backupChannell); /* If same channel, skip transmitting channel hop */ if( optimalChannel == backupChannell ) { return false; } /* Start the channel Hopping process */ freqAgilityRetries = 0; channelChangeInProgress = true; StartChannelHopping(); return true; } #endif #ifdef ENABLE_SLEEP_FEATURE /************************************************************************************ * Function: * bool MiApp_ReadyToSleep(uint32_t* sleepTime) * * Summary: * This function informs whether the device can go sleep or not and how much time * it can sleep * * Description: * This is used to understand the stack is ready to sleep and how much time stack * allows to sleep if it is ready. * * Parameters: * uint32_t* sleepTime - Pointer to sleep time which specifies the sleepable time * when stack is ready to sleep * * Returns: * A boolean to indicates that stack is ready to sleep or not * *****************************************************************************************/ bool MiApp_ReadyToSleep(uint32_t* sleepTime) { if((p2pStarCurrentState == IN_NETWORK_STATE) && !(P2PStatus.bits.DataRequesting || P2PStatus.bits.RxHasUserData || (frameTxQueue.size) || (!txCallbackReceived))) { *sleepTime = dataRequestInterval * 1000; return true; } return false; } #endif #if defined(ENABLE_NETWORK_FREEZER) /************************************************************************************ * Function: * bool MiApp_ResetToFactoryNew(void) * * Summary: * This function makes the device to factory new device * * Description: * This is used to erase all the persistent items in the non-volatile memory and resets the system. * * Returns: * A boolean to indicate the operation is success or not * *****************************************************************************************/ bool MiApp_ResetToFactoryNew(void) { if (PDS_DeleteAll(true)) { NVIC_SystemReset(); return true; } else { return false; } } #endif