ZStack-CC2530-2.5.1a_0FH_关于协议栈下afIncomingMSGPacket_t::timestamp的猜测
扫描二维码
随时随地手机看文章
首先,在协议栈中应用层的事件处理函数都是通过 一个指向接收消息结构体的指针*MSGpkt(afIncomingMSGPacket_t )来传递消息的。在该结构体中有一个参数timestamp,其描述为:
uint32 timestamp; /* receipt timestamp from MAC */
为了探索在无线数据包中这个参数到底是如何来的?我选择在整个工程文件中搜索该参数。然后在搜索结果中看到一个在AF.c文件中的函数:
/********************************************************************* * @fn afBuildMSGIncoming * * @brief Build the message for the app * * @param * * @return pointer to next in data buffer */ static void afBuildMSGIncoming( aps_FrameFormat_t *aff, endPointDesc_t *epDesc, zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp ) { afIncomingMSGPacket_t *MSGpkt; const uint8 len = sizeof( afIncomingMSGPacket_t ) + aff->asduLength; uint8 *asdu = aff->asdu; MSGpkt = (afIncomingMSGPacket_t *)osal_msg_allocate( len ); if ( MSGpkt == NULL ) { return; } MSGpkt->hdr.event = AF_INCOMING_MSG_CMD; MSGpkt->groupId = aff->GroupID; MSGpkt->clusterId = aff->ClusterID; afCopyAddress( &MSGpkt->srcAddr, SrcAddress ); MSGpkt->srcAddr.endPoint = aff->SrcEndPoint; MSGpkt->endPoint = epDesc->endPoint; MSGpkt->wasBroadcast = aff->wasBroadcast; MSGpkt->LinkQuality = sig->LinkQuality; MSGpkt->correlation = sig->correlation; MSGpkt->rssi = sig->rssi; MSGpkt->SecurityUse = SecurityUse; MSGpkt->timestamp = timestamp; MSGpkt->nwkSeqNum = nwkSeqNum; MSGpkt->macDestAddr = aff->macDestAddr; MSGpkt->srcAddr.panId = SrcPanId; MSGpkt->cmd.TransSeqNumber = 0; MSGpkt->cmd.DataLength = aff->asduLength; if ( MSGpkt->cmd.DataLength ) { MSGpkt->cmd.Data = (uint8 *)(MSGpkt + 1); osal_memcpy( MSGpkt->cmd.Data, asdu, MSGpkt->cmd.DataLength ); } else { MSGpkt->cmd.Data = NULL; } #if defined ( MT_AF_CB_FUNC ) // If ZDO or SAPI have registered for this endpoint, dont intercept it here if (AFCB_CHECK(CB_ID_AF_DATA_IND, *(epDesc->task_id))) { MT_AfIncomingMsg( (void *)MSGpkt ); // Release the memory. osal_msg_deallocate( (void *)MSGpkt ); } else #endif { // Send message through task message. osal_msg_send( *(epDesc->task_id), (uint8 *)MSGpkt ); } }
其中可以看到这个函数是一个为应用层事件处理函数生成消息的函数(第35行)。那么我再次在工程文件中搜索该函数,希望能够找到该函数的调用位置。然后发现其在同位于AF.c文件下的afIncomingData函数中被调用:
/********************************************************************* * @fn afIncomingData * * @brief Transfer a data PDU (ASDU) from the APS sub-layer to the AF. * * @param aff - pointer to APS frame format * @param SrcAddress - Source address * @param SrcPanId - Source PAN ID * @param sig - incoming message's link quality * @param nwkSeqNum - incoming network sequence number (from nwk header frame) * @param SecurityUse - Security enable/disable * @param timestamp - the MAC Timer2 timestamp at Rx. * * @return none */ void afIncomingData( aps_FrameFormat_t *aff, zAddrType_t *SrcAddress, uint16 SrcPanId, NLDE_Signal_t *sig, uint8 nwkSeqNum, uint8 SecurityUse, uint32 timestamp ) { endPointDesc_t *epDesc = NULL; epList_t *pList = epList; #if !defined ( APS_NO_GROUPS ) uint8 grpEp = APS_GROUPS_EP_NOT_FOUND; #endif if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) ) { #if !defined ( APS_NO_GROUPS ) // Find the first endpoint for this group grpEp = aps_FindGroupForEndpoint( aff->GroupID, APS_GROUPS_FIND_FIRST ); if ( grpEp == APS_GROUPS_EP_NOT_FOUND ) return; // No endpoint found epDesc = afFindEndPointDesc( grpEp ); if ( epDesc == NULL ) return; // Endpoint descriptor not found pList = afFindEndPointDescList( epDesc->endPoint ); #else return; // Not supported #endif } else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT ) { // Set the list if ( pList != NULL ) { epDesc = pList->epDesc; } } else if ( (epDesc = afFindEndPointDesc( aff->DstEndPoint )) ) { pList = afFindEndPointDescList( epDesc->endPoint ); } while ( epDesc ) { uint16 epProfileID = 0xFFFF; // Invalid Profile ID if ( pList->pfnDescCB ) { uint16 *pID = (uint16 *)(pList->pfnDescCB( AF_DESCRIPTOR_PROFILE_ID, epDesc->endPoint )); if ( pID ) { epProfileID = *pID; osal_mem_free( pID ); } } else if ( epDesc->simpleDesc ) { epProfileID = epDesc->simpleDesc->AppProfId; } if ( (aff->ProfileID == epProfileID) || ((epDesc->endPoint == ZDO_EP) && (aff->ProfileID == ZDO_PROFILE_ID)) ) { { // Save original endpoint uint8 endpoint = aff->DstEndPoint; // overwrite with descriptor's endpoint aff->DstEndPoint = epDesc->endPoint; afBuildMSGIncoming( aff, epDesc, SrcAddress, SrcPanId, sig, nwkSeqNum, SecurityUse, timestamp ); // Restore with original endpoint aff->DstEndPoint = endpoint; } } if ( ((aff->FrmCtrl & APS_DELIVERYMODE_MASK) == APS_FC_DM_GROUP) ) { #if !defined ( APS_NO_GROUPS ) // Find the next endpoint for this group grpEp = aps_FindGroupForEndpoint( aff->GroupID, grpEp ); if ( grpEp == APS_GROUPS_EP_NOT_FOUND ) return; // No endpoint found epDesc = afFindEndPointDesc( grpEp ); if ( epDesc == NULL ) return; // Endpoint descriptor not found pList = afFindEndPointDescList( epDesc->endPoint ); #else return; #endif } else if ( aff->DstEndPoint == AF_BROADCAST_ENDPOINT ) { pList = pList->nextDesc; if ( pList ) epDesc = pList->epDesc; else epDesc = NULL; } else epDesc = NULL; } }
在参数描述那里显示timestamp是Rx时的MAC时间(第12行、第84行)。那么,我开始探索afIncomingData函数在什么位置被调用,采用同样的搜索方法,然后发现其调用的位置被封装了,搜索不到。但是根据这个参数的表述,我们知道这个参数是Rx时的MAC时间。所以我在工程文件中找到了一个名叫mac_rx.c的文件,我有一种这个参数将在这得到其来由的预感。首先,我们看一下这个mac_rx.c文件中包含的.h头文件mac_rx.h:
MAC_INTERNAL_API void macRxInit(void); MAC_INTERNAL_API void macRxRadioPowerUpInit(void); MAC_INTERNAL_API void macRxTxReset(void); MAC_INTERNAL_API void macRxHaltCleanup(void); MAC_INTERNAL_API void macRxThresholdIsr(void); MAC_INTERNAL_API void macRxFifoOverflowIsr(void); MAC_INTERNAL_API void macRxAckTxDoneCallback(void);
对于这个头文件还是有点不明白,然后再去看mac_rx.c文件,从其中一个函数的函数描述,我判断这个函数可能就是和我们当前目的相近的函数:
/*================================================================================================= * @fn rxStartIsr * * @brief First ISR state for receiving a packet - compute packet length, allocate * buffer, initialize buffer. Acknowledgements are handled immediately without * allocating a buffer. * * @param none * * @return none *================================================================================================= */ static void rxStartIsr(void) { uint8 addrLen; uint8 ackWithPending; uint8 dstAddrMode; uint8 srcAddrMode; uint8 mhrLen = 0; MAC_ASSERT(!macRxActive); /* receive on top of receive */ /* indicate rx is active */ macRxActive = MAC_RX_ACTIVE_STARTED; /* * For bullet proof functionality, need to see if the receiver was just turned off. * The logic to request turning off the receiver, disables interrupts and then checks * the value of macRxActive. If it is TRUE, the receiver will not be turned off. * * There is a small hole though. It's possible to attempt turning off the receiver * in the window from when the receive interrupt fires and the point where macRxActive * is set to TRUE. To plug this hole, the on/off status must be tested *after* * macRxActive has been set. If the receiver is off at this point, there is nothing * in the RX fifo and the receive is simply aborted. * * Also, there are some considerations in case a hard disable just happened. Usually, * the receiver will just be off at this point after a hard disable. The check described * above will account for this case too. However, if a hard disable were immediately * followed by an enable, the receiver would be on. To catch this case, the receive * FIFO is also tested to see if it is empty. Recovery is identical to the other cases. */ if (!macRxOnFlag || MAC_RADIO_RX_FIFO_IS_EMPTY()) { /* reset active flag */ macRxActive = MAC_RX_ACTIVE_NO_ACTIVITY; /* * To be absolutely bulletproof, must make sure no transmit queue'ed up during * the tiny, tiny window when macRxActive was not zero. */ rxPostRxUpdates(); /* return immediately from here */ return; } /* * If interrupts are held off for too long it's possible the previous "transmit done" * callback is pending. If this is the case, it needs to be completed before * continuing with the receive logic. */ MAC_RADIO_FORCE_TX_DONE_IF_PENDING(); /* * It's possible receive logic is still waiting for confirmation of an ACK that went out * for the previous receive. This is OK but the callback needs to be canceled at this point. * That callback execute receive cleanup logic that will run at the completion * of *this* receive. Also, it is important the flag for the outgoing ACK to be cleared. */ MAC_RADIO_CANCEL_ACK_TX_DONE_CALLBACK(); macRxOutgoingAckFlag = 0; /* * Make a module-local copy of macRxFilter. This prevents the selected * filter from changing in the middle of a receive. */ rxFilter = macRxFilter; /*------------------------------------------------------------------------------- * Read initial frame information from FIFO. * * This code is not triggered until the following are in the RX FIFO: * frame length - one byte containing length of MAC frame (excludes this field) * frame control field - two bytes defining frame type, addressing fields, control flags * sequence number - one byte unique sequence identifier * additional two bytes - these bytes are available in case the received frame is an ACK, * if so, the frame can be verified and responded to immediately, * if not an ACK, these bytes will be processed normally */ /* read frame length, frame control field, and sequence number from FIFO */ MAC_RADIO_READ_RX_FIFO(rxBuf, MAC_PHY_PHR_LEN + MAC_FCF_FIELD_LEN + MAC_SEQ_NUM_FIELD_LEN); /* bytes to read from FIFO equals frame length minus length of MHR fields just read from FIFO */ rxUnreadLen = (rxBuf[0] & PHY_PACKET_SIZE_MASK) - MAC_FCF_FIELD_LEN - MAC_SEQ_NUM_FIELD_LEN; /* * Workaround for chip bug #1547. The receive buffer can sometimes be corrupted by hardware. * This usually occurs under heavy traffic. If a corrupted receive buffer is detected * the entire receive buffer is flushed. * * In the case that this workaround is not needed, an assert is used to make sure the * receive length field is not corrupted. This is important because a corrupted receive * length field is utterly fatal and, if not caught here, extremely hard to track down. */ if (macChipVersion == REV_A) { if ((rxUnreadLen > (MAC_A_MAX_PHY_PACKET_SIZE - MAC_FCF_FIELD_LEN - MAC_SEQ_NUM_FIELD_LEN)) || (MAC_FRAME_TYPE(&rxBuf[1]) > MAC_FRAME_TYPE_MAX_VALID)) { MAC_RADIO_FLUSH_RX_FIFO(); rxDone(); return; } } else { /* radio supplied a corrupted receive buffer length */ MAC_ASSERT(rxUnreadLen rxUnreadLen) { /* discard frame and exit */ rxDiscardFrame(); return; } /* aux security header plus payload length is equal to unread bytes minus * address length, minus the FCS */ rxPayloadLen = rxUnreadLen - addrLen - MAC_FCS_FIELD_LEN; /*------------------------------------------------------------------------------- * Allocate memory for the incoming frame. */ if (MAC_SEC_ENABLED(&rxBuf[1])) { /* increase the allocation size of MAC header for security */ mhrLen = MAC_MHR_LEN; } pRxBuf = (macRx_t *) MEM_ALLOC(sizeof(macRx_t) + mhrLen + rxPayloadLen); if (pRxBuf == NULL) { /* Cancel the outgoing TX ACK */ MAC_RADIO_CANCEL_TX_ACK(); /* buffer allocation failed, discard the frame and exit*/ rxDiscardFrame(); return; } /*------------------------------------------------------------------------------- * Set up to process ACK request. Do not ACK if in promiscuous mode. */ ackWithPending = 0; if (!rxPromiscuousMode) { macRxOutgoingAckFlag = MAC_ACK_REQUEST(&rxBuf[1]); } /*------------------------------------------------------------------------------- * Process any ACK request. */ if (macRxOutgoingAckFlag) { halIntState_t s; /* * This critical section ensures that the callback ISR is initiated within time * to guarantee correlation with the strobe. */ HAL_ENTER_CRITICAL_SECTION(s); /* Do not ack data packet with pending more data */ if( MAC_FRAME_TYPE(&rxBuf[1]) == MAC_FRAME_TYPE_COMMAND ) { if( macRxCheckMACPendingCallback()) { /* Check is any mac data pending for end devices */ ackWithPending = MAC_RX_FLAG_ACK_PENDING; } else { if( macSrcMatchIsEnabled ) { /* When autopend is enabled, check if allpending is set to true */ if( MAC_SrcMatchCheckAllPending() == MAC_AUTOACK_PENDING_ALL_ON ) { ackWithPending = MAC_RX_FLAG_ACK_PENDING; } } else { /* When autopend is disabled, check the application pending callback */ if( macRxCheckPendingCallback() ) { ackWithPending = MAC_RX_FLAG_ACK_PENDING; } } } } if( ackWithPending == MAC_RX_FLAG_ACK_PENDING ) { MAC_RADIO_TX_ACK_PEND(); } else { MAC_RADIO_TX_ACK(); } /* request a callback to macRxAckTxDoneCallback() when the ACK transmit has finished */ MAC_RADIO_REQUEST_ACK_TX_DONE_CALLBACK(); HAL_EXIT_CRITICAL_SECTION(s); } /*------------------------------------------------------------------------------- * Populate the receive buffer going up to high-level. */ /* configure the payload buffer * save MAC header pointer regardless of security status. */ pRxBuf->mhr.p = pRxBuf->msdu.p = (uint8 *) (pRxBuf + 1); pRxBuf->mhr.len = pRxBuf->msdu.len = rxPayloadLen; if (MAC_SEC_ENABLED(&rxBuf[1])) { /* Copy FCF and sequence number to RX buffer */ pRxBuf->mhr.len = MAC_FCF_FIELD_LEN + MAC_SEQ_NUM_FIELD_LEN; osal_memcpy(pRxBuf->mhr.p, &rxBuf[1], pRxBuf->mhr.len); pRxBuf->mhr.p += pRxBuf->mhr.len; } /* set internal values */ pRxBuf->mac.srcAddr.addrMode = srcAddrMode; pRxBuf->mac.dstAddr.addrMode = dstAddrMode; pRxBuf->mac.timestamp = MAC_RADIO_BACKOFF_CAPTURE(); pRxBuf->mac.timestamp2 = MAC_RADIO_TIMER_CAPTURE(); pRxBuf->internal.frameType = MAC_FRAME_TYPE(&rxBuf[1]); pRxBuf->mac.dsn = MAC_SEQ_NUMBER(&rxBuf[1]); pRxBuf->internal.flags = INTERNAL_FCF_FLAGS(&rxBuf[1]) | ackWithPending; /*------------------------------------------------------------------------------- * If the processing the addressing fields does not require more bytes from * the FIFO go directly address processing function. Otherwise, configure * interrupt to jump there once bytes are received. */ if (addrLen == 0) { /* no addressing fields to read, prepare for payload interrupts */ pFuncRxState = &rxPayloadIsr; rxPrepPayload(); } else { /* need to read and process addressing fields, prepare for address interrupt */ rxNextLen = addrLen; if (MAC_SEC_ENABLED(&rxBuf[1])) { /* When security is enabled, read off security control field as well */ MAC_RADIO_SET_RX_THRESHOLD(rxNextLen + MAC_SEC_CONTROL_FIELD_LEN); } else { MAC_RADIO_SET_RX_THRESHOLD(rxNextLen); } pFuncRxState = &rxAddrIsr; } }
这个函数特别长,也特别复杂,但是我们从其中标红的这段代码和其描述可以知道,这段代码的作用是接受到从硬件电路通过ISA层上传至更高层的无线数据包(第292行,第369行至395行,特别是第390行和391行)。那么我们对
pRxBuf进行分析,我们发现该参数在该文件中被声明:
static macRx_t * pRxBuf;
/* Structure for internal data rx */ typedef struct { macEventHdr_t hdr; sData_t msdu; macRxIntData_t internal; macSec_t sec; macDataInd_t mac; sData_t mhr; } macRx_t;
然后这个参数在该函数被分配地址,并对其进行赋值。对于macDataInd_t有如下定义:
/* Data indication parameters type */ typedef struct { sAddr_t srcAddr; /* The address of the sending device */ sAddr_t dstAddr; /* The address of the destination device */ uint32 timestamp; /* The time, in backoffs, at which the data were received */ uint16 timestamp2; /* The time, in internal MAC timer units, at which the data were received */ uint16 srcPanId; /* The PAN ID of the sending device */ uint16 dstPanId; /* The PAN ID of the destination device */ uint8 mpduLinkQuality; /* The link quality of the received data frame */ uint8 correlation; /* The raw correlation value of the received data frame */ int8 rssi; /* The received RF power in units dBm */ uint8 dsn; /* The data sequence number of the received frame */ } macDataInd_t;
该定义后的注释告诉我们timestamp是收到数据时的回退(补偿)时间,而timestamp是收到数据时的内部定时器timer的值。通过对其赋值函数进行查询:
#define MAC_RADIO_BACKOFF_CAPTURE() macMcuOverflowCapture() #define MAC_RADIO_TIMER_CAPTURE() macMcuTimerCapture()
其中macMcuTimerCapture()函数是对系统时钟timer2的16位定时器数值的读取:
/************************************************************************************************** * @fn macMcuTimerCapture * * @brief Returns the last timer capture. This capture should have occurred at the * receive time of the last frame (the last time SFD transitioned to active). * * @param none * * @return last capture of hardware timer (full 16-bit value) ************************************************************************************************** */ MAC_INTERNAL_API uint16 macMcuTimerCapture(void) { uint16 timerCapture; halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); MAC_MCU_T2_ACCESS_CAPTURE_VALUE(); timerCapture = T2M1 << 8; timerCapture |= T2M0; HAL_EXIT_CRITICAL_SECTION(s); return (timerCapture); }
而macMucOverflowCapture()则是对定时器timer2的溢出计数器的捕获:
/************************************************************************************************** * @fn macMcuOverflowCapture * * @brief Returns the last capture of the overflow counter. A special hardware feature * captures the overflow counter when the regular hardware timer is captured. * * @param none * * @return last capture of overflow count ************************************************************************************************** */ MAC_INTERNAL_API uint32 macMcuOverflowCapture(void) { uint32 overflowCapture; halIntState_t s; /* for efficiency, the 32-bit value is encoded using endian abstracted indexing */ HAL_ENTER_CRITICAL_SECTION(s); MAC_MCU_T2_ACCESS_OVF_CAPTURE_VALUE(); ((uint8 *)&overflowCapture)[UINT32_NDX0] = T2MOVF0; ((uint8 *)&overflowCapture)[UINT32_NDX1] = T2MOVF1; ((uint8 *)&overflowCapture)[UINT32_NDX2] = T2MOVF2; ((uint8 *)&overflowCapture)[UINT32_NDX3] = 0; HAL_EXIT_CRITICAL_SECTION(s); return (overflowCapture); }
到目前,我们可以猜测这个afIncomingMSGPacket_t::timestamp是接受到数据包的时候系统定时器Timer2的溢出计数器的值。但这个我们也没有办法确认,那么我选择去看一看mac_tx.c和mac_tx.h文件,看在其中能否找到一些信息,对于mac_tx.h文件中对函数的声明:
MAC_INTERNAL_API void macTxInit(void); MAC_INTERNAL_API void macTxHaltCleanup(void); MAC_INTERNAL_API void macTxStartQueuedFrame(void); MAC_INTERNAL_API void macTxChannelBusyCallback(void); MAC_INTERNAL_API void macTxDoneCallback(void); MAC_INTERNAL_API void macTxAckReceivedCallback(uint8 seqn, uint8 pendingFlag); MAC_INTERNAL_API void macTxAckNotReceivedCallback(void); MAC_INTERNAL_API void macTxTimestampCallback(void); MAC_INTERNAL_API void macTxCollisionWithRxCallback(void);
我们对mac_tx.c文件中的函数作用有了一定的了解,然后我们去看mac_tx.c文件。当然我们的查看重点在于发送数据包的构建,对于该函数中有关timestamp的只有如下函数:
/************************************************************************************************** * @fn macTxTimestampCallback * * @brief This callback function records the timestamp into the receive data structure. * It should be called as soon as possible after there is a valid timestamp. * * @param none * * @return none ************************************************************************************************** */ MAC_INTERNAL_API void macTxTimestampCallback(void) { MAC_ASSERT(pMacDataTx != NULL); /* transmit structure must be there */ pMacDataTx->internal.timestamp = macBackoffTimerCapture(); pMacDataTx->internal.timestamp2 = MAC_RADIO_TIMER_CAPTURE(); }
这个函数的描述是“该回调函数记录时间戳到接受数据结构中”。到这我们可以非常清楚的知道afIncomingMSGPacket_t::timestamp是发送/接受数据包时系统定时器的溢出计数器的值。至于这个值到底是发送者的溢出计数器的值还是接受者的溢出计数器的值,我个人比较倾向于只是接受者接受到数据包时其系统定时器溢出计数器的值。我们可以通过发包含macMcuOverflowCapture()数据的数据包,然后在接受者接收时将3个数据显示出来查看。