aboutsummaryrefslogtreecommitdiffstats
path: root/qsfp-dd.c
blob: 900bbb5f4e10e72b44920469f3a6dda344a04149 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/**
 * Description:
 *
 * This module adds QSFP-DD support to ethtool. The changes are similar to
 * the ones already existing in qsfp.c, but customized to use the memory
 * addresses and logic as defined in the specification's document.
 *
 */

#include <stdio.h>
#include <math.h>
#include "internal.h"
#include "sff-common.h"
#include "qsfp-dd.h"

static void qsfp_dd_show_identifier(const __u8 *id)
{
	sff8024_show_identifier(id, QSFP_DD_ID_OFFSET);
}

static void qsfp_dd_show_connector(const __u8 *id)
{
	sff8024_show_connector(id, QSFP_DD_CTOR_OFFSET);
}

static void qsfp_dd_show_oui(const __u8 *id)
{
	sff8024_show_oui(id, QSFP_DD_VENDOR_OUI_OFFSET);
}

/**
 * Print the revision compliance. Relevant documents:
 * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
 * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
 */
static void qsfp_dd_show_rev_compliance(const __u8 *id)
{
	__u8 rev = id[QSFP_DD_REV_COMPLIANCE_OFFSET];
	int major = (rev >> 4) & 0x0F;
	int minor = rev & 0x0F;

	printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
}

/**
 * Print information about the device's power consumption.
 * Relevant documents:
 * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
 * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
 * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
 */
static void qsfp_dd_show_power_info(const __u8 *id)
{
	float max_power = 0.0f;
	__u8 base_power = 0;
	__u8 power_class;

	/* Get the power class (first 3 most significat bytes) */
	power_class = (id[QSFP_DD_PWR_CLASS_OFFSET] >> 5) & 0x07;

	/* Get the base power in multiples of 0.25W */
	base_power = id[QSFP_DD_PWR_MAX_POWER_OFFSET];
	max_power = base_power * 0.25f;

	printf("\t%-41s : %d\n", "Power class", power_class + 1);
	printf("\t%-41s : %.02fW\n", "Max power", max_power);
}

/**
 * Print the cable assembly length, for both passive copper and active
 * optical or electrical cables. The base length (bits 5-0) must be
 * multiplied with the SMF length multiplier (bits 7-6) to obtain the
 * correct value. Relevant documents:
 * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
 * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
 */
static void qsfp_dd_show_cbl_asm_len(const __u8 *id)
{
	static const char *fn = "Cable assembly length";
	float mul = 1.0f;
	float val = 0.0f;

	/* Check if max length */
	if (id[QSFP_DD_CBL_ASM_LEN_OFFSET] == QSFP_DD_6300M_MAX_LEN) {
		printf("\t%-41s : > 6.3km\n", fn);
		return;
	}

	/* Get the multiplier from the first two bits */
	switch (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
	case QSFP_DD_MULTIPLIER_00:
		mul = 0.1f;
		break;
	case QSFP_DD_MULTIPLIER_01:
		mul = 1.0f;
		break;
	case QSFP_DD_MULTIPLIER_10:
		mul = 10.0f;
		break;
	case QSFP_DD_MULTIPLIER_11:
		mul = 100.0f;
		break;
	default:
		break;
	}

	/* Get base value from first 6 bits and multiply by mul */
	val = (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
	val = (float)val * mul;
	printf("\t%-41s : %0.2fm\n", fn, val);
}

/**
 * Print the length for SMF fiber. The base length (bits 5-0) must be
 * multiplied with the SMF length multiplier (bits 7-6) to obtain the
 * correct value. Relevant documents:
 * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
 * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
 */
static void qsfp_dd_print_smf_cbl_len(const __u8 *id)
{
	static const char *fn = "Length (SMF)";
	float mul = 1.0f;
	float val = 0.0f;

	/* Get the multiplier from the first two bits */
	switch (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
	case QSFP_DD_MULTIPLIER_00:
		mul = 0.1f;
		break;
	case QSFP_DD_MULTIPLIER_01:
		mul = 1.0f;
		break;
	default:
		break;
	}

	/* Get base value from first 6 bits and multiply by mul */
	val = (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
	val = (float)val * mul;
	printf("\t%-41s : %0.2fkm\n", fn, val);
}

/**
 * Print relevant signal integrity control properties. Relevant documents:
 * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
 * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
 */
static void qsfp_dd_show_sig_integrity(const __u8 *id)
{
	/* CDR Bypass control: 2nd bit from each byte */
	printf("\t%-41s : ", "Tx CDR bypass control");
	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x02));

	printf("\t%-41s : ", "Rx CDR bypass control");
	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x02));

	/* CDR Implementation: 1st bit from each byte */
	printf("\t%-41s : ", "Tx CDR");
	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x01));

	printf("\t%-41s : ", "Rx CDR");
	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x01));
}

