bmc.c 8.75 KB
Newer Older
1
/*
2 3
 * Copyright (C) 2011 CERN (www.cern.ch)
 * Author: Aurelio Colosimo
4
 * Based on PTPd project v. 2.1.0 (see AUTHORS for details)
5 6
 *
 * Released according to the GNU LGPL, version 2.1 or any later version.
7
 */
8

Alessandro Rubini's avatar
Alessandro Rubini committed
9
#include <ppsi/ppsi.h>
10

11 12 13 14 15 16 17 18
/* Flag Field bits symbolic names (table 57, pag. 151) */
#define FFB_LI61	0x01
#define FFB_LI59	0x02
#define FFB_UTCV	0x04
#define FFB_PTP		0x08
#define FFB_TTRA	0x10
#define FFB_FTRA	0x20

19
/* ppi->port_idx port is becoming Master. Table 13 (9.3.5) of the spec. */
20 21
void m1(struct pp_instance *ppi)
{
Alessandro Rubini's avatar
Alessandro Rubini committed
22 23 24
	struct DSParent *parent = DSPAR(ppi);
	struct DSDefault *defds = DSDEF(ppi);

25 26
	/* Current data set update */
	DSCUR(ppi)->stepsRemoved = 0;
27 28
	clear_time(&DSCUR(ppi)->offsetFromMaster);
	clear_time(&DSCUR(ppi)->meanPathDelay);
29

Alessandro Rubini's avatar
Alessandro Rubini committed
30 31 32
	/* Parent data set: we are the parent */
	memset(parent, 0, sizeof(*parent));
	parent->parentPortIdentity.clockIdentity = defds->clockIdentity;
33
	parent->parentPortIdentity.portNumber = 0;
Alessandro Rubini's avatar
Alessandro Rubini committed
34 35 36 37 38 39

	/* Copy grandmaster params from our defds (FIXME: is ir right?) */
	parent->grandmasterIdentity = defds->clockIdentity;
	parent->grandmasterClockQuality = defds->clockQuality;
	parent->grandmasterPriority1 = defds->priority1;
	parent->grandmasterPriority2 = defds->priority2;
40 41

	/* Time Properties data set */
42
	DSPRO(ppi)->ptpTimescale = TRUE;
43 44 45 46
	DSPRO(ppi)->timeSource = INTERNAL_OSCILLATOR;
}


47
/* ppi->port_idx port is synchronized to Ebest Table 16 (9.3.5) of the spec. */
48
static void s1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
49
{
Alessandro Rubini's avatar
Alessandro Rubini committed
50 51 52
	struct DSParent *parent = DSPAR(ppi);
	struct DSTimeProperties *prop = DSPRO(ppi);

53
	/* Current DS */
54
	DSCUR(ppi)->stepsRemoved = ann->stepsRemoved + 1;
55 56

	/* Parent DS */
Alessandro Rubini's avatar
Alessandro Rubini committed
57 58 59 60 61
	parent->parentPortIdentity = hdr->sourcePortIdentity;
	parent->grandmasterIdentity = ann->grandmasterIdentity;
	parent->grandmasterClockQuality = ann->grandmasterClockQuality;
	parent->grandmasterPriority1 = ann->grandmasterPriority1;
	parent->grandmasterPriority2 = ann->grandmasterPriority2;
62 63

	/* Timeproperties DS */
Alessandro Rubini's avatar
Alessandro Rubini committed
64
	prop->timeSource = ann->timeSource;
65
	if (prop->currentUtcOffset != ann->currentUtcOffset) {
66 67
		pp_diag(ppi, bmc, 1, "New UTC offset: %i\n",
			ann->currentUtcOffset);
68 69 70
		prop->currentUtcOffset = ann->currentUtcOffset;
		ppi->t_ops->set(ppi, NULL);
	}
Alessandro Rubini's avatar
Alessandro Rubini committed
71 72 73 74 75 76 77 78

	/* FIXME: can't we just copy the bit keeping values? */
	prop->currentUtcOffsetValid = ((hdr->flagField[1] & FFB_UTCV)	!= 0);
	prop->leap59 = ((hdr->flagField[1] & FFB_LI59) != 0);
	prop->leap61 = ((hdr->flagField[1] & FFB_LI61) != 0);
	prop->timeTraceable = ((hdr->flagField[1] & FFB_TTRA) != 0);
	prop->frequencyTraceable = ((hdr->flagField[1] & FFB_FTRA) != 0);
	prop->ptpTimescale = ((hdr->flagField[1] & FFB_PTP) != 0);
79 80 81

	if (pp_hooks.s1)
		pp_hooks.s1(ppi, hdr, ann);
82 83
}

