xref: /openbsd/sbin/ifconfig/sff.c (revision 4cfa599e)
1*4cfa599eSbluhm /*	$OpenBSD: sff.c,v 1.23 2019/10/24 18:54:10 bluhm Exp $ */
24ce43bedSdlg 
34ce43bedSdlg /*
44ce43bedSdlg  * Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
54ce43bedSdlg  *
64ce43bedSdlg  * Permission to use, copy, modify, and distribute this software for any
74ce43bedSdlg  * purpose with or without fee is hereby granted, provided that the above
84ce43bedSdlg  * copyright notice and this permission notice appear in all copies.
94ce43bedSdlg  *
104ce43bedSdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
114ce43bedSdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
124ce43bedSdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
134ce43bedSdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144ce43bedSdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154ce43bedSdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164ce43bedSdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174ce43bedSdlg  */
184ce43bedSdlg 
194ce43bedSdlg #ifndef SMALL
204ce43bedSdlg 
214ce43bedSdlg #include <sys/ioctl.h>
224ce43bedSdlg 
234ce43bedSdlg #include <net/if.h>
244ce43bedSdlg 
254ce43bedSdlg #include <math.h>
264ce43bedSdlg #include <ctype.h>
274ce43bedSdlg #include <err.h>
284ce43bedSdlg #include <errno.h>
294ce43bedSdlg #include <stdio.h>
304ce43bedSdlg #include <stdint.h>
314ce43bedSdlg #include <stdlib.h>
324ce43bedSdlg #include <string.h>
334ce43bedSdlg #include <unistd.h>
344ce43bedSdlg #include <limits.h>
354ce43bedSdlg #include <vis.h>
364ce43bedSdlg 
37d6a6566dSbluhm #include "ifconfig.h"
38d6a6566dSbluhm 
394ce43bedSdlg #ifndef nitems
404ce43bedSdlg #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
414ce43bedSdlg #endif
424ce43bedSdlg 
434ce43bedSdlg #ifndef ISSET
444ce43bedSdlg #define ISSET(_w, _m)	((_w) & (_m))
454ce43bedSdlg #endif
464ce43bedSdlg 
47b46ace34Sdlg #define SFF_THRESH_HI_ALARM	0
48b46ace34Sdlg #define SFF_THRESH_LO_ALARM	1
49b46ace34Sdlg #define SFF_THRESH_HI_WARN	2
50b46ace34Sdlg #define SFF_THRESH_LO_WARN	3
51b46ace34Sdlg #define SFF_THRESH_COUNT	4
52b46ace34Sdlg 
53b46ace34Sdlg #define SFF_THRESH_REG(_i)	((_i) * 2)
54b46ace34Sdlg 
55b46ace34Sdlg struct sff_thresholds {
56b46ace34Sdlg 	 float		thresholds[SFF_THRESH_COUNT];
57b46ace34Sdlg };
58b46ace34Sdlg 
594662fdbeSdlg struct sff_media_map {
604662fdbeSdlg 	float		factor_wavelength;
614662fdbeSdlg 	int		scale_om1;
624662fdbeSdlg 	int		scale_om2;
634662fdbeSdlg 	int		scale_om3;
644662fdbeSdlg 	uint8_t		connector_type;
654662fdbeSdlg 	uint8_t		wavelength;
664662fdbeSdlg 	uint8_t		dist_smf_m;
674662fdbeSdlg 	uint8_t		dist_smf_km;
684662fdbeSdlg 	uint8_t		dist_om1;
694662fdbeSdlg 	uint8_t		dist_om2;
704662fdbeSdlg 	uint8_t		dist_om3;
714662fdbeSdlg 	uint8_t		dist_cu;
724662fdbeSdlg };
734662fdbeSdlg 
744ce43bedSdlg #define SFF8024_ID_UNKNOWN	0x00
754ce43bedSdlg #define SFF8024_ID_GBIC		0x01
764ce43bedSdlg #define SFF8024_ID_MOBO		0x02 /* Module/connector soldered to mobo */
774ce43bedSdlg 				     /* using SFF-8472 */
784ce43bedSdlg #define SFF8024_ID_SFP		0x03 /* SFP/SFP+/SFP28 */
794ce43bedSdlg #define SFF8024_ID_300PIN_XBI	0x04 /* 300 pin XBI */
804ce43bedSdlg #define SFF8024_ID_XENPAK	0x05
814ce43bedSdlg #define SFF8024_ID_XFP		0x06
824ce43bedSdlg #define SFF8024_ID_XFF		0x07
834ce43bedSdlg #define SFF8024_ID_XFPE		0x08 /* XFP-E */
844ce43bedSdlg #define SFF8024_ID_XPAK		0x09
854ce43bedSdlg #define SFF8024_ID_X2		0x0a
864ce43bedSdlg #define SFF8024_ID_DWDM_SFP	0x0b /* DWDM-SFP/SFP+ */
874ce43bedSdlg 				     /* not using SFF-8472 */
884ce43bedSdlg #define SFF8024_ID_QSFP		0x0c
894ce43bedSdlg #define SFF8024_ID_QSFP_PLUS	0x0d /* or later */
904ce43bedSdlg 				     /* using SFF-8436/8665/8685 et al */
914ce43bedSdlg #define SFF8024_ID_CXP		0x0e /* or later */
924ce43bedSdlg #define SFF8024_ID_HD4X		0x0f /* shielded mini multilane HD 4X */
934ce43bedSdlg #define SFF8024_ID_HD8X		0x10 /* shielded mini multilane HD 8X */
944ce43bedSdlg #define SFF8024_ID_QSFP28	0x11 /* or later */
954ce43bedSdlg 				     /* using SFF-8665 et al */
964ce43bedSdlg #define SFF8024_ID_CXP2		0x12 /* aka CXP28, or later */
974ce43bedSdlg #define SFF8024_ID_CDFP		0x13 /* style 1/style 2 */
984ce43bedSdlg #define SFF8024_ID_HD4X_FAN	0x14 /* shielded mini multilane HD 4X fanout */
994ce43bedSdlg #define SFF8024_ID_HD8X_FAN	0x15 /* shielded mini multilane HD 8X fanout */
1004ce43bedSdlg #define SFF8024_ID_CDFP3	0x16 /* style 3 */
1014ce43bedSdlg #define SFF8024_ID_uQSFP	0x17 /* microQSFP */
1024ce43bedSdlg #define SFF8024_ID_QSFP_DD	0x18 /* QSFP-DD double density 8x */
1034ce43bedSdlg 				     /* INF-8628 */
1044ce43bedSdlg #define SFF8024_ID_RESERVED	0x7f /* up to here is reserved */
1054ce43bedSdlg 				     /* 0x80 to 0xff is vendor specific */
1064ce43bedSdlg 
1074ce43bedSdlg #define SFF8024_ID_IS_RESERVED(_id)	((_id) <= SFF8024_ID_RESERVED)
1084ce43bedSdlg #define SFF8024_ID_IS_VENDOR(_id)	((_id) > SFF8024_ID_RESERVED)
1094ce43bedSdlg 
1104ce43bedSdlg #define SFF8024_CON_UNKNOWN	0x00
1114ce43bedSdlg #define SFF8024_CON_SC		0x01 /* Subscriber Connector */
1124ce43bedSdlg #define SFF8024_CON_FC_1	0x02 /* Fibre Channel Style 1 copper */
1134ce43bedSdlg #define SFF8024_CON_FC_2	0x03 /* Fibre Channel Style 2 copper */
1144ce43bedSdlg #define SFF8024_CON_BNC_TNC	0x04 /* BNC/TNC */
1154ce43bedSdlg #define SFF8024_CON_FC_COAX	0x05 /* Fibre Channel coax headers */
1164ce43bedSdlg #define SFF8024_CON_FJ		0x06 /* Fibre Jack */
1174ce43bedSdlg #define SFF8024_CON_LC		0x07 /* Lucent Connector */
1184ce43bedSdlg #define SFF8024_CON_MT_RJ	0x08 /* Mechanical Transfer - Registered Jack */
1194ce43bedSdlg #define SFF8024_CON_MU		0x09 /* Multiple Optical */
1204ce43bedSdlg #define SFF8024_CON_SG		0x0a
1214ce43bedSdlg #define SFF8024_CON_O_PIGTAIL	0x0b /* Optical Pigtail */
1224ce43bedSdlg #define SFF8024_CON_MPO_1x12	0x0c /* Multifiber Parallel Optic 1x12 */
1234ce43bedSdlg #define SFF8024_CON_MPO_2x16	0x0e /* Multifiber Parallel Optic 2x16 */
1244ce43bedSdlg #define SFF8024_CON_HSSDC2	0x20 /* High Speed Serial Data Connector */
1254ce43bedSdlg #define SFF8024_CON_Cu_PIGTAIL	0x21 /* Copper Pigtail */
1264ce43bedSdlg #define SFF8024_CON_RJ45	0x22
1274ce43bedSdlg #define SFF8024_CON_NO		0x23 /* No separable connector */
1284ce43bedSdlg #define SFF8024_CON_MXC_2x16	0x24
1294ce43bedSdlg #define SFF8024_CON_RESERVED	0x7f /* up to here is reserved */
1304ce43bedSdlg 				     /* 0x80 to 0xff is vendor specific */
1314ce43bedSdlg 
1324ce43bedSdlg #define SFF8024_CON_IS_RESERVED(_id)	((_id) <= SFF8024_CON_RESERVED)
1334ce43bedSdlg #define SFF8024_CON_IS_VENDOR(_id)	((_id) > SFF8024_CON_RESERVED)
1344ce43bedSdlg 
1354ce43bedSdlg static const char *sff8024_id_names[] = {
1364ce43bedSdlg 	[SFF8024_ID_UNKNOWN]	= "Unknown",
1374ce43bedSdlg 	[SFF8024_ID_GBIC]	= "GBIC",
1384ce43bedSdlg 	[SFF8024_ID_SFP]	= "SFP",
139d2ae8f01Ssthen 	[SFF8024_ID_300PIN_XBI]	= "XBI",
1404ce43bedSdlg 	[SFF8024_ID_XENPAK]	= "XENPAK",
1414ce43bedSdlg 	[SFF8024_ID_XFP]	= "XFP",
1424ce43bedSdlg 	[SFF8024_ID_XFF]	= "XFF",
1434ce43bedSdlg 	[SFF8024_ID_XFPE]	= "XFPE",
1444ce43bedSdlg 	[SFF8024_ID_XPAK]	= "XPAK",
1454ce43bedSdlg 	[SFF8024_ID_X2]		= "X2",
1464ce43bedSdlg 	[SFF8024_ID_DWDM_SFP]	= "DWDM-SFP",
1474ce43bedSdlg 	[SFF8024_ID_QSFP]	= "QSFP",
1484ce43bedSdlg 	[SFF8024_ID_QSFP_PLUS]	= "QSFP+",
1494ce43bedSdlg 	[SFF8024_ID_CXP]	= "CXP",
150d2ae8f01Ssthen 	[SFF8024_ID_HD4X]	= "HD 4X",
151d2ae8f01Ssthen 	[SFF8024_ID_HD8X]	= "HD 8X",
1524ce43bedSdlg 	[SFF8024_ID_QSFP28]	= "QSFP28",
1534ce43bedSdlg 	[SFF8024_ID_CXP2]	= "CXP2",
1544ce43bedSdlg 	[SFF8024_ID_CDFP]	= "CDFP Style 1/2",
155d2ae8f01Ssthen 	[SFF8024_ID_HD4X_FAN]	= "HD 4X Fanout",
156d2ae8f01Ssthen 	[SFF8024_ID_HD8X_FAN]	= "HD 8X Fanout",
1574ce43bedSdlg 	[SFF8024_ID_CDFP3]	= "CDFP Style 3",
1584ce43bedSdlg 	[SFF8024_ID_uQSFP]	= "microQSFP",
159d2ae8f01Ssthen 	[SFF8024_ID_QSFP_DD]	= "QSFP-DD",
1604ce43bedSdlg };
1614ce43bedSdlg 
1624ce43bedSdlg static const char *sff8024_con_names[] = {
1634ce43bedSdlg 	[SFF8024_CON_UNKNOWN]	= "Unknown",
1644ce43bedSdlg 	[SFF8024_CON_SC]	= "SC",
165d2ae8f01Ssthen 	[SFF8024_CON_FC_1]	= "FC Style 1",
166d2ae8f01Ssthen 	[SFF8024_CON_FC_2]	= "FC Style 2",
1674ce43bedSdlg 	[SFF8024_CON_BNC_TNC]	= "BNC/TNC",
168d2ae8f01Ssthen 	[SFF8024_CON_FC_COAX]	= "FC coax headers",
169d2ae8f01Ssthen 	[SFF8024_CON_FJ]	= "FJ",
1704ce43bedSdlg 	[SFF8024_CON_LC]	= "LC",
1714ce43bedSdlg 	[SFF8024_CON_MT_RJ]	= "MT-RJ",
1724ce43bedSdlg 	[SFF8024_CON_MU]	= "MU",
1734ce43bedSdlg 	[SFF8024_CON_SG]	= "SG",
174d630c397Sderaadt 	[SFF8024_CON_O_PIGTAIL]	= "AOC",
175f52625bcSderaadt 	[SFF8024_CON_MPO_1x12]	= "MPO 1x12",
176f52625bcSderaadt 	[SFF8024_CON_MPO_2x16]	= "MPO 2x16",
1774ce43bedSdlg 	[SFF8024_CON_HSSDC2]	= "HSSDC II",
1784ce43bedSdlg 	[SFF8024_CON_Cu_PIGTAIL]
179d630c397Sderaadt 				= "DAC",
1804ce43bedSdlg 	[SFF8024_CON_RJ45]	= "RJ45",
18120428388Sdlg 	[SFF8024_CON_NO]	= "No connector",
1824ce43bedSdlg 	[SFF8024_CON_MXC_2x16]	= "MXC 2x16",
1834ce43bedSdlg };
1844ce43bedSdlg 
1854ce43bedSdlg #define SFF8472_ID			0 /* SFF8027 for identifier values */
1864ce43bedSdlg #define SFF8472_EXT_ID			1
1874ce43bedSdlg #define SFF8472_EXT_ID_UNSPECIFIED		0x00
1884ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_1		0x01
1894ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_2		0x02
1904ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_3		0x03
1914ce43bedSdlg #define SFF8472_EXT_ID_2WIRE			0x04
1924ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_5		0x05
1934ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_6		0x06
1944ce43bedSdlg #define SFF8472_EXT_ID_MOD_DEF_7		0x07
1954ce43bedSdlg #define SFF8472_CON			2 /* SFF8027 for connector values */
19623a4e4a1Ssthen #define SFF8472_DIST_SMF_KM		14
19723a4e4a1Ssthen #define SFF8472_DIST_SMF_M		15
19823a4e4a1Ssthen #define SFF8472_DIST_OM2		16
19923a4e4a1Ssthen #define SFF8472_DIST_OM1		17
20023a4e4a1Ssthen #define SFF8472_DIST_CU			18
20123a4e4a1Ssthen #define SFF8472_DIST_OM3		19
2024ce43bedSdlg #define SFF8472_VENDOR_START		20
2034ce43bedSdlg #define SFF8472_VENDOR_END		35
2044ce43bedSdlg #define SFF8472_PRODUCT_START		40
2054ce43bedSdlg #define SFF8472_PRODUCT_END		55
2064ce43bedSdlg #define SFF8472_REVISION_START		56
2074ce43bedSdlg #define SFF8472_REVISION_END		59
208d2ae8f01Ssthen #define SFF8472_WAVELENGTH		60
2094ce43bedSdlg #define SFF8472_SERIAL_START		68
2104ce43bedSdlg #define SFF8472_SERIAL_END		83
2114ce43bedSdlg #define SFF8472_DATECODE		84
2124ce43bedSdlg #define SFF8472_DDM_TYPE		92
2134ce43bedSdlg #define SFF8472_DDM_TYPE_AVG_POWER		(1U << 3)
2144ce43bedSdlg #define SFF8472_DDM_TYPE_CAL_EXT		(1U << 4)
2154ce43bedSdlg #define SFF8472_DDM_TYPE_CAL_INT		(1U << 5)
2164ce43bedSdlg #define SFF8472_DDM_TYPE_IMPL			(1U << 6)
2174ce43bedSdlg #define SFF8472_COMPLIANCE		94
2184ce43bedSdlg #define SFF8472_COMPLIANCE_NONE			0x00
2194ce43bedSdlg #define SFF8472_COMPLIANCE_9_3			0x01 /* SFF-8472 Rev 9.3 */
2204ce43bedSdlg #define SFF8472_COMPLIANCE_9_5			0x02 /* SFF-8472 Rev 9.5 */
2214ce43bedSdlg #define SFF8472_COMPLIANCE_10_2			0x03 /* SFF-8472 Rev 10.2 */
2224ce43bedSdlg #define SFF8472_COMPLIANCE_10_4			0x04 /* SFF-8472 Rev 10.4 */
2234ce43bedSdlg #define SFF8472_COMPLIANCE_11_0			0x05 /* SFF-8472 Rev 11.0 */
2244ce43bedSdlg #define SFF8472_COMPLIANCE_11_3			0x06 /* SFF-8472 Rev 11.3 */
2254ce43bedSdlg #define SFF8472_COMPLIANCE_11_4			0x07 /* SFF-8472 Rev 11.4 */
2264ce43bedSdlg #define SFF8472_COMPLIANCE_12_3			0x08 /* SFF-8472 Rev 12.3 */
2274ce43bedSdlg 
2284662fdbeSdlg static const struct sff_media_map sff8472_media_map = {
2294662fdbeSdlg 	.connector_type		= SFF8472_CON,
2304662fdbeSdlg 	.wavelength		= SFF8472_WAVELENGTH,
2314662fdbeSdlg 	.factor_wavelength	= 1.0,
2324662fdbeSdlg 	.dist_smf_m		= SFF8472_DIST_SMF_M,
2334662fdbeSdlg 	.dist_smf_km		= SFF8472_DIST_SMF_KM,
2344662fdbeSdlg 	.dist_om1		= SFF8472_DIST_OM1,
2354662fdbeSdlg 	.scale_om1		= 10,
2364662fdbeSdlg 	.dist_om2		= SFF8472_DIST_OM2,
2374662fdbeSdlg 	.scale_om2		= 10,
2384662fdbeSdlg 	.dist_om3		= SFF8472_DIST_OM3,
2394662fdbeSdlg 	.scale_om3		= 20,
2404662fdbeSdlg 	.dist_cu		= SFF8472_DIST_CU,
2414662fdbeSdlg };
2424662fdbeSdlg 
2434ce43bedSdlg /*
2444ce43bedSdlg  * page 0xa2
2454ce43bedSdlg  */
246d2ae8f01Ssthen #define SFF8472_AW_TEMP			0
247d2ae8f01Ssthen #define SFF8472_AW_VCC			8
248d2ae8f01Ssthen #define SFF8472_AW_TX_BIAS		16
249d2ae8f01Ssthen #define SFF8472_AW_TX_POWER		24
250d2ae8f01Ssthen #define SFF8472_AW_RX_POWER		32
251d2ae8f01Ssthen #define ALRM_HIGH		0
252d2ae8f01Ssthen #define ALRM_LOW		2
253d2ae8f01Ssthen #define WARN_HIGH		4
254d2ae8f01Ssthen #define WARN_LOW		6
2554ce43bedSdlg #define SFF8472_DDM_TEMP		96
2564ce43bedSdlg #define SFF8472_DDM_VCC			98
2574ce43bedSdlg #define SFF8472_DDM_TX_BIAS		100
2584ce43bedSdlg #define SFF8472_DDM_TX_POWER		102
2594ce43bedSdlg #define SFF8472_DDM_RX_POWER		104
2604ce43bedSdlg #define SFF8472_DDM_LASER		106 /* laser temp/wavelength */
2614ce43bedSdlg 					    /* optional */
2624ce43bedSdlg #define SFF8472_DDM_TEC			108 /* Measured TEC current */
263d2ae8f01Ssthen 					    /* optional */
2644ce43bedSdlg 
2654ce43bedSdlg #define SFF_TEMP_FACTOR		256.0
2664ce43bedSdlg #define SFF_VCC_FACTOR		10000.0
2674ce43bedSdlg #define SFF_BIAS_FACTOR		500.0
2684ce43bedSdlg #define SFF_POWER_FACTOR	10000.0
2694ce43bedSdlg 
2704dc28db5Sdlg /*
271e2fee34bSdlg  * QSFP is defined by SFF-8436, but the management interface is
272e2fee34bSdlg  * updated and maintained by SFF-8636.
273e2fee34bSdlg  */
274e2fee34bSdlg 
275392b4b93Sdlg #define SFF8436_STATUS1		1
276392b4b93Sdlg #define SFF8436_STATUS2		2
277392b4b93Sdlg #define SFF8436_STATUS2_DNR		(1 << 0) /* Data_Not_Ready */
278392b4b93Sdlg #define SFF8436_STATUS2_INTL		(1 << 1) /* Interrupt output state */
279392b4b93Sdlg #define SFF8436_STATUS2_FLAT_MEM	(1 << 2) /* Upper memory flat/paged */
280392b4b93Sdlg 
281392b4b93Sdlg #define SFF8436_TEMP		22
282b46ace34Sdlg #define SFF8436_VCC		26
283b46ace34Sdlg 
284b46ace34Sdlg #define SFF8436_CHANNELS	4	/* number of TX and RX channels */
285b46ace34Sdlg #define SFF8436_RX_POWER_BASE	34
286b46ace34Sdlg #define SFF8436_RX_POWER(_i)	(SFF8436_RX_POWER_BASE + ((_i) * 2))
287b46ace34Sdlg #define SFF8436_TX_BIAS_BASE	42
288b46ace34Sdlg #define SFF8436_TX_BIAS(_i)	(SFF8436_TX_BIAS_BASE + ((_i) * 2))
289b46ace34Sdlg #define SFF8436_TX_POWER_BASE	50
290b46ace34Sdlg #define SFF8436_TX_POWER(_i)	(SFF8436_TX_POWER_BASE + ((_i) * 2))
291b46ace34Sdlg 
292bf12d07dSdlg /* Upper Page 00h */
293bf12d07dSdlg 
294bf12d07dSdlg #define SFF8436_MAXCASETEMP	190	/* C */
295bf12d07dSdlg #define SFF8436_MAXCASETEMP_DEFAULT	70 /* if SFF8436_MAXCASETEMP is 0 */
296bf12d07dSdlg 
297bf12d07dSdlg /* Upper page 03h */
298bf12d07dSdlg 
299b46ace34Sdlg #define SFF8436_AW_TEMP		128
300b46ace34Sdlg #define SFF8436_AW_VCC		144
301b46ace34Sdlg #define SFF8436_AW_RX_POWER	176
302b46ace34Sdlg #define SFF8436_AW_TX_BIAS	184
303b46ace34Sdlg #define SFF8436_AW_TX_POWER	192
304392b4b93Sdlg 
305e2fee34bSdlg /*
3064dc28db5Sdlg  * XFP stuff is defined by INF-8077.
3074dc28db5Sdlg  *
3084dc28db5Sdlg  * The "Serial ID Memory Map" on page 1 contains the interesting strings
3094dc28db5Sdlg  */
3104dc28db5Sdlg 
311e2fee34bSdlg /* SFF-8636 and INF-8077 share a layout for various strings */
312e2fee34bSdlg 
3134662fdbeSdlg #define UPPER_CON			130 /* connector type */
3144662fdbeSdlg #define UPPER_DIST_SMF			142
3154662fdbeSdlg #define UPPER_DIST_OM3			143
3164662fdbeSdlg #define UPPER_DIST_OM2			144
3174662fdbeSdlg #define UPPER_DIST_OM1			145
3184662fdbeSdlg #define UPPER_DIST_CU			146
3194662fdbeSdlg #define UPPER_WAVELENGTH		186
3204662fdbeSdlg 
321e2fee34bSdlg #define UPPER_VENDOR_START		148
322e2fee34bSdlg #define UPPER_VENDOR_END		163
323e2fee34bSdlg #define UPPER_PRODUCT_START		168
324e2fee34bSdlg #define UPPER_PRODUCT_END		183
325e2fee34bSdlg #define UPPER_REVISION_START		184
326e2fee34bSdlg #define UPPER_REVISION_END		185
327e2fee34bSdlg #define UPPER_SERIAL_START		196
328e2fee34bSdlg #define UPPER_SERIAL_END		211
329e2fee34bSdlg #define UPPER_DATECODE			212
330e2fee34bSdlg #define UPPER_LOT_START			218
331e2fee34bSdlg #define UPPER_LOT_END			219
3324dc28db5Sdlg 
3334662fdbeSdlg static const struct sff_media_map upper_media_map = {
3344662fdbeSdlg 	.connector_type		= UPPER_CON,
3354662fdbeSdlg 	.wavelength		= UPPER_WAVELENGTH,
3364662fdbeSdlg 	.factor_wavelength	= 20.0,
3374662fdbeSdlg 	.dist_smf_m		= 0,
3384662fdbeSdlg 	.dist_smf_km		= UPPER_DIST_SMF,
3394662fdbeSdlg 	.dist_om1		= UPPER_DIST_OM1,
3404662fdbeSdlg 	.scale_om1		= 1,
3414662fdbeSdlg 	.dist_om2		= UPPER_DIST_OM1,
3424662fdbeSdlg 	.scale_om2		= 1,
3434662fdbeSdlg 	.dist_om3		= UPPER_DIST_OM3,
3444662fdbeSdlg 	.scale_om3		= 2,
3454662fdbeSdlg 	.dist_cu		= UPPER_DIST_CU,
3464662fdbeSdlg };
3474662fdbeSdlg 
3484ce43bedSdlg static void	hexdump(const void *, size_t);
349*4cfa599eSbluhm static int	if_sff8472(int, const struct if_sffpage *);
350*4cfa599eSbluhm static int	if_sff8636(int, const struct if_sffpage *);
351*4cfa599eSbluhm static int	if_inf8077(int, const struct if_sffpage *);
3524ce43bedSdlg 
3534ce43bedSdlg static const char *
sff_id_name(uint8_t id)3544ce43bedSdlg sff_id_name(uint8_t id)
3554ce43bedSdlg {
3564ce43bedSdlg 	const char *name = NULL;
3574ce43bedSdlg 
3584ce43bedSdlg 	if (id < nitems(sff8024_id_names)) {
3594ce43bedSdlg 		name = sff8024_id_names[id];
3604ce43bedSdlg 		if (name != NULL)
3614ce43bedSdlg 			return (name);
3624ce43bedSdlg 	}
3634ce43bedSdlg 
3644ce43bedSdlg 	if (SFF8024_ID_IS_VENDOR(id))
3654ce43bedSdlg 		return ("Vendor Specific");
3664ce43bedSdlg 
3674ce43bedSdlg 	return ("Reserved");
3684ce43bedSdlg }
3694ce43bedSdlg 
3704ce43bedSdlg static const char *
sff_con_name(uint8_t id)3714ce43bedSdlg sff_con_name(uint8_t id)
3724ce43bedSdlg {
3734ce43bedSdlg 	const char *name = NULL;
3744ce43bedSdlg 
3754ce43bedSdlg 	if (id < nitems(sff8024_con_names)) {
3764ce43bedSdlg 		name = sff8024_con_names[id];
3774ce43bedSdlg 		if (name != NULL)
3784ce43bedSdlg 			return (name);
3794ce43bedSdlg 	}
3804ce43bedSdlg 
3814ce43bedSdlg 	if (SFF8024_CON_IS_VENDOR(id))
3824ce43bedSdlg 		return ("Vendor Specific");
3834ce43bedSdlg 
3844ce43bedSdlg 	return ("Reserved");
3854ce43bedSdlg }
3864ce43bedSdlg 
3874ce43bedSdlg static void
if_sffpage_init(struct if_sffpage * sff,uint8_t addr,uint8_t page)388*4cfa599eSbluhm if_sffpage_init(struct if_sffpage *sff, uint8_t addr, uint8_t page)
3894ce43bedSdlg {
3904ce43bedSdlg 	memset(sff, 0, sizeof(*sff));
3914ce43bedSdlg 
3924ce43bedSdlg 	if (strlcpy(sff->sff_ifname, ifname, sizeof(sff->sff_ifname)) >=
3934ce43bedSdlg 	    sizeof(sff->sff_ifname))
3944ce43bedSdlg 		errx(1, "interface name too long");
3954ce43bedSdlg 
3964ce43bedSdlg 	sff->sff_addr = addr;
3974ce43bedSdlg 	sff->sff_page = page;
3984ce43bedSdlg }
3994ce43bedSdlg 
4004ce43bedSdlg static void
if_sffpage_dump(const struct if_sffpage * sff)401*4cfa599eSbluhm if_sffpage_dump(const struct if_sffpage *sff)
4024ce43bedSdlg {
4034ce43bedSdlg 	printf("%s: addr %02x", ifname, sff->sff_addr);
4044ce43bedSdlg 	if (sff->sff_addr == IFSFF_ADDR_EEPROM)
4054ce43bedSdlg 		printf(" page %u", sff->sff_page);
4064ce43bedSdlg 	putchar('\n');
4074ce43bedSdlg 	hexdump(sff->sff_data, sizeof(sff->sff_data));
4084ce43bedSdlg }
4094ce43bedSdlg 
4104ce43bedSdlg int
if_sff_info(int dump)411*4cfa599eSbluhm if_sff_info(int dump)
4124ce43bedSdlg {
4134ce43bedSdlg 	struct if_sffpage pg0;
4144ce43bedSdlg 	int error = 0;
4154ce43bedSdlg 	uint8_t id, ext_id;
4164ce43bedSdlg 
417*4cfa599eSbluhm 	if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 0);
418d6a6566dSbluhm 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) {
4194dc28db5Sdlg 		if (errno == ENXIO) {
4204dc28db5Sdlg 			/* try 1 for XFP cos myx which can't switch pages... */
421*4cfa599eSbluhm 			if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
422d6a6566dSbluhm 			if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
4234ce43bedSdlg 				return (-1);
4249262558cSdlg 		} else
4259262558cSdlg 			return (-1);
4264dc28db5Sdlg 	}
4274ce43bedSdlg 
4284ce43bedSdlg 	if (dump)
429*4cfa599eSbluhm 		if_sffpage_dump(&pg0);
4304ce43bedSdlg 
4314ce43bedSdlg 	id = pg0.sff_data[0]; /* SFF8472_ID */
4324ce43bedSdlg 
4333561a859Ssthen 	printf("\ttransceiver: %s ", sff_id_name(id));
4344ce43bedSdlg 	switch (id) {
4354ce43bedSdlg 	case SFF8024_ID_SFP:
4364ce43bedSdlg 		ext_id = pg0.sff_data[SFF8472_EXT_ID];
4374ce43bedSdlg 		if (ext_id != SFF8472_EXT_ID_2WIRE) {
438d2ae8f01Ssthen 			printf("extended-id %02xh\n", ext_id);
4394ce43bedSdlg 			break;
4404ce43bedSdlg 		}
4414ce43bedSdlg 		/* FALLTHROUGH */
4424ce43bedSdlg 	case SFF8024_ID_GBIC:
443*4cfa599eSbluhm 		error = if_sff8472(dump, &pg0);
4444ce43bedSdlg 		break;
4454dc28db5Sdlg 	case SFF8024_ID_XFP:
4464dc28db5Sdlg 		if (pg0.sff_page != 1) {
447*4cfa599eSbluhm 			if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
448d6a6566dSbluhm 			if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
4494dc28db5Sdlg 				return (-1);
4504dc28db5Sdlg 			if (dump)
451*4cfa599eSbluhm 				if_sffpage_dump(&pg0);
4524dc28db5Sdlg 		}
453*4cfa599eSbluhm 		error = if_inf8077(dump, &pg0);
4544dc28db5Sdlg 		break;
4555866b5bcSdlg 	case SFF8024_ID_QSFP:
456e2fee34bSdlg 	case SFF8024_ID_QSFP_PLUS:
4575866b5bcSdlg 	case SFF8024_ID_QSFP28:
458*4cfa599eSbluhm 		error = if_sff8636(dump, &pg0);
459e2fee34bSdlg 		break;
46043716df3Sdenis 	default:
46143716df3Sdenis 		printf("\n");
46243716df3Sdenis 		break;
4634ce43bedSdlg 	}
4644ce43bedSdlg 
4654ce43bedSdlg 	return (error);
4664ce43bedSdlg }
4674ce43bedSdlg 
4684ce43bedSdlg static void
if_sff_ascii_print(const struct if_sffpage * sff,const char * name,size_t start,size_t end,const char * trailer)469d2ae8f01Ssthen if_sff_ascii_print(const struct if_sffpage *sff, const char *name,
470d2ae8f01Ssthen     size_t start, size_t end, const char *trailer)
4714ce43bedSdlg {
4724ce43bedSdlg 	const uint8_t *d = sff->sff_data;
4734ce43bedSdlg 	int ch;
4744ce43bedSdlg 
4754ce43bedSdlg 	for (;;) {
4764ce43bedSdlg 		ch = d[start];
4774ce43bedSdlg 		if (!isspace(ch) && ch != '\0')
4784ce43bedSdlg 			break;
4794ce43bedSdlg 
4804ce43bedSdlg 		start++;
481d2ae8f01Ssthen 		if (start == end)
4824ce43bedSdlg 			return;
4834ce43bedSdlg 	}
484d2ae8f01Ssthen 
485d2ae8f01Ssthen 	printf("%s", name);
4864ce43bedSdlg 
4874ce43bedSdlg 	for (;;) {
488d6a6566dSbluhm 		ch = d[end];
4894ce43bedSdlg 		if (!isspace(ch) && ch != '\0')
4904ce43bedSdlg 			break;
4914ce43bedSdlg 
4924ce43bedSdlg 		end--;
4934ce43bedSdlg 	}
4944ce43bedSdlg 
4954ce43bedSdlg 	do {
4964ce43bedSdlg 		char dst[8];
4974ce43bedSdlg 		vis(dst, d[start], VIS_TAB | VIS_NL, 0);
4984ce43bedSdlg 		printf("%s", dst);
4994ce43bedSdlg 	} while (++start <= end);
500d2ae8f01Ssthen 
501d2ae8f01Ssthen 	printf("%s", trailer);
5024ce43bedSdlg }
5034ce43bedSdlg 
5044ce43bedSdlg static void
if_sff_date_print(const struct if_sffpage * sff,const char * name,size_t start,const char * trailer)505d2ae8f01Ssthen if_sff_date_print(const struct if_sffpage *sff, const char *name,
506d2ae8f01Ssthen     size_t start, const char *trailer)
5074ce43bedSdlg {
5084ce43bedSdlg 	const uint8_t *d = sff->sff_data + start;
5094ce43bedSdlg 	size_t i;
5104ce43bedSdlg 
5114ce43bedSdlg 	/* YYMMDD */
5124ce43bedSdlg 	for (i = 0; i < 6; i++) {
5134ce43bedSdlg 		if (!isdigit(d[i])) {
514d2ae8f01Ssthen 			if_sff_ascii_print(sff, name, start,
515d2ae8f01Ssthen 			    start + 5, trailer);
5164ce43bedSdlg 			return;
5174ce43bedSdlg 		}
5184ce43bedSdlg 	}
5194ce43bedSdlg 
520d2ae8f01Ssthen 	printf("%s20%c%c-%c%c-%c%c%s", name,
521d2ae8f01Ssthen 	    d[0], d[1], d[2], d[3], d[4], d[5], trailer);
5224ce43bedSdlg }
5234ce43bedSdlg 
5244ce43bedSdlg static int16_t
if_sff_int(const struct if_sffpage * sff,size_t start)5254ce43bedSdlg if_sff_int(const struct if_sffpage *sff, size_t start)
5264ce43bedSdlg {
5274ce43bedSdlg 	const uint8_t *d = sff->sff_data + start;
5284ce43bedSdlg 
5294ce43bedSdlg 	return (d[0] << 8 | d[1]);
5304ce43bedSdlg }
5314ce43bedSdlg 
5324ce43bedSdlg static uint16_t
if_sff_uint(const struct if_sffpage * sff,size_t start)5334ce43bedSdlg if_sff_uint(const struct if_sffpage *sff, size_t start)
5344ce43bedSdlg {
5354ce43bedSdlg 	const uint8_t *d = sff->sff_data + start;
5364ce43bedSdlg 
5374ce43bedSdlg 	return (d[0] << 8 | d[1]);
5384ce43bedSdlg }
5394ce43bedSdlg 
5404ce43bedSdlg static float
if_sff_power2dbm(const struct if_sffpage * sff,size_t start)541d2ae8f01Ssthen if_sff_power2dbm(const struct if_sffpage *sff, size_t start)
5424ce43bedSdlg {
543d2ae8f01Ssthen 	const uint8_t *d = sff->sff_data + start;
544d2ae8f01Ssthen 
545d2ae8f01Ssthen 	int power = d[0] << 8 | d[1];
5464ce43bedSdlg 	return (10.0 * log10f((float)power / 10000.0));
5474ce43bedSdlg }
5484ce43bedSdlg 
549d2ae8f01Ssthen static void
if_sff_printalarm(const char * unit,int range,float actual,float alrm_high,float alrm_low,float warn_high,float warn_log)550d2ae8f01Ssthen if_sff_printalarm(const char *unit, int range, float actual,
551d2ae8f01Ssthen     float alrm_high, float alrm_low, float warn_high, float warn_log)
552d2ae8f01Ssthen {
5533519233cSsthen 	printf("%.02f%s", actual, unit);
554d2ae8f01Ssthen 	if (range == 1)
5553561a859Ssthen 		printf(" (low %.02f%s, high %.02f%s)", alrm_low,
556d2ae8f01Ssthen 		    unit, alrm_high, unit);
557d2ae8f01Ssthen 
558d2ae8f01Ssthen 	if(actual > alrm_high || actual < alrm_low)
559d2ae8f01Ssthen 		printf(" [ALARM]");
560d2ae8f01Ssthen 	else if(actual > warn_high || actual < warn_log)
561d2ae8f01Ssthen 		printf(" [WARNING]");
562d2ae8f01Ssthen }
563d2ae8f01Ssthen 
56423a4e4a1Ssthen static void
if_sff_printdist(const char * type,int value,int scale)56523a4e4a1Ssthen if_sff_printdist(const char *type, int value, int scale)
56623a4e4a1Ssthen {
56723a4e4a1Ssthen 	int distance = value * scale;
56823a4e4a1Ssthen 
56923a4e4a1Ssthen 	if (value == 0)
57023a4e4a1Ssthen 		return;
57123a4e4a1Ssthen 
57223a4e4a1Ssthen 	if (distance < 10000)
57323a4e4a1Ssthen 		printf (", %s%u%s", value > 254 ? ">" : "", distance, type);
57423a4e4a1Ssthen 	else
57523a4e4a1Ssthen 		printf (", %s%0.1fk%s", value > 254 ? ">" : "",
57623a4e4a1Ssthen 		    distance / 1000.0, type);
57723a4e4a1Ssthen }
57823a4e4a1Ssthen 
5794662fdbeSdlg static void
if_sff_printmedia(const struct if_sffpage * pg,const struct sff_media_map * m)5804662fdbeSdlg if_sff_printmedia(const struct if_sffpage *pg, const struct sff_media_map *m)
5814ce43bedSdlg {
582d6a6566dSbluhm 	uint8_t con;
5834662fdbeSdlg 	unsigned int wavelength;
5844ce43bedSdlg 
5854662fdbeSdlg 	con = pg->sff_data[m->connector_type];
5863561a859Ssthen 	printf("%s", sff_con_name(con));
587d2ae8f01Ssthen 
5884662fdbeSdlg 	wavelength = if_sff_uint(pg, m->wavelength);
5894662fdbeSdlg 	switch (wavelength) {
5904662fdbeSdlg 	case 0x0000:
5914662fdbeSdlg 		/* not known or is unavailable */
5924662fdbeSdlg 		break;
593d2ae8f01Ssthen 	/* Copper Cable */
594d2ae8f01Ssthen 	case 0x0100: /* SFF-8431 Appendix E */
595d2ae8f01Ssthen 	case 0x0400: /* SFF-8431 limiting */
596d2ae8f01Ssthen 	case 0x0c00: /* SFF-8431 limiting and FC-PI-4 limiting */
597d2ae8f01Ssthen 		break;
598d2ae8f01Ssthen 	default:
5994662fdbeSdlg 		printf(", %.f nm", wavelength / m->factor_wavelength);
6004ce43bedSdlg 	}
6014ce43bedSdlg 
6024662fdbeSdlg 	if (m->dist_smf_m != 0 &&
603da9583c4Sdlg 	    pg->sff_data[m->dist_smf_m] > 0 &&
6044662fdbeSdlg 	    pg->sff_data[m->dist_smf_m] < 255)
6054662fdbeSdlg 		if_sff_printdist("m SMF", pg->sff_data[m->dist_smf_m], 100);
60623a4e4a1Ssthen 	else
6074662fdbeSdlg 		if_sff_printdist("km SMF", pg->sff_data[m->dist_smf_km], 1);
6084662fdbeSdlg 	if_sff_printdist("m OM1", pg->sff_data[m->dist_om1], m->scale_om1);
609f81e3884Sdlg 	if_sff_printdist("m OM2", pg->sff_data[m->dist_om2], m->scale_om2);
6104662fdbeSdlg 	if_sff_printdist("m OM3", pg->sff_data[m->dist_om3], m->scale_om3);
6114662fdbeSdlg 	if_sff_printdist("m", pg->sff_data[m->dist_cu], 1);
6124662fdbeSdlg }
6134662fdbeSdlg 
6144662fdbeSdlg static int
if_sff8472(int dump,const struct if_sffpage * pg0)615*4cfa599eSbluhm if_sff8472(int dump, const struct if_sffpage *pg0)
6164662fdbeSdlg {
6174662fdbeSdlg 	struct if_sffpage ddm;
6184662fdbeSdlg 	uint8_t ddm_types;
6194662fdbeSdlg 
6204662fdbeSdlg 	if_sff_printmedia(pg0, &sff8472_media_map);
62123a4e4a1Ssthen 
6223561a859Ssthen 	printf("\n\tmodel: ");
6233561a859Ssthen 	if_sff_ascii_print(pg0, "",
6243561a859Ssthen 	    SFF8472_VENDOR_START, SFF8472_VENDOR_END, " ");
6253561a859Ssthen 	if_sff_ascii_print(pg0, "",
6263561a859Ssthen 	    SFF8472_PRODUCT_START, SFF8472_PRODUCT_END, "");
6273561a859Ssthen 	if_sff_ascii_print(pg0, " rev ",
6283561a859Ssthen 	    SFF8472_REVISION_START, SFF8472_REVISION_END, "");
629d2ae8f01Ssthen 
6303561a859Ssthen 	if_sff_ascii_print(pg0, "\n\tserial: ",
631d2ae8f01Ssthen 	    SFF8472_SERIAL_START, SFF8472_SERIAL_END, ", ");
6323561a859Ssthen 	if_sff_date_print(pg0, "date: ", SFF8472_DATECODE, "\n");
6334ce43bedSdlg 
6344ce43bedSdlg 	ddm_types = pg0->sff_data[SFF8472_DDM_TYPE];
6354ce43bedSdlg 	if (pg0->sff_data[SFF8472_COMPLIANCE] == SFF8472_COMPLIANCE_NONE ||
6364ce43bedSdlg 	    !ISSET(ddm_types, SFF8472_DDM_TYPE_IMPL))
6374ce43bedSdlg 		return (0);
6384ce43bedSdlg 
639*4cfa599eSbluhm 	if_sffpage_init(&ddm, IFSFF_ADDR_DDM, 0);
640d6a6566dSbluhm 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&ddm) == -1)
6414ce43bedSdlg 		return (-1);
6424ce43bedSdlg 
6434ce43bedSdlg 	if (dump)
644*4cfa599eSbluhm 		if_sffpage_dump(&ddm);
6454ce43bedSdlg 
6464ce43bedSdlg 	if (ISSET(ddm_types, SFF8472_DDM_TYPE_CAL_EXT)) {
6474ce43bedSdlg 		printf("\tcalibration: external "
6484ce43bedSdlg 		    "(WARNING: needs more code)\n");
6494ce43bedSdlg 	}
6504ce43bedSdlg 
6513561a859Ssthen 	printf("\tvoltage: ");
652d2ae8f01Ssthen 	if_sff_printalarm(" V", 0,
653d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR,
654d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_HIGH) / SFF_VCC_FACTOR,
655d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_LOW) / SFF_VCC_FACTOR,
656d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_HIGH) / SFF_VCC_FACTOR,
657d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_LOW) / SFF_VCC_FACTOR);
6584ce43bedSdlg 
6593561a859Ssthen 	printf(", bias current: ");
660d2ae8f01Ssthen 	if_sff_printalarm(" mA", 0,
661d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR,
662d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_HIGH) / SFF_BIAS_FACTOR,
663d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_LOW) / SFF_BIAS_FACTOR,
664d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_HIGH) / SFF_BIAS_FACTOR,
665d2ae8f01Ssthen 	    if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_LOW) / SFF_BIAS_FACTOR);
666d2ae8f01Ssthen 
6673561a859Ssthen 	printf("\n\ttemp: ");
668d2ae8f01Ssthen 	if_sff_printalarm(" C", 1,
669d2ae8f01Ssthen 	    if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR,
670d2ae8f01Ssthen 	    if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_HIGH) / SFF_TEMP_FACTOR,
671d2ae8f01Ssthen 	    if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_LOW) / SFF_TEMP_FACTOR,
672d2ae8f01Ssthen 	    if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_HIGH) / SFF_TEMP_FACTOR,
673d2ae8f01Ssthen 	    if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_LOW) / SFF_TEMP_FACTOR);
674d2ae8f01Ssthen 
6753561a859Ssthen 	printf("\n\ttx: ");
676d2ae8f01Ssthen 	if_sff_printalarm(" dBm", 1,
677d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_DDM_TX_POWER),
678d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_HIGH),
679d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_LOW),
680d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_HIGH),
681d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_LOW));
682d2ae8f01Ssthen 
6833561a859Ssthen 	printf("\n\trx: ");
684d2ae8f01Ssthen 	if_sff_printalarm(" dBm", 1,
685d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_DDM_RX_POWER),
686d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_HIGH),
687d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_LOW),
688d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_HIGH),
689d2ae8f01Ssthen 	    if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_LOW));
690d2ae8f01Ssthen 
691d2ae8f01Ssthen 	putchar('\n');
6924ce43bedSdlg 	return (0);
6934ce43bedSdlg }
6944ce43bedSdlg 
695e2fee34bSdlg static void
if_upper_strings(const struct if_sffpage * pg)696e2fee34bSdlg if_upper_strings(const struct if_sffpage *pg)
697e2fee34bSdlg {
6984662fdbeSdlg 	if_sff_printmedia(pg, &upper_media_map);
6994662fdbeSdlg 
700e2fee34bSdlg 	printf("\n\tmodel: ");
701e2fee34bSdlg 	if_sff_ascii_print(pg, "",
702e2fee34bSdlg 	    UPPER_VENDOR_START, UPPER_VENDOR_END, " ");
703e2fee34bSdlg 	if_sff_ascii_print(pg, "",
704e2fee34bSdlg 	    UPPER_PRODUCT_START, UPPER_PRODUCT_END, "");
705e2fee34bSdlg 	if_sff_ascii_print(pg, " rev ",
706e2fee34bSdlg 	    UPPER_REVISION_START, UPPER_REVISION_END, "");
707e2fee34bSdlg 
708e2fee34bSdlg 	if_sff_ascii_print(pg, "\n\tserial: ",
709e2fee34bSdlg 	    UPPER_SERIAL_START, UPPER_SERIAL_END, " ");
710e2fee34bSdlg 	if_sff_date_print(pg, "date: ", UPPER_DATECODE, " ");
711e2fee34bSdlg 	if_sff_ascii_print(pg, "lot: ",
712e2fee34bSdlg 	    UPPER_LOT_START, UPPER_LOT_END, "");
713e2fee34bSdlg 
714e2fee34bSdlg 	putchar('\n');
715e2fee34bSdlg }
716e2fee34bSdlg 
7174ce43bedSdlg static int
if_inf8077(int dump,const struct if_sffpage * pg1)718*4cfa599eSbluhm if_inf8077(int dump, const struct if_sffpage *pg1)
7194dc28db5Sdlg {
720e2fee34bSdlg 	if_upper_strings(pg1);
7214dc28db5Sdlg 
722e2fee34bSdlg 	return (0);
723e2fee34bSdlg }
7244dc28db5Sdlg 
725e2fee34bSdlg static int
if_sff8636_thresh(int dump,const struct if_sffpage * pg0)726*4cfa599eSbluhm if_sff8636_thresh(int dump, const struct if_sffpage *pg0)
727392b4b93Sdlg {
728392b4b93Sdlg 	struct if_sffpage pg3;
729b46ace34Sdlg 	unsigned int i;
730b46ace34Sdlg 	struct sff_thresholds temp, vcc, tx, rx, bias;
731392b4b93Sdlg 
732*4cfa599eSbluhm 	if_sffpage_init(&pg3, IFSFF_ADDR_EEPROM, 3);
733d6a6566dSbluhm 	if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg3) == -1) {
734392b4b93Sdlg 		if (dump)
735392b4b93Sdlg 			warn("%s SIOCGIFSFFPAGE page 3", ifname);
736392b4b93Sdlg 		return (-1);
737392b4b93Sdlg 	}
738392b4b93Sdlg 
739392b4b93Sdlg 	if (dump)
740*4cfa599eSbluhm 		if_sffpage_dump(&pg3);
741392b4b93Sdlg 
742392b4b93Sdlg 	if (pg3.sff_data[0x7f] != 3) { /* just in case... */
743392b4b93Sdlg 		if (dump) {
744392b4b93Sdlg 			warnx("%s SIOCGIFSFFPAGE: page select unsupported",
745392b4b93Sdlg 			    ifname);
746392b4b93Sdlg 		}
747392b4b93Sdlg 		return (-1);
748392b4b93Sdlg 	}
749392b4b93Sdlg 
750b46ace34Sdlg 	for (i = 0; i < SFF_THRESH_COUNT; i++) {
751b46ace34Sdlg 		temp.thresholds[i] = if_sff_int(&pg3,
752b46ace34Sdlg 		    SFF8436_AW_TEMP + SFF_THRESH_REG(i)) / SFF_TEMP_FACTOR;
753b46ace34Sdlg 
754b46ace34Sdlg 		vcc.thresholds[i] = if_sff_uint(&pg3,
755b46ace34Sdlg 		    SFF8436_AW_VCC + SFF_THRESH_REG(i)) / SFF_VCC_FACTOR;
756b46ace34Sdlg 
757b46ace34Sdlg 		rx.thresholds[i] = if_sff_power2dbm(&pg3,
758b46ace34Sdlg 		    SFF8436_AW_RX_POWER + SFF_THRESH_REG(i));
759b46ace34Sdlg 
760b46ace34Sdlg 		bias.thresholds[i] = if_sff_uint(&pg3,
761b46ace34Sdlg 		    SFF8436_AW_TX_BIAS + SFF_THRESH_REG(i)) / SFF_BIAS_FACTOR;
762b46ace34Sdlg 
763b46ace34Sdlg 		tx.thresholds[i] = if_sff_power2dbm(&pg3,
764b46ace34Sdlg 		    SFF8436_AW_TX_POWER + SFF_THRESH_REG(i));
765b46ace34Sdlg 	}
766b46ace34Sdlg 
767b46ace34Sdlg 	printf("\ttemp: ");
768bf12d07dSdlg 	if_sff_printalarm(" C", 1,
769b46ace34Sdlg 	    if_sff_int(&pg3, SFF8436_TEMP) / SFF_TEMP_FACTOR,
770b46ace34Sdlg 	    temp.thresholds[SFF_THRESH_HI_ALARM],
771b46ace34Sdlg 	    temp.thresholds[SFF_THRESH_LO_ALARM],
772b46ace34Sdlg 	    temp.thresholds[SFF_THRESH_HI_WARN],
773b46ace34Sdlg 	    temp.thresholds[SFF_THRESH_LO_WARN]);
774b46ace34Sdlg 	printf("\n");
775b46ace34Sdlg 
776b46ace34Sdlg 	printf("\tvoltage: ");
777bf12d07dSdlg 	if_sff_printalarm(" V", 1,
778b46ace34Sdlg 	    if_sff_uint(&pg3, SFF8436_VCC) / SFF_VCC_FACTOR,
779b46ace34Sdlg 	    vcc.thresholds[SFF_THRESH_HI_ALARM],
780b46ace34Sdlg 	    vcc.thresholds[SFF_THRESH_LO_ALARM],
781b46ace34Sdlg 	    vcc.thresholds[SFF_THRESH_HI_WARN],
782b46ace34Sdlg 	    vcc.thresholds[SFF_THRESH_LO_WARN]);
783b46ace34Sdlg 	printf("\n");
784b46ace34Sdlg 
785b46ace34Sdlg 	for (i = 0; i < SFF8436_CHANNELS; i++) {
786b46ace34Sdlg 		unsigned int channel = i + 1;
787b46ace34Sdlg 
788b46ace34Sdlg 		printf("\tchannel %u bias current: ", channel);
789b46ace34Sdlg 		if_sff_printalarm(" mA", 1,
790b46ace34Sdlg 		    if_sff_uint(&pg3, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR,
791b46ace34Sdlg 		    bias.thresholds[SFF_THRESH_HI_ALARM],
792b46ace34Sdlg 		    bias.thresholds[SFF_THRESH_LO_ALARM],
793b46ace34Sdlg 		    bias.thresholds[SFF_THRESH_HI_WARN],
794b46ace34Sdlg 		    bias.thresholds[SFF_THRESH_LO_WARN]);
795b46ace34Sdlg 		printf("\n");
796b46ace34Sdlg 
797b46ace34Sdlg 		printf("\tchannel %u tx: ", channel);
798b46ace34Sdlg 		if_sff_printalarm(" dBm", 1,
799b46ace34Sdlg 		    if_sff_power2dbm(&pg3, SFF8436_TX_POWER(i)),
800b46ace34Sdlg 		    tx.thresholds[SFF_THRESH_HI_ALARM],
801b46ace34Sdlg 		    tx.thresholds[SFF_THRESH_LO_ALARM],
802b46ace34Sdlg 		    tx.thresholds[SFF_THRESH_HI_WARN],
803b46ace34Sdlg 		    tx.thresholds[SFF_THRESH_LO_WARN]);
804b46ace34Sdlg 		printf("\n");
805b46ace34Sdlg 
806b46ace34Sdlg 		printf("\tchannel %u rx: ", channel);
807b46ace34Sdlg 		if_sff_printalarm(" dBm", 1,
808b46ace34Sdlg 		    if_sff_power2dbm(&pg3, SFF8436_RX_POWER(i)),
809b46ace34Sdlg 		    rx.thresholds[SFF_THRESH_HI_ALARM],
810b46ace34Sdlg 		    rx.thresholds[SFF_THRESH_LO_ALARM],
811b46ace34Sdlg 		    rx.thresholds[SFF_THRESH_HI_WARN],
812b46ace34Sdlg 		    rx.thresholds[SFF_THRESH_LO_WARN]);
813b46ace34Sdlg 		printf("\n");
814b46ace34Sdlg 	}
815b46ace34Sdlg 
816b46ace34Sdlg 	return (0);
817392b4b93Sdlg }
818392b4b93Sdlg 
819392b4b93Sdlg static int
if_sff8636(int dump,const struct if_sffpage * pg0)820*4cfa599eSbluhm if_sff8636(int dump, const struct if_sffpage *pg0)
821e2fee34bSdlg {
822392b4b93Sdlg 	int16_t temp;
823bf12d07dSdlg 	uint8_t maxcasetemp;
824392b4b93Sdlg 	uint8_t flat;
825b46ace34Sdlg 	unsigned int i;
826392b4b93Sdlg 
827e2fee34bSdlg 	if_upper_strings(pg0);
8284dc28db5Sdlg 
829392b4b93Sdlg 	if (pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_DNR) {
830392b4b93Sdlg 		printf("\tmonitor data not ready\n");
831392b4b93Sdlg 		return (0);
832392b4b93Sdlg 	}
833392b4b93Sdlg 
834bf12d07dSdlg 	maxcasetemp = pg0->sff_data[SFF8436_MAXCASETEMP];
835bf12d07dSdlg 	if (maxcasetemp == 0x00)
836bf12d07dSdlg 		maxcasetemp = SFF8436_MAXCASETEMP_DEFAULT;
837bf12d07dSdlg 	printf("\tmax case temp: %u C\n", maxcasetemp);
838bf12d07dSdlg 
839392b4b93Sdlg 	temp = if_sff_int(pg0, SFF8436_TEMP);
840392b4b93Sdlg 	/* the temp reading look unset, assume the rest will be unset too */
841392b4b93Sdlg 	if ((uint16_t)temp == 0 || (uint16_t)temp == 0xffffU) {
842392b4b93Sdlg 		if (!dump)
843392b4b93Sdlg 			return (0);
844392b4b93Sdlg 	}
845392b4b93Sdlg 
846392b4b93Sdlg 	flat = pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_FLAT_MEM;
847*4cfa599eSbluhm 	if (!flat && if_sff8636_thresh(dump, pg0) == 0) {
848b46ace34Sdlg 		if (!dump)
849392b4b93Sdlg 			return (0);
850b46ace34Sdlg 	}
851392b4b93Sdlg 
852392b4b93Sdlg 	printf("\t");
853392b4b93Sdlg 	printf("temp: %.02f%s", temp / SFF_TEMP_FACTOR, " C");
854392b4b93Sdlg 	printf(", ");
855392b4b93Sdlg 	printf("voltage: %.02f%s",
856b46ace34Sdlg 	    if_sff_uint(pg0, SFF8436_VCC) / SFF_VCC_FACTOR, " V");
857392b4b93Sdlg 	printf("\n");
858392b4b93Sdlg 
859b46ace34Sdlg 	for (i = 0; i < SFF8436_CHANNELS; i++) {
860b46ace34Sdlg 		printf("\t");
861b46ace34Sdlg 		printf("channel %u: ", i + 1);
862b46ace34Sdlg 		printf("bias current: %.02f mA",
863b46ace34Sdlg 		    if_sff_uint(pg0, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR);
864b46ace34Sdlg 		printf(", ");
865b46ace34Sdlg 		printf("rx: %.02f dBm",
866b46ace34Sdlg 		    if_sff_power2dbm(pg0, SFF8436_RX_POWER(i)));
867b46ace34Sdlg 		printf(", ");
868b46ace34Sdlg 		printf("tx: %.02f dBm",
869b46ace34Sdlg 		    if_sff_power2dbm(pg0, SFF8436_TX_POWER(i)));
870b46ace34Sdlg 		printf("\n");
871b46ace34Sdlg 	}
872b46ace34Sdlg 
8734dc28db5Sdlg 	return (0);
8744dc28db5Sdlg }
8754dc28db5Sdlg 
8764dc28db5Sdlg static int
printable(int ch)8774ce43bedSdlg printable(int ch)
8784ce43bedSdlg {
8794ce43bedSdlg 	if (ch == '\0')
8804ce43bedSdlg 		return ('_');
8814ce43bedSdlg 	if (!isprint(ch))
8824ce43bedSdlg 		return ('~');
8834ce43bedSdlg 
8844ce43bedSdlg 	return (ch);
8854ce43bedSdlg }
8864ce43bedSdlg 
8874ce43bedSdlg static void
hexdump(const void * d,size_t datalen)8884ce43bedSdlg hexdump(const void *d, size_t datalen)
8894ce43bedSdlg {
8904ce43bedSdlg 	const uint8_t *data = d;
8914ce43bedSdlg 	int i, j = 0;
8924ce43bedSdlg 
8934ce43bedSdlg 	for (i = 0; i < datalen; i += j) {
8944ce43bedSdlg 		printf("% 4d: ", i);
8954ce43bedSdlg 		for (j = 0; j < 16 && i+j < datalen; j++)
8964ce43bedSdlg 			printf("%02x ", data[i + j]);
8974ce43bedSdlg 		while (j++ < 16)
8984ce43bedSdlg 			printf("   ");
8994ce43bedSdlg 		printf("|");
9004ce43bedSdlg 		for (j = 0; j < 16 && i+j < datalen; j++)
9014ce43bedSdlg 			putchar(printable(data[i + j]));
9024ce43bedSdlg 		printf("|\n");
9034ce43bedSdlg 	}
9044ce43bedSdlg }
9054ce43bedSdlg 
9064ce43bedSdlg #endif /* SMALL */
907