hooks.c 9.52 KB
Newer Older
1 2 3
#include <ppsi/ppsi.h>

/* ext-whiterabbit must offer its own hooks */
4

baujc's avatar
baujc committed
5 6
int wrTmoIdx=0; /* TimeOut Index */

7
static int wr_init(struct pp_instance *ppi, void *buf, int len)
8
{
9
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
10

baujc's avatar
baujc committed
11 12 13
	if ( wrTmoIdx==0)
		wrTmoIdx=pp_timeout_get_timer(ppi,"WR_EXT_0",TO_RAND_NONE, TMO_CF_INSTANCE_DEPENDENT);

14
	wr_reset_process(ppi,WR_ROLE_NONE);
15
	ppi->pdstate = PP_PDSTATE_WAIT_MSG;
16

17 18 19 20 21 22 23
#ifdef CONFIG_ABSCAL
        /* absolute calibration only exists in arch-wrpc, so far */
        extern int ptp_mode;
        if (ptp_mode == 4 /* WRC_MODE_ABSCAL */)
                ppi->next_state = WRS_WR_LINK_ON;
#endif

24 25 26
	return 0;
}

27 28
/* open hook called only for each WR pp_instances */
static int wr_open(struct pp_instance *ppi, struct pp_runtime_opts *rt_opts)
29
{
30
	pp_diag(NULL, ext, 2, "hook: %s\n", __func__);
31

32 33 34 35 36 37 38 39 40 41 42
	if ( is_slaveOnly(DSDEF(ppi)) ||
			( is_externalPortConfigurationEnabled(DSDEF(ppi)) &&
					ppi->externalPortConfigurationPortDS.desiredState==PPS_SLAVE)
	   ){
		WR_DSPOR(ppi)->wrConfig = WR_S_ONLY;
	} else {
		WR_DSPOR(ppi)->wrConfig = ( ppi->portDS->masterOnly ||
				( is_externalPortConfigurationEnabled(DSDEF(ppi)) &&
						ppi->externalPortConfigurationPortDS.desiredState==PPS_MASTER)) ?
								WR_M_ONLY :
								WR_M_AND_S;
43
	}
44 45 46
	return 0;
}

47
static int wr_handle_resp(struct pp_instance *ppi)
48
{
49 50
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);

51
	/* This correction_field we received is already part of t4 */
52
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
53
		wr_servo_got_resp(ppi);
54 55
		if ( ppi->pdstate==PP_PDSTATE_PDETECTED)
			pdstate_set_state_pdetected(ppi); // Maintain state Protocol detected on MASTER side
56 57
	}
	else {
baujc's avatar
baujc committed
58
		pp_servo_got_resp(ppi,OPTS(ppi)->ptpFallbackPpsGen);
59 60 61 62
	}
	return 0;
}

63 64 65
static int wr_handle_dreq(struct pp_instance *ppi)
{
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
66
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
67 68 69 70 71 72 73
		if ( ppi->pdstate==PP_PDSTATE_PDETECTED)
			pdstate_set_state_pdetected(ppi); // Maintain state Protocol detected on MASTER side
	}

	return 0;
}

baujc's avatar
baujc committed
74
static int  wr_sync_followup(struct pp_instance *ppi) {
75

76
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
baujc's avatar
baujc committed
77 78 79 80 81
		wr_servo_got_sync(ppi);
	}
	else {
		pp_servo_got_sync(ppi,OPTS(ppi)->ptpFallbackPpsGen);
	}
82 83 84
	return 1; /* the caller returns too */
}

baujc's avatar
baujc committed
85
static int wr_handle_sync(struct pp_instance *ppi)
86 87 88
{
	/* This handle is called in case of one step clock */
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
baujc's avatar
baujc committed
89
	return wr_sync_followup(ppi);
90 91
}

baujc's avatar
baujc committed
92
static int wr_handle_followup(struct pp_instance *ppi)
93 94 95
{
	/* This handle is called in case of two step clock */
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
baujc's avatar
baujc committed
96
	return wr_sync_followup(ppi);
97 98
}

baujc's avatar
baujc committed
99
static int wr_handle_presp(struct pp_instance *ppi)
100
{
101
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
102
		wr_servo_got_presp(ppi);
103 104 105
		if ( ppi->pdstate==PP_PDSTATE_PDETECTED)
			pdstate_set_state_pdetected(ppi); // Maintain state Protocol detected on MASTER side
	}
baujc's avatar
baujc committed
106
	else
107
		pp_servo_got_presp(ppi);
108 109 110
	return 0;
}

111
static int wr_pack_announce(struct pp_instance *ppi)
112
{
113
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
114 115
	/* Even though the extension is disable we continue to send WR TLV */
	if ( WR_DSPOR(ppi)->wrConfig != WR_S_ONLY) {
116 117 118 119 120 121
		msg_pack_announce_wr_tlv(ppi);
		return WR_ANNOUNCE_LENGTH;
	}
	return PP_ANNOUNCE_LENGTH;
}