84
static void p1(struct pp_instance *ppi, MsgHeader *hdr, MsgAnnounce *ann)
85 86 87 88 89
{
	/* In the default implementation, nothing should be done when a port goes
	 * to passive state. This empty function is a placeholder for
	 * extension-specific needs, to be implemented as a hook */
}
90

91
/* Copy local data set into header and ann message. 9.3.4 table 12. */
92
static void copy_d0(struct pp_instance *ppi, struct pp_frgn_master *m)
93
{
94
	struct DSDefault *defds = DSDEF(ppi);
95 96
	struct MsgHeader *hdr = &m->hdr;
	struct MsgAnnounce *ann = &m->ann;
97 98 99 100 101

	ann->grandmasterIdentity = defds->clockIdentity;
	ann->grandmasterClockQuality = defds->clockQuality;
	ann->grandmasterPriority1 = defds->priority1;
	ann->grandmasterPriority2 = defds->priority2;
102
	ann->stepsRemoved = 0;
103
	hdr->sourcePortIdentity.clockIdentity = defds->clockIdentity;
104 105
}

106 107 108 109
static int idcmp(struct ClockIdentity *a, struct ClockIdentity *b)
{
	return memcmp(a, b, sizeof(*a));
}
110 111

/*
112 113 114
 * Data set comparison between two foreign masters. Return similar to
 * memcmp().  However, lower values take precedence, so in A-B (like
 * in comparisons,   > 0 means B wins (and < 0 means A wins).
115
 */
116
static int bmc_dataset_cmp(struct pp_instance *ppi,
117 118
			   struct pp_frgn_master *a,
			   struct pp_frgn_master *b)
119
{
120
	struct ClockQuality *qa, *qb;
121 122
	struct MsgAnnounce *aa = &a->ann;
	struct MsgAnnounce *ab = &b->ann;
123 124 125
	struct ClockIdentity *ida = &a->hdr.sourcePortIdentity.clockIdentity;
	struct ClockIdentity *idb = &b->hdr.sourcePortIdentity.clockIdentity;
	struct ClockIdentity *idparent;
126
	int diff;
127

128 129
	/* dataset_cmp is called several times, so report only at level 2 */
	pp_diag(ppi, bmc, 2,"%s\n", __func__);
130

131
	if (!idcmp(&aa->grandmasterIdentity, &ab->grandmasterIdentity)) {
132

133
		/* The grandmaster is the same: part 2, fig 28, page 90. */
134

135 136 137
		diff = aa->stepsRemoved - ab->stepsRemoved;
		if (diff > 1 || diff < -1)
			return diff;
138

139
		idparent = &DSPAR(ppi)->parentPortIdentity.clockIdentity;
140

141
		if (diff > 0) {
142
			if (!idcmp(ida, idparent)) {
143 144
				pp_diag(ppi, bmc, 1,"%s:%i: Error 1\n",
					__func__, __LINE__);
145 146 147
				return 0;
			}
			return 1;
148

149
		}
150
		if (diff < 0) {
151
			if (!idcmp(idb, idparent)) {
152 153
				pp_diag(ppi, bmc, 1,"%s:%i: Error 1\n",
					__func__, __LINE__);
154
				return 0;
155
			}
156 157
			return -1;
		}
158 159 160
		/* stepsRemoved is equal, compare identities */
		diff = idcmp(ida, idb);
		if (!diff) {
161
			pp_diag(ppi, bmc, 1,"%s:%i: Error 2\n", __func__, __LINE__);
162
			return 0;
163
		}
164
		return diff;
165 166
	}

167
	/* The grandmasters are different: part 1, fig 27, page 89. */
168 169
	qa = &aa->grandmasterClockQuality;
	qb = &ab->grandmasterClockQuality;
170

171 172
	if (aa->grandmasterPriority1 != ab->grandmasterPriority1)
		return aa->grandmasterPriority1 - ab->grandmasterPriority1;
173

174 175
	if (qa->clockClass != qb->clockClass)
		return qa->clockClass - qb->clockClass;
176

177 178
	if (qa->clockAccuracy != qb->clockAccuracy)
		return qa->clockAccuracy - qb->clockAccuracy;
179

180 181
	if (qa->offsetScaledLogVariance != qb->offsetScaledLogVariance)
		return qa->clockClass - qb->clockClass;
182

183 184
	if (aa->grandmasterPriority2 != ab->grandmasterPriority2)
		return aa->grandmasterPriority2 - ab->grandmasterPriority2;
185

186
	return idcmp(&aa->grandmasterIdentity, &ab->grandmasterIdentity);
187 188 189
}

/* State decision algorithm 9.3.3 Fig 26 */
190 191
static int bmc_state_decision(struct pp_instance *ppi,
							  struct pp_frgn_master *m)
