liunxptp 代码学习
PTP协议栈详解
场景一
当我们配置时钟A为OC且一个端口为master,一个时钟B为BC,一个端口为slave,且延迟机制为E2E。
延迟机制的基本原理
延迟机制的算法方式
协议栈的过程
一、step 1: master->slave(sync报文的发送和sync的follow_up报文的发送)
int clock_poll(struct clock *c)
{
int cnt, i;
enum fsm_event event;
struct pollfd *cur;
struct port *p;
clock_check_pollfd(c);
cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1);
if (cnt < 0) {
if (EINTR == errno) {
return 0;
} else {
pr_emerg("poll failed");
return -1;
}
} else if (!cnt) {
return 0;
}
cur = c->pollfd;
LIST_FOREACH(p, &c->ports, list) {
/* Let the ports handle their events. */
for (i = 0; i < N_POLLFD; i++) {
if (cur[i].revents & (POLLIN|POLLPRI|POLLERR)) {
if (cur[i].revents & POLLERR) {
pr_err("port %d: unexpected socket error",
port_number(p));
event = EV_FAULT_DETECTED;
} else {
/*引发事件,这是一个回调,当为0C/BC是回调bc_event,E2ETC时回e2e_event.P2PTC时回调p2p_event*/
event = port_event(p, i);
}
/* 推荐状态事件是由STATE—DECISION—EVENT引发的执行最佳主时钟算法的结果*/
if (EV_STATE_DECISION_EVENT == event) {
c->sde = 1;
}
/* see 9.2.6 */
if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event) {
c->sde = 1;
}
port_dispatch(p, event, 0);
/* Clear any fault after a little while. */
if (PS_FAULTY == port_state(p)) {
clock_fault_timeout(p, 1);
break;
}
}
}
bc_event的代码
// bc_event
static enum fsm_event bc_event(struct port *p, int fd_index)
{
enum fsm_event event = EV_NONE;
struct ptp_message *msg;
int cnt, fd = p->fda.fd[fd_index], err;
/*时钟自己发送的请求*/
switch (fd_index) {
case FD_ANNOUNCE_TIMER:
case FD_SYNC_RX_TIMER:
pr_debug("port %hu: %s timeout", portnum(p),
fd_index == FD_SYNC_RX_TIMER ? "rx sync" : "announce");
if (p->best) {
fc_clear(p->best);
}
/*
* Clear out the event returned by poll(). It is only cleared
* in port_*_transition(). But, when BMCA == 'noop', there is no
* state transition. So, it won't be cleared anywhere else.
*/
if (p->bmca == BMCA_NOOP) {
port_clr_tmo(p->fda.fd[FD_SYNC_RX_TIMER]);
}
if (p->inhibit_announce) {
port_clr_tmo(p->fda.fd[FD_ANNOUNCE_TIMER]);
} else {
port_set_announce_tmo(p);
}
delay_req_prune(p);
if (clock_slave_only(p->clock) && p->delayMechanism != DM_P2P &&
port_renew_transport(p)) {
return EV_FAULT_DETECTED;
}
if (p->inhibit_announce) {
return EV_NONE;
}
return EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES;
case FD_DELAY_TIMER:
pr_debug("port %hu: delay timeout", portnum(p));
port_set_delay_tmo(p);
delay_req_prune(p);
return port_delay_request(p) ? EV_FAULT_DETECTED : EV_NONE;
case FD_QUALIFICATION_TIMER:
pr_debug("port %hu: qualification timeout", portnum(p));
return EV_QUALIFICATION_TIMEOUT_EXPIRES;
case FD_MANNO_TIMER:
pr_debug("port %hu: master tx announce timeout", portnum(p));
port_set_manno_tmo(p);
return port_tx_announce(p, NULL) ? EV_FAULT_DETECTED : EV_NONE;
case FD_SYNC_TX_TIMER:
pr_debug("port %hu: master sync timeout", portnum(p));
port_set_sync_tx_tmo(p);
/*处于master端口应周期性的发送sync报文,sync报文应多播方式发送*/
return port_tx_sync(p, NULL) ? EV_FAULT_DETECTED : EV_NONE;
case FD_UNICAST_SRV_TIMER:
pr_debug("port %hu: unicast service timeout", portnum(p));
return unicast_service_timer(p) ? EV_FAULT_DETECTED : EV_NONE;
case FD_UNICAST_REQ_TIMER:
pr_debug("port %hu: unicast request timeout", portnum(p));
return unicast_client_timer(p) ? EV_FAULT_DETECTED : EV_NONE;
case FD_RTNL:
pr_debug("port %hu: received link status notification", portnum(p));
rtnl_link_status(fd, p->name, port_link_status, p);
if (p->link_status == (LINK_UP | LINK_STATE_CHANGED))
return EV_FAULT_CLEARED;
else if ((p->link_status == (LINK_DOWN | LINK_STATE_CHANGED)) ||
(p->link_status & TS_LABEL_CHANGED))
return EV_FAULT_DETECTED;
else
return EV_NONE;
}
msg = msg_allocate();
if (!msg)
return EV_FAULT_DETECTED;
msg->hwts.type = p->timestamping;
/*时钟接收到的报文*/
cnt = transport_recv(p->trp, fd, msg);
if (cnt < 0) {
pr_err("port %hu: recv message failed", portnum(p));
msg_put(msg);
return EV_FAULT_DETECTED;
}
err = msg_post_recv(msg, cnt);
if (err) {
switch (err) {
case -EBADMSG:
pr_err("port %hu: bad message", portnum(p));
break;
case -EPROTO:
pr_debug("port %hu: ignoring message", portnum(p));
break;
}
msg_put(msg);
return EV_NONE;
}
port_stats_inc_rx(p, msg);
if (port_ignore(p, msg)) {
msg_put(msg);
return EV_NONE;
}
if (msg_sots_missing(msg) &&
!(p->timestamping == TS_P2P1STEP && msg_type(msg) == PDELAY_REQ)) {
pr_err("port %hu: received %s without timestamp",
portnum(p), msg_type_string(msg_type(msg)));
msg_put(msg);
return EV_NONE;
}
if (msg_sots_valid(msg)) {
ts_add(&msg->hwts.ts, -p->rx_timestamp_offset);
clock_check_ts(p->clock, tmv_to_nanoseconds(msg->hwts.ts));
}
/*对收到的报文进行处理*/
switch (msg_type(msg)) {
case SYNC:
process_sync(p, msg);
break;
case DELAY_REQ:
if (process_delay_req(p, msg))
event = EV_FAULT_DETECTED;
break;
case PDELAY_REQ:
if (process_pdelay_req(p, msg))
event = EV_FAULT_DETECTED;
break;
case PDELAY_RESP:
if (process_pdelay_resp(p, msg))
event = EV_FAULT_DETECTED;
break;
case FOLLOW_UP:
process_follow_up(p, msg);
break;
case DELAY_RESP:
process_delay_resp(p, msg);
break;
case PDELAY_RESP_FOLLOW_UP:
process_pdelay_resp_fup(p, msg);
break;
case ANNOUNCE:
if (process_announce(p, msg))
event = EV_STATE_DECISION_EVENT;
break;
case SIGNALING:
if (process_signaling(p, msg)) {
event = EV_FAULT_DETECTED;
}
break;
case MANAGEMENT:
if (clock_manage(p->clock, p, msg))
event = EV_STATE_DECISION_EVENT;
break;
}
msg_put(msg);
return event;
}
bc_event中有两个switch(msg_type),第一个主动发出的报文,第二个对接收到的报文进行处理。
int port_tx_announce(struct port *p, struct address *dst)
{
struct timePropertiesDS tp = clock_time_properties(p->clock);
struct parent_ds *dad = clock_parent_ds(p->clock);
struct ptp_message *msg;
int err;
if (p->inhibit_multicast_service && !dst) {
return 0;
}
if (!port_capable(p)) {
return 0;
}
msg = msg_allocate();
if (!msg) {
return -1;
}
msg->hwts.type = p->timestamping;
msg->header.tsmt = ANNOUNCE | p->transportSpecific;
msg->header.ver = PTP_VERSION;
msg->header.messageLength = sizeof(struct announce_msg);
msg->header.domainNumber = clock_domain_number(p->clock);
msg->header.sourcePortIdentity = p->portIdentity;
msg->header.sequenceId = p->seqnum.announce++;
msg->header.control = CTL_OTHER;
msg->header.logMessageInterval = p->logAnnounceInterval;
msg->header.flagField[1] = tp.flags;
if (dst) {
msg->address = *dst;
msg->header.flagField[0] |= UNICAST;
}
msg->announce.currentUtcOffset = tp.currentUtcOffset;
msg->announce.grandmasterPriority1 = dad->pds.grandmasterPriority1;
msg->announce.grandmasterClockQuality = dad->pds.grandmasterClockQuality;
msg->announce.grandmasterPriority2 = dad->pds.grandmasterPriority2;
msg->announce.grandmasterIdentity = dad->pds.grandmasterIdentity;
msg->announce.stepsRemoved = clock_steps_removed(p->clock);
msg->announce.timeSource = tp.timeSource;
if (p->path_trace_enabled && path_trace_append(p, msg, dad)) {
pr_err("port %hu: append path trace failed", portnum(p));
}
err = port_prepare_and_send(p, msg, TRANS_GENERAL)