static void issueDelayReqTimerExpired(PtpClock *ptpClock) { switch (ptpClock->portDS.delayMechanism) { case E2E: if(ptpClock->portDS.portState != PTP_SLAVE) { break; } if (timerExpired(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { timerStart(DELAYREQ_INTERVAL_TIMER, getRand(pow2ms(ptpClock->portDS.logMinDelayReqInterval + 1)), ptpClock->itimer); DBGV("event DELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issueDelayReq(ptpClock); } break; case P2P: if (timerExpired(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { timerStart(PDELAYREQ_INTERVAL_TIMER, getRand(pow2ms(ptpClock->portDS.logMinPdelayReqInterval + 1)), ptpClock->itimer); DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issuePDelayReq(ptpClock); } break; default: break; } }
void handleSync(MsgHeader *header, Octet *msgIbuf, ssize_t length, TimeInternal *time, Boolean badTime, Boolean isFromSelf, PtpClock *ptpClock) { MsgSync *sync; TimeInternal originTimestamp; if(length < SYNC_PACKET_LENGTH) { ERROR("short sync message\n"); toState(PTP_FAULTY, ptpClock); return; } switch(ptpClock->port_state) { case PTP_FAULTY: case PTP_INITIALIZING: case PTP_DISABLED: DBGV("handleSync: disreguard\n"); return; case PTP_UNCALIBRATED: case PTP_SLAVE: if(isFromSelf) { DBG("handleSync: ignore from self\n"); return; } if(getFlag(header->flags, PTP_SYNC_BURST) && !ptpClock->burst_enabled) return; DBGV("handleSync: looking for uuid %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", ptpClock->parent_uuid[0], ptpClock->parent_uuid[1], ptpClock->parent_uuid[2], ptpClock->parent_uuid[3], ptpClock->parent_uuid[4], ptpClock->parent_uuid[5]); if( header->sequenceId > ptpClock->parent_last_sync_sequence_number && header->sourceCommunicationTechnology == ptpClock->parent_communication_technology && header->sourcePortId == ptpClock->parent_port_id && !memcmp(header->sourceUuid, ptpClock->parent_uuid, PTP_UUID_LENGTH) ) { /* addForeign() takes care of msgUnpackSync() */ ptpClock->record_update = TRUE; sync = addForeign(ptpClock->msgIbuf, &ptpClock->msgTmpHeader, ptpClock); if(sync->syncInterval != ptpClock->sync_interval) { DBGV("message's sync interval is %d, but clock's is %d\n", sync->syncInterval, ptpClock->sync_interval); /* spec recommends handling a sync interval discrepancy as a fault */ } /* * TODO: Sync packets without hardware time stamp are rare, but might happen. * Need to decide what to do with the bad default time stamp, similar to handleDelayReq(). */ ptpClock->sync_receive_time.seconds = time->seconds; ptpClock->sync_receive_time.nanoseconds = time->nanoseconds; if(!getFlag(header->flags, PTP_ASSIST)) { ptpClock->waitingForFollow = FALSE; toInternalTime(&originTimestamp, &sync->originTimestamp, &ptpClock->halfEpoch); updateOffset(&originTimestamp, &ptpClock->sync_receive_time, &ptpClock->ofm_filt, ptpClock); updateClock(ptpClock); } else { ptpClock->waitingForFollow = TRUE; } s1(header, sync, ptpClock); if(!(--ptpClock->R)) { issueDelayReq(ptpClock); ptpClock->Q = 0; ptpClock->R = getRand(&ptpClock->random_seed)%(PTP_DELAY_REQ_INTERVAL - 2) + 2; DBG("Q = %d, R = %d\n", ptpClock->Q, ptpClock->R); } DBGV("SYNC_RECEIPT_TIMER reset\n"); timerStart(SYNC_RECEIPT_TIMER, PTP_SYNC_RECEIPT_TIMEOUT(ptpClock->sync_interval), ptpClock->itimer); } else { DBGV("handleSync: unwanted\n"); } case PTP_MASTER: default: if( header->sourceCommunicationTechnology == ptpClock->clock_communication_technology || header->sourceCommunicationTechnology == PTP_DEFAULT || ptpClock->clock_communication_technology == PTP_DEFAULT ) { if(!isFromSelf) { ptpClock->record_update = TRUE; addForeign(ptpClock->msgIbuf, &ptpClock->msgTmpHeader, ptpClock); } else if(ptpClock->port_state == PTP_MASTER && ptpClock->clock_followup_capable) { addTime(time, time, &ptpClock->runTimeOpts.outboundLatency); issueFollowup(time, ptpClock); } } break; } }
/* handle actions and events for 'port_state' */ void doState(RunTimeOpts *rtOpts, PtpClock *ptpClock) { UInteger8 state; ptpClock->message_activity = FALSE; /* Process record_update (BMC algorithm) before everything else */ switch (ptpClock->portState) { case PTP_LISTENING: case PTP_PASSIVE: case PTP_SLAVE: case PTP_MASTER: /*State decision Event*/ /* If we received a valid Announce message, and can use it (record_update), then run the BMC algorithm */ if(ptpClock->record_update) { DBG2("event STATE_DECISION_EVENT\n"); ptpClock->record_update = FALSE; state = bmc(ptpClock->foreign, rtOpts, ptpClock); if(state != ptpClock->portState) toState(state, rtOpts, ptpClock); } break; default: break; } switch (ptpClock->portState) { case PTP_FAULTY: /* imaginary troubleshooting */ DBG("event FAULT_CLEARED\n"); toState(PTP_INITIALIZING, rtOpts, ptpClock); return; case PTP_LISTENING: case PTP_UNCALIBRATED: case PTP_SLAVE: // passive mode behaves like the SLAVE state, in order to wait for the announce timeout of the current active master case PTP_PASSIVE: handle(rtOpts, ptpClock); /* * handle SLAVE timers: * - No Announce message was received * - Time to send new delayReq (miss of delayResp is not monitored explicitelly) */ if (timerExpired(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer)) { DBG("event ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES\n"); ptpClock->number_foreign_records = 0; ptpClock->foreign_record_i = 0; if(!ptpClock->slaveOnly && ptpClock->clockQuality.clockClass != 255) { m1(rtOpts,ptpClock); toState(PTP_MASTER, rtOpts, ptpClock); } else { /* * Force a reset when getting a timeout in state listening, that will lead to an IGMP reset * previously this was not the case when we were already in LISTENING mode */ toState(PTP_LISTENING, rtOpts, ptpClock); } } if (timerExpired(OPERATOR_MESSAGES_TIMER, ptpClock->itimer)) { reset_operator_messages(rtOpts, ptpClock); } if (ptpClock->delayMechanism == E2E) { if(timerExpired(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { DBG2("event DELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issueDelayReq(rtOpts,ptpClock); } } else if (ptpClock->delayMechanism == P2P) { if (timerExpired(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issuePDelayReq(rtOpts,ptpClock); } /* FIXME: Path delay should also rearm its timer with the value received from the Master */ } break; case PTP_MASTER: /* * handle SLAVE timers: * - Time to send new Sync * - Time to send new Announce * - Time to send new PathDelay * (DelayResp has no timer - as these are sent and retransmitted by the slaves) */ if (timerExpired(SYNC_INTERVAL_TIMER, ptpClock->itimer)) { DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n"); issueSync(rtOpts, ptpClock); } if (timerExpired(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer)) { DBGV("event ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES\n"); issueAnnounce(rtOpts, ptpClock); } if (ptpClock->delayMechanism == P2P) { if (timerExpired(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer)) { DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n"); issuePDelayReq(rtOpts,ptpClock); } } // TODO: why is handle() below expiretimer, while in slave is the opposite handle(rtOpts, ptpClock); if (ptpClock->slaveOnly || ptpClock->clockQuality.clockClass == 255) toState(PTP_LISTENING, rtOpts, ptpClock); break; case PTP_DISABLED: handle(rtOpts, ptpClock); break; default: DBG("(doState) do unrecognized state\n"); break; } }