/**
 * Print relevant media interface technology info. Relevant documents:
 * [1] CMIS Rev. 3
 * --> pag. 61, section 1.7.3.14, Table 36
 * --> pag. 64, section 1.7.4.3, 1.7.4.4
 * [2] CMIS Rev. 4
 * --> pag. 97, section 8.3.14, Table 8-24
 * --> pag. 98, section 8.4, Table 8-25
 * --> page 100, section 8.4.3, 8.4.4
 */
static void qsfp_dd_show_mit_compliance(const __u8 *id)
{
	static const char *cc = " (Copper cable,";

	printf("\t%-41s : 0x%02x", "Transmitter technology",
	       id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]);

	switch (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]) {
	case QSFP_DD_850_VCSEL:
		printf(" (850 nm VCSEL)\n");
		break;
	case QSFP_DD_1310_VCSEL:
		printf(" (1310 nm VCSEL)\n");
		break;
	case QSFP_DD_1550_VCSEL:
		printf(" (1550 nm VCSEL)\n");
		break;
	case QSFP_DD_1310_FP:
		printf(" (1310 nm FP)\n");
		break;
	case QSFP_DD_1310_DFB:
		printf(" (1310 nm DFB)\n");
		break;
	case QSFP_DD_1550_DFB:
		printf(" (1550 nm DFB)\n");
		break;
	case QSFP_DD_1310_EML:
		printf(" (1310 nm EML)\n");
		break;
	case QSFP_DD_1550_EML:
		printf(" (1550 nm EML)\n");
		break;
	case QSFP_DD_OTHERS:
		printf(" (Others/Undefined)\n");
		break;
	case QSFP_DD_1490_DFB:
		printf(" (1490 nm DFB)\n");
		break;
	case QSFP_DD_COPPER_UNEQUAL:
		printf("%s unequalized)\n", cc);
		break;
	case QSFP_DD_COPPER_PASS_EQUAL:
		printf("%s passive equalized)\n", cc);
		break;
	case QSFP_DD_COPPER_NF_EQUAL:
		printf("%s near and far end limiting active equalizers)\n", cc);
		break;
	case QSFP_DD_COPPER_F_EQUAL:
		printf("%s far end limiting active equalizers)\n", cc);
		break;
	case QSFP_DD_COPPER_N_EQUAL:
		printf("%s near end limiting active equalizers)\n", cc);
		break;
	case QSFP_DD_COPPER_LINEAR_EQUAL:
		printf("%s linear active equalizers)\n", cc);
		break;
	}

	if (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET] >= QSFP_DD_COPPER_UNEQUAL) {
		printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
		       id[QSFP_DD_COPPER_ATT_5GHZ]);
		printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
		       id[QSFP_DD_COPPER_ATT_7GHZ]);
		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
		       id[QSFP_DD_COPPER_ATT_12P9GHZ]);
		printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
		       id[QSFP_DD_COPPER_ATT_25P8GHZ]);
	} else {
		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
		       (((id[QSFP_DD_NOM_WAVELENGTH_MSB] << 8) |
				id[QSFP_DD_NOM_WAVELENGTH_LSB]) * 0.05));
		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
		       (((id[QSFP_DD_WAVELENGTH_TOL_MSB] << 8) |
		       id[QSFP_DD_WAVELENGTH_TOL_LSB]) * 0.005));
	}
}

