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
55struct sff_thresholds {
56	 float		thresholds[SFF_THRESH_COUNT];
57};
58
59struct 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
135static 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
162static 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
228static 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
333static 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
348static void	hexdump(const void *, size_t);
349static int	if_sff8472(int, const struct if_sffpage *);
350static int	if_sff8636(int, const struct if_sffpage *);
351static int	if_inf8077(int, const struct if_sffpage *);
352
353static const char *
354sff_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
370static const char *
371sff_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
387static void
388if_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
400static void
401if_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
410int
411if_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
468static void
469if_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
504static void
505if_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
524static int16_t
525if_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
532static uint16_t
533if_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
540static float
541if_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
549static void
550if_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
564static void
565if_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
579static void
580if_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
614static int
615if_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
695static void
696if_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
717static int
718if_inf8077(int dump, const struct if_sffpage *pg1)
719{
720	if_upper_strings(pg1);
721
722	return (0);
723}
724
725static int
726if_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
819static int
820if_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
876static int
877printable(int ch)
878{
879	if (ch == '\0')
880		return ('_');
881	if (!isprint(ch))
882		return ('~');
883
884	return (ch);
885}
886
887static void
888hexdump(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