xref: /openbsd/sbin/ifconfig/sff.c (revision 4cfa599e)
1 /*	$OpenBSD: sff.c,v 1.23 2019/10/24 18:54:10 bluhm Exp $ */
2 
3 /*
4  * Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef SMALL
20 
21 #include <sys/ioctl.h>
22 
23 #include <net/if.h>
24 
25 #include <math.h>
26 #include <ctype.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <vis.h>
36 
37 #include "ifconfig.h"
38 
39 #ifndef nitems
40 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
41 #endif
42 
43 #ifndef ISSET
44 #define ISSET(_w, _m)	((_w) & (_m))
45 #endif
46 
47 #define SFF_THRESH_HI_ALARM	0
48 #define SFF_THRESH_LO_ALARM	1
49 #define SFF_THRESH_HI_WARN	2
50 #define SFF_THRESH_LO_WARN	3
51 #define SFF_THRESH_COUNT	4
52 
53 #define SFF_THRESH_REG(_i)	((_i) * 2)
54 
55 struct sff_thresholds {
56 	 float		thresholds[SFF_THRESH_COUNT];
57 };
58 
59 struct sff_media_map {
60 	float		factor_wavelength;
61 	int		scale_om1;
62 	int		scale_om2;
63 	int		scale_om3;
64 	uint8_t		connector_type;
65 	uint8_t		wavelength;
66 	uint8_t		dist_smf_m;
67 	uint8_t		dist_smf_km;
68 	uint8_t		dist_om1;
69 	uint8_t		dist_om2;
70 	uint8_t		dist_om3;
71 	uint8_t		dist_cu;
72 };
73 
74 #define SFF8024_ID_UNKNOWN	0x00
75 #define SFF8024_ID_GBIC		0x01
76 #define SFF8024_ID_MOBO		0x02 /* Module/connector soldered to mobo */
77 				     /* using SFF-8472 */
78 #define SFF8024_ID_SFP		0x03 /* SFP/SFP+/SFP28 */
79 #define SFF8024_ID_300PIN_XBI	0x04 /* 300 pin XBI */
80 #define SFF8024_ID_XENPAK	0x05
81 #define SFF8024_ID_XFP		0x06
82 #define SFF8024_ID_XFF		0x07
83 #define SFF8024_ID_XFPE		0x08 /* XFP-E */
84 #define SFF8024_ID_XPAK		0x09
85 #define SFF8024_ID_X2		0x0a
86 #define SFF8024_ID_DWDM_SFP	0x0b /* DWDM-SFP/SFP+ */
87 				     /* not using SFF-8472 */
88 #define SFF8024_ID_QSFP		0x0c
89 #define SFF8024_ID_QSFP_PLUS	0x0d /* or later */
90 				     /* using SFF-8436/8665/8685 et al */
91 #define SFF8024_ID_CXP		0x0e /* or later */
92 #define SFF8024_ID_HD4X		0x0f /* shielded mini multilane HD 4X */
93 #define SFF8024_ID_HD8X		0x10 /* shielded mini multilane HD 8X */
94 #define SFF8024_ID_QSFP28	0x11 /* or later */
95 				     /* using SFF-8665 et al */
96 #define SFF8024_ID_CXP2		0x12 /* aka CXP28, or later */
97 #define SFF8024_ID_CDFP		0x13 /* style 1/style 2 */
98 #define SFF8024_ID_HD4X_FAN	0x14 /* shielded mini multilane HD 4X fanout */
99 #define SFF8024_ID_HD8X_FAN	0x15 /* shielded mini multilane HD 8X fanout */
100 #define SFF8024_ID_CDFP3	0x16 /* style 3 */
101 #define SFF8024_ID_uQSFP	0x17 /* microQSFP */
102 #define SFF8024_ID_QSFP_DD	0x18 /* QSFP-DD double density 8x */
103 				     /* INF-8628 */
104 #define SFF8024_ID_RESERVED	0x7f /* up to here is reserved */
105 				     /* 0x80 to 0xff is vendor specific */
106 
107 #define SFF8024_ID_IS_RESERVED(_id)	((_id) <= SFF8024_ID_RESERVED)
108 #define SFF8024_ID_IS_VENDOR(_id)	((_id) > SFF8024_ID_RESERVED)
109 
110 #define SFF8024_CON_UNKNOWN	0x00
111 #define SFF8024_CON_SC		0x01 /* Subscriber Connector */
112 #define SFF8024_CON_FC_1	0x02 /* Fibre Channel Style 1 copper */
113 #define SFF8024_CON_FC_2	0x03 /* Fibre Channel Style 2 copper */
114 #define SFF8024_CON_BNC_TNC	0x04 /* BNC/TNC */
115 #define SFF8024_CON_FC_COAX	0x05 /* Fibre Channel coax headers */
116 #define SFF8024_CON_FJ		0x06 /* Fibre Jack */
117 #define SFF8024_CON_LC		0x07 /* Lucent Connector */
118 #define SFF8024_CON_MT_RJ	0x08 /* Mechanical Transfer - Registered Jack */
119 #define SFF8024_CON_MU		0x09 /* Multiple Optical */
120 #define SFF8024_CON_SG		0x0a
121 #define SFF8024_CON_O_PIGTAIL	0x0b /* Optical Pigtail */
122 #define SFF8024_CON_MPO_1x12	0x0c /* Multifiber Parallel Optic 1x12 */
123 #define SFF8024_CON_MPO_2x16	0x0e /* Multifiber Parallel Optic 2x16 */
124 #define SFF8024_CON_HSSDC2	0x20 /* High Speed Serial Data Connector */
125 #define SFF8024_CON_Cu_PIGTAIL	0x21 /* Copper Pigtail */
126 #define SFF8024_CON_RJ45	0x22
127 #define SFF8024_CON_NO		0x23 /* No separable connector */
128 #define SFF8024_CON_MXC_2x16	0x24
129 #define SFF8024_CON_RESERVED	0x7f /* up to here is reserved */
130 				     /* 0x80 to 0xff is vendor specific */
131 
132 #define SFF8024_CON_IS_RESERVED(_id)	((_id) <= SFF8024_CON_RESERVED)
133 #define SFF8024_CON_IS_VENDOR(_id)	((_id) > SFF8024_CON_RESERVED)
134 
135 static const char *sff8024_id_names[] = {
136 	[SFF8024_ID_UNKNOWN]	= "Unknown",
137 	[SFF8024_ID_GBIC]	= "GBIC",
138 	[SFF8024_ID_SFP]	= "SFP",
139 	[SFF8024_ID_300PIN_XBI]	= "XBI",
140 	[SFF8024_ID_XENPAK]	= "XENPAK",
141 	[SFF8024_ID_XFP]	= "XFP",
142 	[SFF8024_ID_XFF]	= "XFF",
143 	[SFF8024_ID_XFPE]	= "XFPE",
144 	[SFF8024_ID_XPAK]	= "XPAK",
145 	[SFF8024_ID_X2]		= "X2",
146 	[SFF8024_ID_DWDM_SFP]	= "DWDM-SFP",
147 	[SFF8024_ID_QSFP]	= "QSFP",
148 	[SFF8024_ID_QSFP_PLUS]	= "QSFP+",
149 	[SFF8024_ID_CXP]	= "CXP",
150 	[SFF8024_ID_HD4X]	= "HD 4X",
151 	[SFF8024_ID_HD8X]	= "HD 8X",
152 	[SFF8024_ID_QSFP28]	= "QSFP28",
153 	[SFF8024_ID_CXP2]	= "CXP2",
154 	[SFF8024_ID_CDFP]	= "CDFP Style 1/2",
155 	[SFF8024_ID_HD4X_FAN]	= "HD 4X Fanout",
156 	[SFF8024_ID_HD8X_FAN]	= "HD 8X Fanout",
157 	[SFF8024_ID_CDFP3]	= "CDFP Style 3",
158 	[SFF8024_ID_uQSFP]	= "microQSFP",
159 	[SFF8024_ID_QSFP_DD]	= "QSFP-DD",
160 };
161 
162 static const char *sff8024_con_names[] = {
163 	[SFF8024_CON_UNKNOWN]	= "Unknown",
164 	[SFF8024_CON_SC]	= "SC",
165 	[SFF8024_CON_FC_1]	= "FC Style 1",
166 	[SFF8024_CON_FC_2]	= "FC Style 2",
167 	[SFF8024_CON_BNC_TNC]	= "BNC/TNC",
168 	[SFF8024_CON_FC_COAX]	= "FC coax headers",
169 	[SFF8024_CON_FJ]	= "FJ",
170 	[SFF8024_CON_LC]	= "LC",
171 	[SFF8024_CON_MT_RJ]	= "MT-RJ",
172 	[SFF8024_CON_MU]	= "MU",
173 	[SFF8024_CON_SG]	= "SG",
174 	[SFF8024_CON_O_PIGTAIL]	= "AOC",
175 	[SFF8024_CON_MPO_1x12]	= "MPO 1x12",
176 	[SFF8024_CON_MPO_2x16]	= "MPO 2x16",
177 	[SFF8024_CON_HSSDC2]	= "HSSDC II",
178 	[SFF8024_CON_Cu_PIGTAIL]
179 				= "DAC",
180 	[SFF8024_CON_RJ45]	= "RJ45",
181 	[SFF8024_CON_NO]	= "No connector",
182 	[SFF8024_CON_MXC_2x16]	= "MXC 2x16",
183 };
184 
185 #define SFF8472_ID			0 /* SFF8027 for identifier values */
186 #define SFF8472_EXT_ID			1
187 #define SFF8472_EXT_ID_UNSPECIFIED		0x00
188 #define SFF8472_EXT_ID_MOD_DEF_1		0x01
189 #define SFF8472_EXT_ID_MOD_DEF_2		0x02
190 #define SFF8472_EXT_ID_MOD_DEF_3		0x03
191 #define SFF8472_EXT_ID_2WIRE			0x04
192 #define SFF8472_EXT_ID_MOD_DEF_5		0x05
193 #define SFF8472_EXT_ID_MOD_DEF_6		0x06
194 #define SFF8472_EXT_ID_MOD_DEF_7		0x07
195 #define SFF8472_CON			2 /* SFF8027 for connector values */
196 #define SFF8472_DIST_SMF_KM		14
197 #define SFF8472_DIST_SMF_M		15
198 #define SFF8472_DIST_OM2		16
199 #define SFF8472_DIST_OM1		17
200 #define SFF8472_DIST_CU			18
201 #define SFF8472_DIST_OM3		19
202 #define SFF8472_VENDOR_START		20
203 #define SFF8472_VENDOR_END		35
204 #define SFF8472_PRODUCT_START		40
205 #define SFF8472_PRODUCT_END		55
206 #define SFF8472_REVISION_START		56
207 #define SFF8472_REVISION_END		59
208 #define SFF8472_WAVELENGTH		60
209 #define SFF8472_SERIAL_START		68
210 #define SFF8472_SERIAL_END		83
211 #define SFF8472_DATECODE		84
212 #define SFF8472_DDM_TYPE		92
213 #define SFF8472_DDM_TYPE_AVG_POWER		(1U << 3)
214 #define SFF8472_DDM_TYPE_CAL_EXT		(1U << 4)
215 #define SFF8472_DDM_TYPE_CAL_INT		(1U << 5)
216 #define SFF8472_DDM_TYPE_IMPL			(1U << 6)
217 #define SFF8472_COMPLIANCE		94
218 #define SFF8472_COMPLIANCE_NONE			0x00
219 #define SFF8472_COMPLIANCE_9_3			0x01 /* SFF-8472 Rev 9.3 */
220 #define SFF8472_COMPLIANCE_9_5			0x02 /* SFF-8472 Rev 9.5 */
221 #define SFF8472_COMPLIANCE_10_2			0x03 /* SFF-8472 Rev 10.2 */
222 #define SFF8472_COMPLIANCE_10_4			0x04 /* SFF-8472 Rev 10.4 */
223 #define SFF8472_COMPLIANCE_11_0			0x05 /* SFF-8472 Rev 11.0 */
224 #define SFF8472_COMPLIANCE_11_3			0x06 /* SFF-8472 Rev 11.3 */
225 #define SFF8472_COMPLIANCE_11_4			0x07 /* SFF-8472 Rev 11.4 */
226 #define SFF8472_COMPLIANCE_12_3			0x08 /* SFF-8472 Rev 12.3 */
227 
228 static const struct sff_media_map sff8472_media_map = {
229 	.connector_type		= SFF8472_CON,
230 	.wavelength		= SFF8472_WAVELENGTH,
231 	.factor_wavelength	= 1.0,
232 	.dist_smf_m		= SFF8472_DIST_SMF_M,
233 	.dist_smf_km		= SFF8472_DIST_SMF_KM,
234 	.dist_om1		= SFF8472_DIST_OM1,
235 	.scale_om1		= 10,
236 	.dist_om2		= SFF8472_DIST_OM2,
237 	.scale_om2		= 10,
238 	.dist_om3		= SFF8472_DIST_OM3,
239 	.scale_om3		= 20,
240 	.dist_cu		= SFF8472_DIST_CU,
241 };
242 
243 /*
244  * page 0xa2
245  */
246 #define SFF8472_AW_TEMP			0
247 #define SFF8472_AW_VCC			8
248 #define SFF8472_AW_TX_BIAS		16
249 #define SFF8472_AW_TX_POWER		24
250 #define SFF8472_AW_RX_POWER		32
251 #define ALRM_HIGH		0
252 #define ALRM_LOW		2
253 #define WARN_HIGH		4
254 #define WARN_LOW		6
255 #define SFF8472_DDM_TEMP		96
256 #define SFF8472_DDM_VCC			98
257 #define SFF8472_DDM_TX_BIAS		100
258 #define SFF8472_DDM_TX_POWER		102
259 #define SFF8472_DDM_RX_POWER		104
260 #define SFF8472_DDM_LASER		106 /* laser temp/wavelength */
261 					    /* optional */
262 #define SFF8472_DDM_TEC			108 /* Measured TEC current */
263 					    /* optional */
264 
265 #define SFF_TEMP_FACTOR		256.0
266 #define SFF_VCC_FACTOR		10000.0
267 #define SFF_BIAS_FACTOR		500.0
268 #define SFF_POWER_FACTOR	10000.0
269 
270 /*
271  * QSFP is defined by SFF-8436, but the management interface is
272  * updated and maintained by SFF-8636.
273  */
274 
275 #define SFF8436_STATUS1		1
276 #define SFF8436_STATUS2		2
277 #define SFF8436_STATUS2_DNR		(1 << 0) /* Data_Not_Ready */
278 #define SFF8436_STATUS2_INTL		(1 << 1) /* Interrupt output state */
279 #define SFF8436_STATUS2_FLAT_MEM	(1 << 2) /* Upper memory flat/paged */
280 
281 #define SFF8436_TEMP		22
282 #define SFF8436_VCC		26
283 
284 #define SFF8436_CHANNELS	4	/* number of TX and RX channels */
285 #define SFF8436_RX_POWER_BASE	34
286 #define SFF8436_RX_POWER(_i)	(SFF8436_RX_POWER_BASE + ((_i) * 2))
287 #define SFF8436_TX_BIAS_BASE	42
288 #define SFF8436_TX_BIAS(_i)	(SFF8436_TX_BIAS_BASE + ((_i) * 2))
289 #define SFF8436_TX_POWER_BASE	50
290 #define SFF8436_TX_POWER(_i)	(SFF8436_TX_POWER_BASE + ((_i) * 2))
291 
292 /* Upper Page 00h */
293 
294 #define SFF8436_MAXCASETEMP	190	/* C */
295 #define SFF8436_MAXCASETEMP_DEFAULT	70 /* if SFF8436_MAXCASETEMP is 0 */
296 
297 /* Upper page 03h */
298 
299 #define SFF8436_AW_TEMP		128
300 #define SFF8436_AW_VCC		144
301 #define SFF8436_AW_RX_POWER	176
302 #define SFF8436_AW_TX_BIAS	184
303 #define SFF8436_AW_TX_POWER	192
304 
305 /*
306  * XFP stuff is defined by INF-8077.
307  *
308  * The "Serial ID Memory Map" on page 1 contains the interesting strings
309  */
310 
311 /* SFF-8636 and INF-8077 share a layout for various strings */
312 
313 #define UPPER_CON			130 /* connector type */
314 #define UPPER_DIST_SMF			142
315 #define UPPER_DIST_OM3			143
316 #define UPPER_DIST_OM2			144
317 #define UPPER_DIST_OM1			145
318 #define UPPER_DIST_CU			146
319 #define UPPER_WAVELENGTH		186
320 
321 #define UPPER_VENDOR_START		148
322 #define UPPER_VENDOR_END		163
323 #define UPPER_PRODUCT_START		168
324 #define UPPER_PRODUCT_END		183
325 #define UPPER_REVISION_START		184
326 #define UPPER_REVISION_END		185
327 #define UPPER_SERIAL_START		196
328 #define UPPER_SERIAL_END		211
329 #define UPPER_DATECODE			212
330 #define UPPER_LOT_START			218
331 #define UPPER_LOT_END			219
332 
333 static const struct sff_media_map upper_media_map = {
334 	.connector_type		= UPPER_CON,
335 	.wavelength		= UPPER_WAVELENGTH,
336 	.factor_wavelength	= 20.0,
337 	.dist_smf_m		= 0,
338 	.dist_smf_km		= UPPER_DIST_SMF,
339 	.dist_om1		= UPPER_DIST_OM1,
340 	.scale_om1		= 1,
341 	.dist_om2		= UPPER_DIST_OM1,
342 	.scale_om2		= 1,
343 	.dist_om3		= UPPER_DIST_OM3,
344 	.scale_om3		= 2,
345 	.dist_cu		= UPPER_DIST_CU,
346 };
347 
348 static void	hexdump(const void *, size_t);
349 static int	if_sff8472(int, const struct if_sffpage *);
350 static int	if_sff8636(int, const struct if_sffpage *);
351 static int	if_inf8077(int, const struct if_sffpage *);
352 
353 static const char *
sff_id_name(uint8_t id)354 sff_id_name(uint8_t id)
355 {
356 	const char *name = NULL;
357 
358 	if (id < nitems(sff8024_id_names)) {
359 		name = sff8024_id_names[id];
360 		if (name != NULL)
361 			return (name);
362 	}
363 
364 	if (SFF8024_ID_IS_VENDOR(id))
365 		return ("Vendor Specific");
366 
367 	return ("Reserved");
368 }
369 
370 static const char *
sff_con_name(uint8_t id)371 sff_con_name(uint8_t id)
372 {
373 	const char *name = NULL;
374 
375 	if (id < nitems(sff8024_con_names)) {
376 		name = sff8024_con_names[id];
377 		if (name != NULL)
378 			return (name);
379 	}
380 
381 	if (SFF8024_CON_IS_VENDOR(id))
382 		return ("Vendor Specific");
383 
384 	return ("Reserved");
385 }
386 
387 static void
if_sffpage_init(struct if_sffpage * sff,uint8_t addr,uint8_t page)388 if_sffpage_init(struct if_sffpage *sff, uint8_t addr, uint8_t page)
389 {
390 	memset(sff, 0, sizeof(*sff));
391 
392 	if (strlcpy(sff->sff_ifname, ifname, sizeof(sff->sff_ifname)) >=
393 	    sizeof(sff->sff_ifname))
394 		errx(1, "interface name too long");
395 
396 	sff->sff_addr = addr;
397 	sff->sff_page = page;
398 }
399 
400 static void
if_sffpage_dump(const struct if_sffpage * sff)401 if_sffpage_dump(const struct if_sffpage *sff)
402 {
403 	printf("%s: addr %02x", ifname, sff->sff_addr);
404 	if (sff->sff_addr == IFSFF_ADDR_EEPROM)
405 		printf(" page %u", sff->sff_page);
406 	putchar('\n');
407 	hexdump(sff->sff_data, sizeof(sff->sff_data));
408 }
409 
410 int
if_sff_info(int dump)411 if_sff_info(int dump)
412 {
413 	struct if_sffpage pg0;
414 	int error = 0;
415 	uint8_t id, ext_id;
416 
417 	if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 0);
418 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) {
419 		if (errno == ENXIO) {
420 			/* try 1 for XFP cos myx which can't switch pages... */
421 			if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
422 			if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
423 				return (-1);
424 		} else
425 			return (-1);
426 	}
427 
428 	if (dump)
429 		if_sffpage_dump(&pg0);
430 
431 	id = pg0.sff_data[0]; /* SFF8472_ID */
432 
433 	printf("\ttransceiver: %s ", sff_id_name(id));
434 	switch (id) {
435 	case SFF8024_ID_SFP:
436 		ext_id = pg0.sff_data[SFF8472_EXT_ID];
437 		if (ext_id != SFF8472_EXT_ID_2WIRE) {
438 			printf("extended-id %02xh\n", ext_id);
439 			break;
440 		}
441 		/* FALLTHROUGH */
442 	case SFF8024_ID_GBIC:
443 		error = if_sff8472(dump, &pg0);
444 		break;
445 	case SFF8024_ID_XFP:
446 		if (pg0.sff_page != 1) {
447 			if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
448 			if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
449 				return (-1);
450 			if (dump)
451 				if_sffpage_dump(&pg0);
452 		}
453 		error = if_inf8077(dump, &pg0);
454 		break;
455 	case SFF8024_ID_QSFP:
456 	case SFF8024_ID_QSFP_PLUS:
457 	case SFF8024_ID_QSFP28:
458 		error = if_sff8636(dump, &pg0);
459 		break;
460 	default:
461 		printf("\n");
462 		break;
463 	}
464 
465 	return (error);
466 }
467 
468 static void
if_sff_ascii_print(const struct if_sffpage * sff,const char * name,size_t start,size_t end,const char * trailer)469 if_sff_ascii_print(const struct if_sffpage *sff, const char *name,
470     size_t start, size_t end, const char *trailer)
471 {
472 	const uint8_t *d = sff->sff_data;
473 	int ch;
474 
475 	for (;;) {
476 		ch = d[start];
477 		if (!isspace(ch) && ch != '\0')
478 			break;
479 
480 		start++;
481 		if (start == end)
482 			return;
483 	}
484 
485 	printf("%s", name);
486 
487 	for (;;) {
488 		ch = d[end];
489 		if (!isspace(ch) && ch != '\0')
490 			break;
491 
492 		end--;
493 	}
494 
495 	do {
496 		char dst[8];
497 		vis(dst, d[start], VIS_TAB | VIS_NL, 0);
498 		printf("%s", dst);
499 	} while (++start <= end);
500 
501 	printf("%s", trailer);
502 }
503 
504 static void
if_sff_date_print(const struct if_sffpage * sff,const char * name,size_t start,const char * trailer)505 if_sff_date_print(const struct if_sffpage *sff, const char *name,
506     size_t start, const char *trailer)
507 {
508 	const uint8_t *d = sff->sff_data + start;
509 	size_t i;
510 
511 	/* YYMMDD */
512 	for (i = 0; i < 6; i++) {
513 		if (!isdigit(d[i])) {
514 			if_sff_ascii_print(sff, name, start,
515 			    start + 5, trailer);
516 			return;
517 		}
518 	}
519 
520 	printf("%s20%c%c-%c%c-%c%c%s", name,
521 	    d[0], d[1], d[2], d[3], d[4], d[5], trailer);
522 }
523 
524 static int16_t
if_sff_int(const struct if_sffpage * sff,size_t start)525 if_sff_int(const struct if_sffpage *sff, size_t start)
526 {
527 	const uint8_t *d = sff->sff_data + start;
528 
529 	return (d[0] << 8 | d[1]);
530 }
531 
532 static uint16_t
if_sff_uint(const struct if_sffpage * sff,size_t start)533 if_sff_uint(const struct if_sffpage *sff, size_t start)
534 {
535 	const uint8_t *d = sff->sff_data + start;
536 
537 	return (d[0] << 8 | d[1]);
538 }
539 
540 static float
if_sff_power2dbm(const struct if_sffpage * sff,size_t start)541 if_sff_power2dbm(const struct if_sffpage *sff, size_t start)
542 {
543 	const uint8_t *d = sff->sff_data + start;
544 
545 	int power = d[0] << 8 | d[1];
546 	return (10.0 * log10f((float)power / 10000.0));
547 }
548 
549 static void
if_sff_printalarm(const char * unit,int range,float actual,float alrm_high,float alrm_low,float warn_high,float warn_log)550 if_sff_printalarm(const char *unit, int range, float actual,
551     float alrm_high, float alrm_low, float warn_high, float warn_log)
552 {
553 	printf("%.02f%s", actual, unit);
554 	if (range == 1)
555 		printf(" (low %.02f%s, high %.02f%s)", alrm_low,
556 		    unit, alrm_high, unit);
557 
558 	if(actual > alrm_high || actual < alrm_low)
559 		printf(" [ALARM]");
560 	else if(actual > warn_high || actual < warn_log)
561 		printf(" [WARNING]");
562 }
563 
564 static void
if_sff_printdist(const char * type,int value,int scale)565 if_sff_printdist(const char *type, int value, int scale)
566 {
567 	int distance = value * scale;
568 
569 	if (value == 0)
570 		return;
571 
572 	if (distance < 10000)
573 		printf (", %s%u%s", value > 254 ? ">" : "", distance, type);
574 	else
575 		printf (", %s%0.1fk%s", value > 254 ? ">" : "",
576 		    distance / 1000.0, type);
577 }
578 
579 static void
if_sff_printmedia(const struct if_sffpage * pg,const struct sff_media_map * m)580 if_sff_printmedia(const struct if_sffpage *pg, const struct sff_media_map *m)
581 {
582 	uint8_t con;
583 	unsigned int wavelength;
584 
585 	con = pg->sff_data[m->connector_type];
586 	printf("%s", sff_con_name(con));
587 
588 	wavelength = if_sff_uint(pg, m->wavelength);
589 	switch (wavelength) {
590 	case 0x0000:
591 		/* not known or is unavailable */
592 		break;
593 	/* Copper Cable */
594 	case 0x0100: /* SFF-8431 Appendix E */
595 	case 0x0400: /* SFF-8431 limiting */
596 	case 0x0c00: /* SFF-8431 limiting and FC-PI-4 limiting */
597 		break;
598 	default:
599 		printf(", %.f nm", wavelength / m->factor_wavelength);
600 	}
601 
602 	if (m->dist_smf_m != 0 &&
603 	    pg->sff_data[m->dist_smf_m] > 0 &&
604 	    pg->sff_data[m->dist_smf_m] < 255)
605 		if_sff_printdist("m SMF", pg->sff_data[m->dist_smf_m], 100);
606 	else
607 		if_sff_printdist("km SMF", pg->sff_data[m->dist_smf_km], 1);
608 	if_sff_printdist("m OM1", pg->sff_data[m->dist_om1], m->scale_om1);
609 	if_sff_printdist("m OM2", pg->sff_data[m->dist_om2], m->scale_om2);
610 	if_sff_printdist("m OM3", pg->sff_data[m->dist_om3], m->scale_om3);
611 	if_sff_printdist("m", pg->sff_data[m->dist_cu], 1);
612 }
613 
614 static int
if_sff8472(int dump,const struct if_sffpage * pg0)615 if_sff8472(int dump, const struct if_sffpage *pg0)
616 {
617 	struct if_sffpage ddm;
618 	uint8_t ddm_types;
619 
620 	if_sff_printmedia(pg0, &sff8472_media_map);
621 
622 	printf("\n\tmodel: ");
623 	if_sff_ascii_print(pg0, "",
624 	    SFF8472_VENDOR_START, SFF8472_VENDOR_END, " ");
625 	if_sff_ascii_print(pg0, "",
626 	    SFF8472_PRODUCT_START, SFF8472_PRODUCT_END, "");
627 	if_sff_ascii_print(pg0, " rev ",
628 	    SFF8472_REVISION_START, SFF8472_REVISION_END, "");
629 
630 	if_sff_ascii_print(pg0, "\n\tserial: ",
631 	    SFF8472_SERIAL_START, SFF8472_SERIAL_END, ", ");
632 	if_sff_date_print(pg0, "date: ", SFF8472_DATECODE, "\n");
633 
634 	ddm_types = pg0->sff_data[SFF8472_DDM_TYPE];
635 	if (pg0->sff_data[SFF8472_COMPLIANCE] == SFF8472_COMPLIANCE_NONE ||
636 	    !ISSET(ddm_types, SFF8472_DDM_TYPE_IMPL))
637 		return (0);
638 
639 	if_sffpage_init(&ddm, IFSFF_ADDR_DDM, 0);
640 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&ddm) == -1)
641 		return (-1);
642 
643 	if (dump)
644 		if_sffpage_dump(&ddm);
645 
646 	if (ISSET(ddm_types, SFF8472_DDM_TYPE_CAL_EXT)) {
647 		printf("\tcalibration: external "
648 		    "(WARNING: needs more code)\n");
649 	}
650 
651 	printf("\tvoltage: ");
652 	if_sff_printalarm(" V", 0,
653 	    if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR,
654 	    if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_HIGH) / SFF_VCC_FACTOR,
655 	    if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_LOW) / SFF_VCC_FACTOR,
656 	    if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_HIGH) / SFF_VCC_FACTOR,
657 	    if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_LOW) / SFF_VCC_FACTOR);
658 
659 	printf(", bias current: ");
660 	if_sff_printalarm(" mA", 0,
661 	    if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR,
662 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_HIGH) / SFF_BIAS_FACTOR,
663 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_LOW) / SFF_BIAS_FACTOR,
664 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_HIGH) / SFF_BIAS_FACTOR,
665 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_LOW) / SFF_BIAS_FACTOR);
666 
667 	printf("\n\ttemp: ");
668 	if_sff_printalarm(" C", 1,
669 	    if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR,
670 	    if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_HIGH) / SFF_TEMP_FACTOR,
671 	    if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_LOW) / SFF_TEMP_FACTOR,
672 	    if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_HIGH) / SFF_TEMP_FACTOR,
673 	    if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_LOW) / SFF_TEMP_FACTOR);
674 
675 	printf("\n\ttx: ");
676 	if_sff_printalarm(" dBm", 1,
677 	    if_sff_power2dbm(&ddm, SFF8472_DDM_TX_POWER),
678 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_HIGH),
679 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_LOW),
680 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_HIGH),
681 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_LOW));
682 
683 	printf("\n\trx: ");
684 	if_sff_printalarm(" dBm", 1,
685 	    if_sff_power2dbm(&ddm, SFF8472_DDM_RX_POWER),
686 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_HIGH),
687 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_LOW),
688 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_HIGH),
689 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_LOW));
690 
691 	putchar('\n');
692 	return (0);
693 }
694 
695 static void
if_upper_strings(const struct if_sffpage * pg)696 if_upper_strings(const struct if_sffpage *pg)
697 {
698 	if_sff_printmedia(pg, &upper_media_map);
699 
700 	printf("\n\tmodel: ");
701 	if_sff_ascii_print(pg, "",
702 	    UPPER_VENDOR_START, UPPER_VENDOR_END, " ");
703 	if_sff_ascii_print(pg, "",
704 	    UPPER_PRODUCT_START, UPPER_PRODUCT_END, "");
705 	if_sff_ascii_print(pg, " rev ",
706 	    UPPER_REVISION_START, UPPER_REVISION_END, "");
707 
708 	if_sff_ascii_print(pg, "\n\tserial: ",
709 	    UPPER_SERIAL_START, UPPER_SERIAL_END, " ");
710 	if_sff_date_print(pg, "date: ", UPPER_DATECODE, " ");
711 	if_sff_ascii_print(pg, "lot: ",
712 	    UPPER_LOT_START, UPPER_LOT_END, "");
713 
714 	putchar('\n');
715 }
716 
717 static int
if_inf8077(int dump,const struct if_sffpage * pg1)718 if_inf8077(int dump, const struct if_sffpage *pg1)
719 {
720 	if_upper_strings(pg1);
721 
722 	return (0);
723 }
724 
725 static int
if_sff8636_thresh(int dump,const struct if_sffpage * pg0)726 if_sff8636_thresh(int dump, const struct if_sffpage *pg0)
727 {
728 	struct if_sffpage pg3;
729 	unsigned int i;
730 	struct sff_thresholds temp, vcc, tx, rx, bias;
731 
732 	if_sffpage_init(&pg3, IFSFF_ADDR_EEPROM, 3);
733 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg3) == -1) {
734 		if (dump)
735 			warn("%s SIOCGIFSFFPAGE page 3", ifname);
736 		return (-1);
737 	}
738 
739 	if (dump)
740 		if_sffpage_dump(&pg3);
741 
742 	if (pg3.sff_data[0x7f] != 3) { /* just in case... */
743 		if (dump) {
744 			warnx("%s SIOCGIFSFFPAGE: page select unsupported",
745 			    ifname);
746 		}
747 		return (-1);
748 	}
749 
750 	for (i = 0; i < SFF_THRESH_COUNT; i++) {
751 		temp.thresholds[i] = if_sff_int(&pg3,
752 		    SFF8436_AW_TEMP + SFF_THRESH_REG(i)) / SFF_TEMP_FACTOR;
753 
754 		vcc.thresholds[i] = if_sff_uint(&pg3,
755 		    SFF8436_AW_VCC + SFF_THRESH_REG(i)) / SFF_VCC_FACTOR;
756 
757 		rx.thresholds[i] = if_sff_power2dbm(&pg3,
758 		    SFF8436_AW_RX_POWER + SFF_THRESH_REG(i));
759 
760 		bias.thresholds[i] = if_sff_uint(&pg3,
761 		    SFF8436_AW_TX_BIAS + SFF_THRESH_REG(i)) / SFF_BIAS_FACTOR;
762 
763 		tx.thresholds[i] = if_sff_power2dbm(&pg3,
764 		    SFF8436_AW_TX_POWER + SFF_THRESH_REG(i));
765 	}
766 
767 	printf("\ttemp: ");
768 	if_sff_printalarm(" C", 1,
769 	    if_sff_int(&pg3, SFF8436_TEMP) / SFF_TEMP_FACTOR,
770 	    temp.thresholds[SFF_THRESH_HI_ALARM],
771 	    temp.thresholds[SFF_THRESH_LO_ALARM],
772 	    temp.thresholds[SFF_THRESH_HI_WARN],
773 	    temp.thresholds[SFF_THRESH_LO_WARN]);
774 	printf("\n");
775 
776 	printf("\tvoltage: ");
777 	if_sff_printalarm(" V", 1,
778 	    if_sff_uint(&pg3, SFF8436_VCC) / SFF_VCC_FACTOR,
779 	    vcc.thresholds[SFF_THRESH_HI_ALARM],
780 	    vcc.thresholds[SFF_THRESH_LO_ALARM],
781 	    vcc.thresholds[SFF_THRESH_HI_WARN],
782 	    vcc.thresholds[SFF_THRESH_LO_WARN]);
783 	printf("\n");
784 
785 	for (i = 0; i < SFF8436_CHANNELS; i++) {
786 		unsigned int channel = i + 1;
787 
788 		printf("\tchannel %u bias current: ", channel);
789 		if_sff_printalarm(" mA", 1,
790 		    if_sff_uint(&pg3, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR,
791 		    bias.thresholds[SFF_THRESH_HI_ALARM],
792 		    bias.thresholds[SFF_THRESH_LO_ALARM],
793 		    bias.thresholds[SFF_THRESH_HI_WARN],
794 		    bias.thresholds[SFF_THRESH_LO_WARN]);
795 		printf("\n");
796 
797 		printf("\tchannel %u tx: ", channel);
798 		if_sff_printalarm(" dBm", 1,
799 		    if_sff_power2dbm(&pg3, SFF8436_TX_POWER(i)),
800 		    tx.thresholds[SFF_THRESH_HI_ALARM],
801 		    tx.thresholds[SFF_THRESH_LO_ALARM],
802 		    tx.thresholds[SFF_THRESH_HI_WARN],
803 		    tx.thresholds[SFF_THRESH_LO_WARN]);
804 		printf("\n");
805 
806 		printf("\tchannel %u rx: ", channel);
807 		if_sff_printalarm(" dBm", 1,
808 		    if_sff_power2dbm(&pg3, SFF8436_RX_POWER(i)),
809 		    rx.thresholds[SFF_THRESH_HI_ALARM],
810 		    rx.thresholds[SFF_THRESH_LO_ALARM],
811 		    rx.thresholds[SFF_THRESH_HI_WARN],
812 		    rx.thresholds[SFF_THRESH_LO_WARN]);
813 		printf("\n");
814 	}
815 
816 	return (0);
817 }
818 
819 static int
if_sff8636(int dump,const struct if_sffpage * pg0)820 if_sff8636(int dump, const struct if_sffpage *pg0)
821 {
822 	int16_t temp;
823 	uint8_t maxcasetemp;
824 	uint8_t flat;
825 	unsigned int i;
826 
827 	if_upper_strings(pg0);
828 
829 	if (pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_DNR) {
830 		printf("\tmonitor data not ready\n");
831 		return (0);
832 	}
833 
834 	maxcasetemp = pg0->sff_data[SFF8436_MAXCASETEMP];
835 	if (maxcasetemp == 0x00)
836 		maxcasetemp = SFF8436_MAXCASETEMP_DEFAULT;
837 	printf("\tmax case temp: %u C\n", maxcasetemp);
838 
839 	temp = if_sff_int(pg0, SFF8436_TEMP);
840 	/* the temp reading look unset, assume the rest will be unset too */
841 	if ((uint16_t)temp == 0 || (uint16_t)temp == 0xffffU) {
842 		if (!dump)
843 			return (0);
844 	}
845 
846 	flat = pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_FLAT_MEM;
847 	if (!flat && if_sff8636_thresh(dump, pg0) == 0) {
848 		if (!dump)
849 			return (0);
850 	}
851 
852 	printf("\t");
853 	printf("temp: %.02f%s", temp / SFF_TEMP_FACTOR, " C");
854 	printf(", ");
855 	printf("voltage: %.02f%s",
856 	    if_sff_uint(pg0, SFF8436_VCC) / SFF_VCC_FACTOR, " V");
857 	printf("\n");
858 
859 	for (i = 0; i < SFF8436_CHANNELS; i++) {
860 		printf("\t");
861 		printf("channel %u: ", i + 1);
862 		printf("bias current: %.02f mA",
863 		    if_sff_uint(pg0, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR);
864 		printf(", ");
865 		printf("rx: %.02f dBm",
866 		    if_sff_power2dbm(pg0, SFF8436_RX_POWER(i)));
867 		printf(", ");
868 		printf("tx: %.02f dBm",
869 		    if_sff_power2dbm(pg0, SFF8436_TX_POWER(i)));
870 		printf("\n");
871 	}
872 
873 	return (0);
874 }
875 
876 static int
printable(int ch)877 printable(int ch)
878 {
879 	if (ch == '\0')
880 		return ('_');
881 	if (!isprint(ch))
882 		return ('~');
883 
884 	return (ch);
885 }
886 
887 static void
hexdump(const void * d,size_t datalen)888 hexdump(const void *d, size_t datalen)
889 {
890 	const uint8_t *data = d;
891 	int i, j = 0;
892 
893 	for (i = 0; i < datalen; i += j) {
894 		printf("% 4d: ", i);
895 		for (j = 0; j < 16 && i+j < datalen; j++)
896 			printf("%02x ", data[i + j]);
897 		while (j++ < 16)
898 			printf("   ");
899 		printf("|");
900 		for (j = 0; j < 16 && i+j < datalen; j++)
901 			putchar(printable(data[i + j]));
902 		printf("|\n");
903 	}
904 }
905 
906 #endif /* SMALL */
907