192
{
193
	int cmpres, ret;
194
	struct pp_frgn_master myself;
195

196
	if (ppi->role == PPSI_ROLE_SLAVE)
197
		goto slave;
198

199
	if ((!ppi->frgn_rec_num) && (ppi->state == PPS_LISTENING))
200 201
		return PPS_LISTENING;

202 203
	/* copy local information to a foreign_master structure */
	copy_d0(ppi, &myself);
204

205
	/* dataset_cmp is "a - b" but lower values win */
206
	cmpres = bmc_dataset_cmp(ppi, &myself, m);
207

208 209 210
	if (ppi->role == PPSI_ROLE_MASTER)
		goto master;
	
211
	if (DSDEF(ppi)->clockQuality.clockClass < 128) {
212 213
		if (cmpres < 0)
			goto master;
214 215
		if (cmpres > 0)
			goto passive;
216
	}
217 218
	if (cmpres < 0)
		goto master;
219 220 221 222 223 224
	if (cmpres > 0) {
		if (DSDEF(ppi)->numberPorts == 1)
			goto slave; /* directly skip to ordinary clock handling */
		else
			goto check_boundary_clk;
	}
225

226
	pp_diag(ppi, bmc, 1,"%s: error\n", __func__);
227 228 229 230

	/*  MB: Is this the return code below correct? */
	/*  Anyway, it's a valid return code. */
	return PPS_FAULTY;
231

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
check_boundary_clk:
	if (ppi->port_idx == GLBS(ppi)->ebest_idx) /* This port is the Ebest */
		goto slave;

	/* If idcmp returns 0, it means that this port is not the best because
		* Ebest is better by topology than Erbest */
	if (!idcmp(&myself.ann.grandmasterIdentity,
			&m->ann.grandmasterIdentity))
		goto passive;
	else
		goto master;

passive:
	p1(ppi, &m->hdr, &m->ann);
	pp_diag(ppi, bmc, 1,"%s: passive\n", __func__);
	return PPS_PASSIVE;

249
master:
250
	//TODO: consider whether a smarter solution is needed for non-simple cases
251
	if(cmpres < 0) { // it is M1 and M2, see IEEE1588-2008, page 87, in short switch is a GM
252
		m1(ppi); //GM
253 254 255 256 257 258
		ret = PPS_MASTER;
	} else
		ret = PPS_PRE_MASTER;
	pp_diag(ppi, bmc, 1,"%s: %smaster\n", __func__,
		ret == PPS_PRE_MASTER ? "pre-" : "");
	return ret;
259 260 261

slave:
	s1(ppi, &m->hdr, &m->ann);
262
	pp_diag(ppi, bmc, 1,"%s: slave\n", __func__);
263
	return PPS_SLAVE;
264

265 266
}

267
/* Find Ebest, 9.3.2.2 */
268
static void bmc_update_ebest(struct pp_globals *ppg)
269 270 271 272
{
	int i, best;
	struct pp_instance *ppi, *ppi_best;

273
	for (i = 1, best = 0; i < ppg->defaultDS->numberPorts; i++) {
274

275 276
		ppi_best = INST(ppg, best);
		ppi = INST(ppg, i);
277 278 279 280 281 282 283 284 285 286 287 288 289 290

		if ((ppi->frgn_rec_num > 0) &&
			 (bmc_dataset_cmp(ppi,
				&ppi_best->frgn_master[ppi_best->frgn_rec_best],
				&ppi->frgn_master[ppi->frgn_rec_best])
				< 0))
				best = i;
	}

	if (ppg->ebest_idx != best) {
		ppg->ebest_idx = best;
		ppg->ebest_updated = 1;
	}
}
291

292
int bmc(struct pp_instance *ppi)
293
{
294
	struct pp_frgn_master *frgn_master = ppi->frgn_master;
295
	int i, best;
296

297
	if (!ppi->frgn_rec_num)
298 299 300 301 302
		if (ppi->state == PPS_MASTER)	{
			m1(ppi);
			return ppi->state;
		}

303 304
	/* Find Erbest, 9.3.2.3 */
	for (i = 1, best = 0; i < ppi->frgn_rec_num; i++)
305 306
		if (bmc_dataset_cmp(ppi, &frgn_master[i], &frgn_master[best])
		    < 0)
307 308
			best = i;

309 310
	pp_diag(ppi, bmc, 1,"Best foreign master is %i/%i\n", best,
		ppi->frgn_rec_num);
311 312 313 314
	if (ppi->frgn_rec_best != best) {
		ppi->frgn_rec_best = best;
		bmc_update_ebest(GLBS(ppi));
	}
315

316
	return bmc_state_decision(ppi, &frgn_master[best]);
317
}