122
static void wr_unpack_announce(struct pp_instance *ppi,void *buf, MsgAnnounce *ann)
123
{
124
	MsgHeader *hdr = &ppi->received_ptp_header;
125
	int msg_len = ntohs(*(UInteger16 *) (buf + 2));
126 127 128 129
	Boolean parentIsWRnode=FALSE;
	Boolean resetWrProtocol=FALSE;
	int slaveUncalState=ppi->state==PPS_UNCALIBRATED || ppi->state==PPS_SLAVE;
	struct wr_dsport *wrp = WR_DSPOR(ppi);
130

131
	pp_diag(NULL, ext, 2, "hook: %s\n", __func__);
132 133 134 135 136

	// If the message is not coming from erbest, it must be discarded
	if ( !bmc_is_erbest(ppi,&hdr->sourcePortIdentity))
		return;

137 138 139 140 141
	if (msg_len >= WR_ANNOUNCE_LENGTH) {
		UInteger16 wr_flags;
		MsgHeader *hdr = &ppi->received_ptp_header;
		struct PortIdentity *pid = &hdr->sourcePortIdentity;

142

143 144 145
		msg_unpack_announce_wr_tlv(buf, ann, &wr_flags);
		parentIsWRnode=(wr_flags & WR_NODE_MODE)!=NON_WR;

146 147 148 149 150 151
		// Check if a new parent is detected.
		// This part is needed to cover the following use case :
		// on the master side, the WR extension is disabled (WR  calibration failure or PTP profile selected)
		// then the PPSi process is restarted using the WR profile. On the slave side, if the timeout ANN_RECEIPT has not fired,
		// we must detect that the PPSi process has been restarted and then replay the calibration protocol.
		// Checked parameters :
152 153 154 155 156
		// - The parent is a WR node
		// - ptp state=(slave|uncalibrated|listening)
		// - Same parent port ID but with a not continuous sequence ID (With a margin of 1)
		// - The port identity is different
		if ( parentIsWRnode &&
157
				(slaveUncalState ||	ppi->state==PPS_LISTENING))  {
158 159 160 161

			Boolean samePid=!bmc_pidcmp(pid, &wrp->parentAnnPortIdentity);
			if ( !samePid  ||
					(samePid &&
Jean-Claude BAU's avatar
Jean-Claude BAU committed
162 163
							(hdr->sequenceId!=(UInteger16) (wrp->parentAnnSequenceId+1) &&
							hdr->sequenceId!=(UInteger16) (wrp->parentAnnSequenceId+2))
164
							)) {
165 166
				/* For other states, it is done in the state_change hook */
				resetWrProtocol=slaveUncalState;
167
				pdstate_enable_extension(ppi);
168
			}
169 170
		} else {
			parentIsWRnode=FALSE;
171
			resetWrProtocol=ppi->extState==PP_EXSTATE_ACTIVE  &&  slaveUncalState;
172 173 174 175
		}
		memcpy(&wrp->parentAnnPortIdentity,pid,sizeof(struct PortIdentity));
		wrp->parentAnnSequenceId=hdr->sequenceId;

176

177 178 179 180 181 182 183 184
		/* Update the WR parent state */
		if ( !parentIsWRnode  )
			/* Not a WR node */
			wr_flags=0; /* Forget all bits. They are not relevant */
		wrp->parentIsWRnode = parentIsWRnode;
		wrp->parentWrModeOn = (wr_flags & WR_IS_WR_MODE) != 0;
		wrp->parentCalibrated =(wr_flags & WR_IS_CALIBRATED) != 0;
		wrp->parentWrConfig = wr_flags & WR_NODE_MODE;
185 186 187 188 189
	} else {
		resetWrProtocol=ppi->extState==PP_EXSTATE_ACTIVE  &&  slaveUncalState;
	}

	if ( resetWrProtocol ) {
Jean-Claude BAU's avatar
Jean-Claude BAU committed
190
		ppi->next_state=PPS_UNCALIBRATED;
191 192
		wrp->next_state=WRS_PRESENT;
		wrp->wrMode=WR_SLAVE;
193
	}
194 195
}