/*
 * 2-byte internal temperature conversions:
 * First byte is a signed 8-bit integer, which is the temp decimal part
 * Second byte is a multiple of 1/256th of a degree, which is added to
 * the dec part.
 */
#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))

/**
 * Print relevant module level monitoring values. Relevant documents:
 * [1] CMIS Rev. 3:
 * --> pag. 50, section 1.7.2.4, Table 22
 *
 * [2] CMIS Rev. 4:
 * --> pag. 84, section 8.2.4, Table 8-6
 */
static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id)
{
	PRINT_TEMP("Module temperature",
		   OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET));
	PRINT_VCC("Module voltage",
		  OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
}

/**
 * Print relevant info about the maximum supported fiber media length
 * for each type of fiber media at the maximum module-supported bit rate.
 * Relevant documents:
 * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
 * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
 */
static void qsfp_dd_show_link_len(const __u8 *id)
{
	qsfp_dd_print_smf_cbl_len(id);
	sff_show_value_with_unit(id, QSFP_DD_OM5_LEN_OFFSET,
				 "Length (OM5)", 2, "m");
	sff_show_value_with_unit(id, QSFP_DD_OM4_LEN_OFFSET,
				 "Length (OM4)", 2, "m");
	sff_show_value_with_unit(id, QSFP_DD_OM3_LEN_OFFSET,
				 "Length (OM3 50/125um)", 2, "m");
	sff_show_value_with_unit(id, QSFP_DD_OM2_LEN_OFFSET,
				 "Length (OM2 50/125um)", 1, "m");
}

/**
 * Show relevant information about the vendor. Relevant documents:
 * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
 * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
 */
static void qsfp_dd_show_vendor_info(const __u8 *id)
{
	sff_show_ascii(id, QSFP_DD_VENDOR_NAME_START_OFFSET,
		       QSFP_DD_VENDOR_NAME_END_OFFSET, "Vendor name");
	qsfp_dd_show_oui(id);
	sff_show_ascii(id, QSFP_DD_VENDOR_PN_START_OFFSET,
		       QSFP_DD_VENDOR_PN_END_OFFSET, "Vendor PN");
	sff_show_ascii(id, QSFP_DD_VENDOR_REV_START_OFFSET,
		       QSFP_DD_VENDOR_REV_END_OFFSET, "Vendor rev");
	sff_show_ascii(id, QSFP_DD_VENDOR_SN_START_OFFSET,
		       QSFP_DD_VENDOR_SN_END_OFFSET, "Vendor SN");
	sff_show_ascii(id, QSFP_DD_DATE_YEAR_OFFSET,
		       QSFP_DD_DATE_VENDOR_LOT_OFFSET + 1, "Date code");

	if (id[QSFP_DD_CLEI_PRESENT_BYTE] & QSFP_DD_CLEI_PRESENT_MASK)
		sff_show_ascii(id, QSFP_DD_CLEI_START_OFFSET,
			       QSFP_DD_CLEI_END_OFFSET, "CLEI code");
}

void qsfp_dd_show_all(const __u8 *id)
{
	qsfp_dd_show_identifier(id);
	qsfp_dd_show_power_info(id);
	qsfp_dd_show_connector(id);
	qsfp_dd_show_cbl_asm_len(id);
	qsfp_dd_show_sig_integrity(id);
	qsfp_dd_show_mit_compliance(id);
	qsfp_dd_show_mod_lvl_monitors(id);
	qsfp_dd_show_link_len(id);
	qsfp_dd_show_vendor_info(id);
	qsfp_dd_show_rev_compliance(id);
}