196 197 198 199 200
static void wr_state_change(struct pp_instance *ppi)
{
	struct wr_dsport *wrp = WR_DSPOR(ppi);
	
	pp_diag(ppi, ext, 2, "hook: %s\n", __func__);
201 202 203 204 205
	if ( ppi->extState==PP_EXSTATE_PTP &&  ppi->next_state==PPS_UNCALIBRATED ) {
		// Extension need to be re-enabled
		pdstate_enable_extension(ppi);
	}

206
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
207 208

		// Check leaving state
209 210 211 212 213
		if ( wrp->wrModeOn &&
				((ppi->state == PPS_SLAVE) ||
						(ppi->state == PPS_MASTER))) {
			/* if we are leaving the MASTER or SLAVE state */
			wr_reset_process(ppi,WR_ROLE_NONE);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
		}

		// Check entering state
		switch (ppi->next_state) {
		case PPS_MASTER : /* Enter in MASTER_STATE */
			wrp->next_state=WRS_IDLE;
			wr_reset_process(ppi,WR_MASTER);
			break;
		case PPS_UNCALIBRATED : /* Enter in UNCALIBRATED state */
			wrp->next_state=WRS_PRESENT;
			wr_reset_process(ppi,WR_SLAVE);
			break;
		case PPS_LISTENING : /* Enter in LISTENING state */
			wr_reset_process(ppi,WR_ROLE_NONE);
			break;
229 230
		}

231
	} else {
232
		wr_reset_process(ppi,WR_ROLE_NONE);
233
	}
234 235 236 237 238 239 240 241 242

	if (ppi->state == PPS_SLAVE) {
		/* We are leaving SLAVE state, so we must reset locking
		 * This must be done on all cases (extension ACTIVE or NOT) because it may be possible we entered
		 * in SLAVE state with the extension active and now it can be inactive
		 */
		WRH_OPER()->locking_reset(ppi);
		if ( ppi->next_state!=PPS_UNCALIBRATED ) {
			/* Leave SLAVE/UNCALIB states : We must stop the PPS generation */
243 244
			if ( !GOPTS(GLBS(ppi))->forcePpsGen )
				TOPS(ppi)->enable_timing_output(GLBS(ppi),0);
245 246
		}
	}
247
}
248

249
int wr_ready_for_slave(struct pp_instance *ppi) {
250
	if ( ppi->extState==PP_EXSTATE_ACTIVE ) {
251 252 253 254 255 256 257
		struct wr_dsport *wrp = WR_DSPOR(ppi);
		return wrp->wrModeOn && wrp->parentWrModeOn && wrp->state == WRS_IDLE;
	} else
		return 1; /* Allow to go to slave state as we will not do a WR handshake */
}


baujc's avatar
baujc committed
258
static int wr_require_precise_timestamp(struct pp_instance *ppi) {
259
	return ppi->extState==PP_EXSTATE_ACTIVE && WR_DSPOR(ppi)->wrModeOn;
baujc's avatar
baujc committed
260 261
}

262 263 264 265 266 267 268
static int wr_get_tmo_lstate_detection(struct pp_instance *ppi) {
	/* The WR protocol detection comes :
	 * - for a SLAVE: from the announce message (WR TLV)
	 * - for a MASTER: from the reception of the WR_PRESENT message
	 * To cover this 2 cases we will use 2 times the announce receipt time-out
	 */
	return is_externalPortConfigurationEnabled(DSDEF(ppi)) ?
269 270
			20000 : /* 20s: externalPortConfiguration enable means no ANN_RECEIPT timeout */
			pp_timeout_get(ppi,PP_TO_ANN_RECEIPT)<<3;
271 272 273 274 275 276 277 278 279 280
}

static TimeInterval wr_get_latency (struct pp_instance *ppi) {
	return 0;
}

/* WR extension is not compliant with the standard concerning the contents of the correction fields */
static int wr_is_correction_field_compliant (struct pp_instance *ppi) {
	return 0;
}
281 282 283 284 285 286 287 288

static int wr_extension_state_changed( struct pp_instance * ppi) {
	if ( ppi->extState==PP_EXSTATE_DISABLE || ppi->extState==PP_EXSTATE_PTP) {
		wr_reset_process(ppi,WR_ROLE_NONE);
	}
	return 0;
}

289
struct pp_ext_hooks wr_ext_hooks = {
290
	.init = wr_init,
291
	.open = wr_open,
292
	.handle_resp = wr_handle_resp,
293
	.handle_dreq = wr_handle_dreq,
294
	.handle_sync = wr_handle_sync,
295
	.handle_followup = wr_handle_followup,
296 297
	.ready_for_slave = wr_ready_for_slave,
	.run_ext_state_machine = wr_run_state_machine,
298
#if CONFIG_HAS_P2P
299
	.handle_presp = wr_handle_presp,
300
#endif
301 302
	.pack_announce = wr_pack_announce,
	.unpack_announce = wr_unpack_announce,
303
	.state_change = wr_state_change,
baujc's avatar
baujc committed
304 305
	.servo_reset= wr_servo_reset,
	.require_precise_timestamp=wr_require_precise_timestamp,
306 307 308 309
	.get_tmo_lstate_detection=wr_get_tmo_lstate_detection,
	.get_ingress_latency=wr_get_latency,
	.get_egress_latency=wr_get_latency,
	.is_correction_field_compliant=wr_is_correction_field_compliant,
310
	.extension_state_changed= wr_extension_state_changed
311
};