xref: /illumos-gate/usr/src/lib/libsff/common/libsff.c (revision 45d3dd98)
1*45d3dd98SRobert Mustacchi /*
2*45d3dd98SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*45d3dd98SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*45d3dd98SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*45d3dd98SRobert Mustacchi  * 1.0 of the CDDL.
6*45d3dd98SRobert Mustacchi  *
7*45d3dd98SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*45d3dd98SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*45d3dd98SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*45d3dd98SRobert Mustacchi  */
11*45d3dd98SRobert Mustacchi 
12*45d3dd98SRobert Mustacchi /*
13*45d3dd98SRobert Mustacchi  * Copyright (c) 2017, Joyent, Inc.
14*45d3dd98SRobert Mustacchi  */
15*45d3dd98SRobert Mustacchi 
16*45d3dd98SRobert Mustacchi /*
17*45d3dd98SRobert Mustacchi  * Parse raw SFF data into an nvlist that can be processed by users, providing
18*45d3dd98SRobert Mustacchi  * them with what can be printable strings. At the moment, we handle the
19*45d3dd98SRobert Mustacchi  * majority of parsing page 0xa0 based on SFF 8472 (thus covering INF-8074 and
20*45d3dd98SRobert Mustacchi  * friends) and SFF 8636 (thus covering SFF-8436 and friends). Interfaces that
21*45d3dd98SRobert Mustacchi  * parse data into logical structures may be useful to add when considering
22*45d3dd98SRobert Mustacchi  * monitoring data in page 0xa2.
23*45d3dd98SRobert Mustacchi  *
24*45d3dd98SRobert Mustacchi  * When parsing, we try to make sure that the user has supplied, or at least
25*45d3dd98SRobert Mustacchi  * thinks they have supplied, a buffer of sufficient length. The general design
26*45d3dd98SRobert Mustacchi  * is that we require the buffer to be large enough to cover all of the offsets
27*45d3dd98SRobert Mustacchi  * that we care about. If the buffer isn't this large, then we leave it be.
28*45d3dd98SRobert Mustacchi  *
29*45d3dd98SRobert Mustacchi  * This library is private and subject to change at any time.
30*45d3dd98SRobert Mustacchi  */
31*45d3dd98SRobert Mustacchi 
32*45d3dd98SRobert Mustacchi #include <assert.h>
33*45d3dd98SRobert Mustacchi #include <strings.h>
34*45d3dd98SRobert Mustacchi #include <libsff.h>
35*45d3dd98SRobert Mustacchi #include <errno.h>
36*45d3dd98SRobert Mustacchi #include <ctype.h>
37*45d3dd98SRobert Mustacchi 
38*45d3dd98SRobert Mustacchi #include "sff.h"
39*45d3dd98SRobert Mustacchi 
40*45d3dd98SRobert Mustacchi #define	MIN(a, b)	((a) < (b) ? (a) : (b))
41*45d3dd98SRobert Mustacchi 
42*45d3dd98SRobert Mustacchi /*
43*45d3dd98SRobert Mustacchi  * Maximum size of a string buffer while parsing.
44*45d3dd98SRobert Mustacchi  */
45*45d3dd98SRobert Mustacchi #define	SFP_STRBUF	128
46*45d3dd98SRobert Mustacchi 
47*45d3dd98SRobert Mustacchi /*
48*45d3dd98SRobert Mustacchi  * Minimum length of the buffer we require to parse the SFP data.
49*45d3dd98SRobert Mustacchi  */
50*45d3dd98SRobert Mustacchi #define	SFP_MIN_LEN_8472	96
51*45d3dd98SRobert Mustacchi #define	SFP_MIN_LEN_8636	224
52*45d3dd98SRobert Mustacchi 
53*45d3dd98SRobert Mustacchi /*
54*45d3dd98SRobert Mustacchi  * This table is derived from SFF 8024 Section 4.1, Table 4-1.
55*45d3dd98SRobert Mustacchi  */
56*45d3dd98SRobert Mustacchi static const char *sff_8024_id_strs[SFF_8024_NIDS] = {
57*45d3dd98SRobert Mustacchi 	"Unknown or Unspecified",
58*45d3dd98SRobert Mustacchi 	"GBIC",
59*45d3dd98SRobert Mustacchi 	"Module/connector soldered to motherboard",
60*45d3dd98SRobert Mustacchi 	"SFP/SFP+/SFP28",
61*45d3dd98SRobert Mustacchi 	"300 pin XBI",
62*45d3dd98SRobert Mustacchi 	"XENPAK",
63*45d3dd98SRobert Mustacchi 	"XFP",
64*45d3dd98SRobert Mustacchi 	"XFF",
65*45d3dd98SRobert Mustacchi 	"XFP-E",
66*45d3dd98SRobert Mustacchi 	"XPAK",
67*45d3dd98SRobert Mustacchi 	"X2",
68*45d3dd98SRobert Mustacchi 	"DWDM-SFP/SFP+ (not using SFF-8472)",
69*45d3dd98SRobert Mustacchi 	"QSFP",
70*45d3dd98SRobert Mustacchi 	"QSFP+ or later",
71*45d3dd98SRobert Mustacchi 	"CXP or later",
72*45d3dd98SRobert Mustacchi 	"Shielded Mini Multilane HD 4X",
73*45d3dd98SRobert Mustacchi 	"Shielded Mini Multilane HD 8X",
74*45d3dd98SRobert Mustacchi 	"QSFP28 or later",
75*45d3dd98SRobert Mustacchi 	"CXP2 (aka CXP28) or later",
76*45d3dd98SRobert Mustacchi 	"CDFP (Style 1/Style2)",
77*45d3dd98SRobert Mustacchi 	"Shielded Mini Multilane HD 4X Fanout Cable",
78*45d3dd98SRobert Mustacchi 	"Shielded Mini Multilane HD 8X Fanout Cable",
79*45d3dd98SRobert Mustacchi 	"CDFP (Style 3)",
80*45d3dd98SRobert Mustacchi 	"microQSFP"
81*45d3dd98SRobert Mustacchi };
82*45d3dd98SRobert Mustacchi 
83*45d3dd98SRobert Mustacchi /*
84*45d3dd98SRobert Mustacchi  * The set of values used for the encoding depends on whether we're a basic SFP
85*45d3dd98SRobert Mustacchi  * device or not. The values are inconsistent between SFP and QSFP based
86*45d3dd98SRobert Mustacchi  * devices.
87*45d3dd98SRobert Mustacchi  *
88*45d3dd98SRobert Mustacchi  * This table is derived from SFF 8024 r3.9 Table 4-2.
89*45d3dd98SRobert Mustacchi  */
90*45d3dd98SRobert Mustacchi #define	SFF_8024_NENCS	9
91*45d3dd98SRobert Mustacchi static const char *sff_8024_enc_sfp[] = {
92*45d3dd98SRobert Mustacchi 	"Unspecified",
93*45d3dd98SRobert Mustacchi 	"8B/10B",
94*45d3dd98SRobert Mustacchi 	"4B/5B",
95*45d3dd98SRobert Mustacchi 	"NRZ",
96*45d3dd98SRobert Mustacchi 	"Manchester",
97*45d3dd98SRobert Mustacchi 	"SONET Scrambled",
98*45d3dd98SRobert Mustacchi 	"64B/66B",
99*45d3dd98SRobert Mustacchi 	"256B/257B",
100*45d3dd98SRobert Mustacchi 	"PAM4"
101*45d3dd98SRobert Mustacchi };
102*45d3dd98SRobert Mustacchi 
103*45d3dd98SRobert Mustacchi static const char *sff_8024_enc_qsfp[] = {
104*45d3dd98SRobert Mustacchi 	"Unspecified",
105*45d3dd98SRobert Mustacchi 	"8B/10B",
106*45d3dd98SRobert Mustacchi 	"4B/5B",
107*45d3dd98SRobert Mustacchi 	"NRZ",
108*45d3dd98SRobert Mustacchi 	"SONET Scrambled",
109*45d3dd98SRobert Mustacchi 	"64B/66B",
110*45d3dd98SRobert Mustacchi 	"Manchester",
111*45d3dd98SRobert Mustacchi 	"256B/257B",
112*45d3dd98SRobert Mustacchi 	"PAM4"
113*45d3dd98SRobert Mustacchi };
114*45d3dd98SRobert Mustacchi 
115*45d3dd98SRobert Mustacchi /*
116*45d3dd98SRobert Mustacchi  * This table is derived from SFF 8024 r3.9 Section 4.4.
117*45d3dd98SRobert Mustacchi  */
118*45d3dd98SRobert Mustacchi #define	SFF_8024_EXT_SPEC_NENTRIES	27
119*45d3dd98SRobert Mustacchi static const char *sff_8024_ext_spec[] = {
120*45d3dd98SRobert Mustacchi 	"Unspecified",
121*45d3dd98SRobert Mustacchi 	"100G AOC or 25GAUI C2M AOC",
122*45d3dd98SRobert Mustacchi 	"100GBASE-SR4 or 25GBASE-SR",
123*45d3dd98SRobert Mustacchi 	"100GBASE-LR4 or 25GBASE-LR",
124*45d3dd98SRobert Mustacchi 	"100GBASE-ER4 or 25GBASE-ER",
125*45d3dd98SRobert Mustacchi 	"100GBASE-SR10",
126*45d3dd98SRobert Mustacchi 	"100G CWDM4",
127*45d3dd98SRobert Mustacchi 	"100G PSM4 Parallel SMF",
128*45d3dd98SRobert Mustacchi 	"100G ACC or 25GAUI C2M ACC",
129*45d3dd98SRobert Mustacchi 	"Obsolete",
130*45d3dd98SRobert Mustacchi 	"Reserved",
131*45d3dd98SRobert Mustacchi 	"100GBASE-CR4 or 25GBASE-CR CA-L",
132*45d3dd98SRobert Mustacchi 	"25GBASE-CR CA-S",
133*45d3dd98SRobert Mustacchi 	"25GBASE-CR CA-N",
134*45d3dd98SRobert Mustacchi 	"Reserved",
135*45d3dd98SRobert Mustacchi 	"Reserved",
136*45d3dd98SRobert Mustacchi 	"40GBASE-ER4",
137*45d3dd98SRobert Mustacchi 	"4 x 10GBASE-SR",
138*45d3dd98SRobert Mustacchi 	"40G PSM4 Parallel SMF",
139*45d3dd98SRobert Mustacchi 	"G959.1 profile P1I1-2D1",
140*45d3dd98SRobert Mustacchi 	"G959.1 profile P1S1-2D2",
141*45d3dd98SRobert Mustacchi 	"G959.1 profile P1L1-2D2",
142*45d3dd98SRobert Mustacchi 	"10GBASE-T with SFI electrical interface",
143*45d3dd98SRobert Mustacchi 	"100G CLR4",
144*45d3dd98SRobert Mustacchi 	"100G AOC or 25GAUI C2M AOC",
145*45d3dd98SRobert Mustacchi 	"100G ACC or 25GAUI C2M ACC",
146*45d3dd98SRobert Mustacchi 	"100GE-DWDM2"
147*45d3dd98SRobert Mustacchi };
148*45d3dd98SRobert Mustacchi 
149*45d3dd98SRobert Mustacchi typedef struct sff_pair {
150*45d3dd98SRobert Mustacchi 	uint_t sp_val;
151*45d3dd98SRobert Mustacchi 	const char *sp_name;
152*45d3dd98SRobert Mustacchi } sff_pair_t;
153*45d3dd98SRobert Mustacchi 
154*45d3dd98SRobert Mustacchi /*
155*45d3dd98SRobert Mustacchi  * This table is derived from SFF 8024 r3.9 Section 4.3.
156*45d3dd98SRobert Mustacchi  */
157*45d3dd98SRobert Mustacchi static sff_pair_t sff_8024_connectors[] = {
158*45d3dd98SRobert Mustacchi 	{ 0x00, "Unknown" },
159*45d3dd98SRobert Mustacchi 	{ 0x01, "SC (Subscriber Connector)" },
160*45d3dd98SRobert Mustacchi 	{ 0x02, "Fibre Channel Style 1 copper connector" },
161*45d3dd98SRobert Mustacchi 	{ 0x03, "Fibre Channel Style 2 copper connector" },
162*45d3dd98SRobert Mustacchi 	{ 0x04, "BNC/TNC (Bayonet/Threaded Neill-Concelman)" },
163*45d3dd98SRobert Mustacchi 	{ 0x05, "Fibre Channel coax headers" },
164*45d3dd98SRobert Mustacchi 	{ 0x06, "Fiber Jack" },
165*45d3dd98SRobert Mustacchi 	{ 0x07, "LC (Lucent Connector)" },
166*45d3dd98SRobert Mustacchi 	{ 0x08, "MT-RJ (Mechanical Transfer - Registered Jack)" },
167*45d3dd98SRobert Mustacchi 	{ 0x09, "MU (Multiple Optical)" },
168*45d3dd98SRobert Mustacchi 	{ 0x0A, "SG" },
169*45d3dd98SRobert Mustacchi 	{ 0x0B, "Optical Pigtail" },
170*45d3dd98SRobert Mustacchi 	{ 0x0C, "MPO 1x12 (Multifiber Parallel Optic)" },
171*45d3dd98SRobert Mustacchi 	{ 0x0D, "MPO 2x16" },
172*45d3dd98SRobert Mustacchi 	{ 0x20, "HSSDC II (High Speed Serial Data Connector)" },
173*45d3dd98SRobert Mustacchi 	{ 0x21, "Copper pigtail" },
174*45d3dd98SRobert Mustacchi 	{ 0x22, "RJ45 (Registered Jack)" },
175*45d3dd98SRobert Mustacchi 	{ 0x23, "No separable connector" },
176*45d3dd98SRobert Mustacchi 	{ 0x24, "MXC 2x16" },
177*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
178*45d3dd98SRobert Mustacchi };
179*45d3dd98SRobert Mustacchi 
180*45d3dd98SRobert Mustacchi /*
181*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
182*45d3dd98SRobert Mustacchi  */
183*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_10GETH_MASK	0xf0
184*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_10geth[] = {
185*45d3dd98SRobert Mustacchi 	{ 0x80, "10G Base-ER" },
186*45d3dd98SRobert Mustacchi 	{ 0x40, "10G Base-LRM" },
187*45d3dd98SRobert Mustacchi 	{ 0x20, "10G Base-LR" },
188*45d3dd98SRobert Mustacchi 	{ 0x10, "10G Base-SR" },
189*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
190*45d3dd98SRobert Mustacchi };
191*45d3dd98SRobert Mustacchi 
192*45d3dd98SRobert Mustacchi /*
193*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
194*45d3dd98SRobert Mustacchi  */
195*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_IB_MASK	0x0f
196*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_ib[] = {
197*45d3dd98SRobert Mustacchi 	{ 0x08, "1X SX" },
198*45d3dd98SRobert Mustacchi 	{ 0x04,	"1X LX" },
199*45d3dd98SRobert Mustacchi 	{ 0x02, "1X Copper Active" },
200*45d3dd98SRobert Mustacchi 	{ 0x01, "1X Copper Passive" },
201*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
202*45d3dd98SRobert Mustacchi };
203*45d3dd98SRobert Mustacchi 
204*45d3dd98SRobert Mustacchi /*
205*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
206*45d3dd98SRobert Mustacchi  */
207*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_ESCON_MASK	0xc0
208*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_escon[] = {
209*45d3dd98SRobert Mustacchi 	{ 0x80, "ESCON MMF, 1310nm LED" },
210*45d3dd98SRobert Mustacchi 	{ 0x40, "ESCON SMF, 1310nm Laser" },
211*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
212*45d3dd98SRobert Mustacchi };
213*45d3dd98SRobert Mustacchi 
214*45d3dd98SRobert Mustacchi /*
215*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.  These values come from both
216*45d3dd98SRobert Mustacchi  * bytes 4 and 5. We treat this as a uint16_t with the low byte as byte 4 and
217*45d3dd98SRobert Mustacchi  * the high byte as byte 5.
218*45d3dd98SRobert Mustacchi  */
219*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_SOCON_MASK	0x773f
220*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_sonet[] = {
221*45d3dd98SRobert Mustacchi 	{ 0x20, "OC-192, short reach" },
222*45d3dd98SRobert Mustacchi 	{ 0x10, "SONET reach specifier bit 1" },
223*45d3dd98SRobert Mustacchi 	{ 0x08, "ONET reach specifier bit 2" },
224*45d3dd98SRobert Mustacchi 	{ 0x04, "OC-48, long reach" },
225*45d3dd98SRobert Mustacchi 	{ 0x02, "OC-48, intermediate reach" },
226*45d3dd98SRobert Mustacchi 	{ 0x01, "OC-48, short reach" },
227*45d3dd98SRobert Mustacchi 	/* 0x8000 is unallocated */
228*45d3dd98SRobert Mustacchi 	{ 0x4000, "OC-12, single mode, long reach" },
229*45d3dd98SRobert Mustacchi 	{ 0x2000, "OC-12, single mode, inter. reach" },
230*45d3dd98SRobert Mustacchi 	{ 0x1000, "OC-12, short reach" },
231*45d3dd98SRobert Mustacchi 	/* 0x800 is unallocted */
232*45d3dd98SRobert Mustacchi 	{ 0x0400, "OC-3, single mode, long reach" },
233*45d3dd98SRobert Mustacchi 	{ 0x0200, "OC-3, single mode, inter. reach" },
234*45d3dd98SRobert Mustacchi 	{ 0x0100, "OC-3, short reach" },
235*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
236*45d3dd98SRobert Mustacchi };
237*45d3dd98SRobert Mustacchi 
238*45d3dd98SRobert Mustacchi /*
239*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
240*45d3dd98SRobert Mustacchi  */
241*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_ETH_MASK	0xff
242*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_eth[] = {
243*45d3dd98SRobert Mustacchi 	{ 0x80, "BASE-PX" },
244*45d3dd98SRobert Mustacchi 	{ 0x40, "BASE-BX10" },
245*45d3dd98SRobert Mustacchi 	{ 0x20, "100BASE-FX" },
246*45d3dd98SRobert Mustacchi 	{ 0x10, "100BASE-LX/LX10" },
247*45d3dd98SRobert Mustacchi 	{ 0x08, "1000BASE-T" },
248*45d3dd98SRobert Mustacchi 	{ 0x04, "1000BASE-CX" },
249*45d3dd98SRobert Mustacchi 	{ 0x02, "1000BASE-LX" },
250*45d3dd98SRobert Mustacchi 	{ 0x01, "1000BASE-SX" },
251*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
252*45d3dd98SRobert Mustacchi };
253*45d3dd98SRobert Mustacchi 
254*45d3dd98SRobert Mustacchi /*
255*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
256*45d3dd98SRobert Mustacchi  */
257*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_FCLEN_MASK	0xf8
258*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_fclen[] = {
259*45d3dd98SRobert Mustacchi 	{ 0x80, "very long distance (V)" },
260*45d3dd98SRobert Mustacchi 	{ 0x40, "short distance (S)" },
261*45d3dd98SRobert Mustacchi 	{ 0x20, "intermeddiate distance (I)" },
262*45d3dd98SRobert Mustacchi 	{ 0x10, "long distance (L)" },
263*45d3dd98SRobert Mustacchi 	{ 0x08, "medium distance (M)" },
264*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
265*45d3dd98SRobert Mustacchi };
266*45d3dd98SRobert Mustacchi 
267*45d3dd98SRobert Mustacchi /*
268*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.  These values come from both
269*45d3dd98SRobert Mustacchi  * bytes 7 and 8. We treat this as a uint16_t with the low byte as byte 7 and
270*45d3dd98SRobert Mustacchi  * the high byte as byte 8.
271*45d3dd98SRobert Mustacchi  */
272*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_TECH_MASK	0xf007
273*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_tech[] = {
274*45d3dd98SRobert Mustacchi 	{ 0x4, "Shortwave laser, linear Rx (SA)" },
275*45d3dd98SRobert Mustacchi 	{ 0x2, "Longwave laser (LC)" },
276*45d3dd98SRobert Mustacchi 	{ 0x1, "Electrical inter-enclosure (EL)" },
277*45d3dd98SRobert Mustacchi 	{ 0x8000, "Electrical intra-enclosure (EL)" },
278*45d3dd98SRobert Mustacchi 	{ 0x4000, "Shortwave laser w/o OFC (SN)" },
279*45d3dd98SRobert Mustacchi 	{ 0x2000, "Shortwave laser with OFC (SL)" },
280*45d3dd98SRobert Mustacchi 	{ 0x1000, "Longwave laser (LL)" },
281*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
282*45d3dd98SRobert Mustacchi };
283*45d3dd98SRobert Mustacchi 
284*45d3dd98SRobert Mustacchi /*
285*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
286*45d3dd98SRobert Mustacchi  */
287*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_CABLE_MASK	0x0c
288*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_CABLE_ACTIVE	0x08
289*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_CABLE_PASSIVE	0x04
290*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_cable[] = {
291*45d3dd98SRobert Mustacchi 	{ 0x08, "Active Cable" },
292*45d3dd98SRobert Mustacchi 	{ 0x04, "Passive Cable" },
293*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
294*45d3dd98SRobert Mustacchi };
295*45d3dd98SRobert Mustacchi 
296*45d3dd98SRobert Mustacchi /*
297*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
298*45d3dd98SRobert Mustacchi  */
299*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_MEDIA_MASK	0xfd
300*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_media[] = {
301*45d3dd98SRobert Mustacchi 	{ 0x80, "Twin Axial Pair (TW)" },
302*45d3dd98SRobert Mustacchi 	{ 0x40, "Twisted Pair (TP)" },
303*45d3dd98SRobert Mustacchi 	{ 0x20, "Miniature Coax (MI)" },
304*45d3dd98SRobert Mustacchi 	{ 0x10, "Video Coax (TV)" },
305*45d3dd98SRobert Mustacchi 	{ 0x08, "Multimode, 62.5um (M6)" },
306*45d3dd98SRobert Mustacchi 	{ 0x04, "Multimode, 50um (M5, M5E)" },
307*45d3dd98SRobert Mustacchi 	/* 0x02 is Unallocated */
308*45d3dd98SRobert Mustacchi 	{ 0x01, "Single Mode (SM)" },
309*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
310*45d3dd98SRobert Mustacchi };
311*45d3dd98SRobert Mustacchi 
312*45d3dd98SRobert Mustacchi /*
313*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 5-3.
314*45d3dd98SRobert Mustacchi  */
315*45d3dd98SRobert Mustacchi #define	SFF_8472_COMP_SPEED_MASK	0xfd
316*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_comp_speed[] = {
317*45d3dd98SRobert Mustacchi 	{ 0x80, "1200 MBytes/sec" },
318*45d3dd98SRobert Mustacchi 	{ 0x40, "800 MBytes/sec" },
319*45d3dd98SRobert Mustacchi 	{ 0x20, "1600 MBytes/sec" },
320*45d3dd98SRobert Mustacchi 	{ 0x10, "400 MBytes/sec" },
321*45d3dd98SRobert Mustacchi 	{ 0x08, "3200 MBytes/sec" },
322*45d3dd98SRobert Mustacchi 	{ 0x04, "200 MBytes/sec" },
323*45d3dd98SRobert Mustacchi 	/* 0x02 is Unallocated */
324*45d3dd98SRobert Mustacchi 	{ 0x01, "100 MBytes/sec" },
325*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
326*45d3dd98SRobert Mustacchi };
327*45d3dd98SRobert Mustacchi 
328*45d3dd98SRobert Mustacchi /*
329*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 8-1.
330*45d3dd98SRobert Mustacchi  * Note, only byte 60 is allocated at this time.
331*45d3dd98SRobert Mustacchi  */
332*45d3dd98SRobert Mustacchi #define	SFF_8472_PCABLE_COMP_MASK	0x3f
333*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_pcable_comp[] = {
334*45d3dd98SRobert Mustacchi 	{ 0x20, "Reserved for SFF-8461" },
335*45d3dd98SRobert Mustacchi 	{ 0x10, "Reserved for SFF-8461" },
336*45d3dd98SRobert Mustacchi 	{ 0x08, "Reserved for SFF-8461" },
337*45d3dd98SRobert Mustacchi 	{ 0x04, "Reserved for SFF-8461" },
338*45d3dd98SRobert Mustacchi 	{ 0x02, "Compliant to FC-PI-4 Appendix H" },
339*45d3dd98SRobert Mustacchi 	{ 0x01, "Compliant to SFF-8431 Appendix E" },
340*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
341*45d3dd98SRobert Mustacchi };
342*45d3dd98SRobert Mustacchi 
343*45d3dd98SRobert Mustacchi /*
344*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 8-2.
345*45d3dd98SRobert Mustacchi  * Note, only byte 60 is allocated at this time.
346*45d3dd98SRobert Mustacchi  */
347*45d3dd98SRobert Mustacchi #define	SFF_8472_ACABLE_COMP_MASK	0xf
348*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_acable_comp[] = {
349*45d3dd98SRobert Mustacchi 	{ 0x08, "Compliant to FC-PI-4 Limiting" },
350*45d3dd98SRobert Mustacchi 	{ 0x04, "Compliant to SFF-8431 Limiting" },
351*45d3dd98SRobert Mustacchi 	{ 0x02, "Compliant to FC-PI-4 Appendix H" },
352*45d3dd98SRobert Mustacchi 	{ 0x01, "Compliant to SFF-8431 Appendix" },
353*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
354*45d3dd98SRobert Mustacchi };
355*45d3dd98SRobert Mustacchi 
356*45d3dd98SRobert Mustacchi /*
357*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 8-3.
358*45d3dd98SRobert Mustacchi  * Note that we combined byte 64 and 65. Byte 64 is the upper bit.
359*45d3dd98SRobert Mustacchi  */
360*45d3dd98SRobert Mustacchi #define	SFF_8472_OPTION_MASK	0x3ffe
361*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_options[] = {
362*45d3dd98SRobert Mustacchi 	{ 0x2000, "Power Level 3 Requirement"},
363*45d3dd98SRobert Mustacchi 	{ 0x1000, "Paging Implemented"},
364*45d3dd98SRobert Mustacchi 	{ 0x0800, "Retimer or CDR implemented"},
365*45d3dd98SRobert Mustacchi 	{ 0x0400, "Cooled Transceiver Implemented"},
366*45d3dd98SRobert Mustacchi 	{ 0x0200, "Power Level 2 Requirement"},
367*45d3dd98SRobert Mustacchi 	{ 0x0100, "Linear Receiver Output Implemented"},
368*45d3dd98SRobert Mustacchi 	{ 0x0080, "Receiver decision threshold implemented"},
369*45d3dd98SRobert Mustacchi 	{ 0x0040, "Tunable transmitter"},
370*45d3dd98SRobert Mustacchi 	{ 0x0020, "RATE_SELECT implemented"},
371*45d3dd98SRobert Mustacchi 	{ 0x0010, "TX_DISABLE implemented"},
372*45d3dd98SRobert Mustacchi 	{ 0x0008, "TX_FAULT implemented"},
373*45d3dd98SRobert Mustacchi 	{ 0x0004, "Rx_LOS inverted"},
374*45d3dd98SRobert Mustacchi 	{ 0x0002, "Rx_LOS implemented"},
375*45d3dd98SRobert Mustacchi };
376*45d3dd98SRobert Mustacchi 
377*45d3dd98SRobert Mustacchi /*
378*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 8-6.
379*45d3dd98SRobert Mustacchi  */
380*45d3dd98SRobert Mustacchi #define	SFF_8472_EXTOPT_MASK	0xfe
381*45d3dd98SRobert Mustacchi static sff_pair_t sff_8472_extopts[] = {
382*45d3dd98SRobert Mustacchi 	{ 0x80, "Alarm/Warning flags implemented" },
383*45d3dd98SRobert Mustacchi 	{ 0x40, "Soft TX_DISABLE implemented" },
384*45d3dd98SRobert Mustacchi 	{ 0x20, "Soft TX_FAULT implemented" },
385*45d3dd98SRobert Mustacchi 	{ 0x10, "Soft RX_LOS implemented" },
386*45d3dd98SRobert Mustacchi 	{ 0x08, "Soft RATE_SELECT implemented" },
387*45d3dd98SRobert Mustacchi 	{ 0x04, "Application Select implemented" },
388*45d3dd98SRobert Mustacchi 	{ 0x02, "Soft Rate Select Control Implemented" },
389*45d3dd98SRobert Mustacchi 	{ 0x01, "" },
390*45d3dd98SRobert Mustacchi };
391*45d3dd98SRobert Mustacchi 
392*45d3dd98SRobert Mustacchi /*
393*45d3dd98SRobert Mustacchi  * This is derived from SFF 8472 r12.2 Table 8-8.
394*45d3dd98SRobert Mustacchi  */
395*45d3dd98SRobert Mustacchi #define	SFF_8472_8472_COMP_NENTRIES 9
396*45d3dd98SRobert Mustacchi static const char *sff_8472_8472_comp[] = {
397*45d3dd98SRobert Mustacchi 	"Not compliant",
398*45d3dd98SRobert Mustacchi 	"Rev 9.3",
399*45d3dd98SRobert Mustacchi 	"Rev 9.5",
400*45d3dd98SRobert Mustacchi 	"Rev 10.2",
401*45d3dd98SRobert Mustacchi 	"Rev 10.4",
402*45d3dd98SRobert Mustacchi 	"Rev 11.0",
403*45d3dd98SRobert Mustacchi 	"Rev 11.3",
404*45d3dd98SRobert Mustacchi 	"Rev 11.4",
405*45d3dd98SRobert Mustacchi 	"Rev 12.0"
406*45d3dd98SRobert Mustacchi };
407*45d3dd98SRobert Mustacchi 
408*45d3dd98SRobert Mustacchi /*
409*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
410*45d3dd98SRobert Mustacchi  */
411*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_10GETH_MASK 0x7f
412*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_10geth[] = {
413*45d3dd98SRobert Mustacchi 	{ 0x40, "10GBASE-LRM" },
414*45d3dd98SRobert Mustacchi 	{ 0x20, "10GBASE-LR" },
415*45d3dd98SRobert Mustacchi 	{ 0x10, "10GBASE-SR" },
416*45d3dd98SRobert Mustacchi 	{ 0x08, "40GBASE-CR4" },
417*45d3dd98SRobert Mustacchi 	{ 0x04, "40GBASE-SR4" },
418*45d3dd98SRobert Mustacchi 	{ 0x02, "40GBASE-LR4" },
419*45d3dd98SRobert Mustacchi 	{ 0x01, "40G Active Cable (XLPPI)" },
420*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
421*45d3dd98SRobert Mustacchi };
422*45d3dd98SRobert Mustacchi 
423*45d3dd98SRobert Mustacchi /*
424*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
425*45d3dd98SRobert Mustacchi  */
426*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_SONET_MASK 0x07
427*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_sonet[] = {
428*45d3dd98SRobert Mustacchi 	{ 0x04, "OC 48, long reach" },
429*45d3dd98SRobert Mustacchi 	{ 0x02, "OC 48, intermediate reach" },
430*45d3dd98SRobert Mustacchi 	{ 0x01, "OC 48 short reach" },
431*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
432*45d3dd98SRobert Mustacchi };
433*45d3dd98SRobert Mustacchi 
434*45d3dd98SRobert Mustacchi /*
435*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
436*45d3dd98SRobert Mustacchi  */
437*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_SAS_MASK	0xf0
438*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_sas[] = {
439*45d3dd98SRobert Mustacchi 	{ 0x80, "SAS 24.0 Gb/s" },
440*45d3dd98SRobert Mustacchi 	{ 0x40, "SAS 12.0 Gb/s" },
441*45d3dd98SRobert Mustacchi 	{ 0x20, "SAS 6.0 Gb/s" },
442*45d3dd98SRobert Mustacchi 	{ 0x10, "SAS 3.0 Gb/s" },
443*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
444*45d3dd98SRobert Mustacchi };
445*45d3dd98SRobert Mustacchi 
446*45d3dd98SRobert Mustacchi /*
447*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
448*45d3dd98SRobert Mustacchi  */
449*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_ETH_MASK	0x0f
450*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_eth[] = {
451*45d3dd98SRobert Mustacchi 	{ 0x08, "1000BASE-T" },
452*45d3dd98SRobert Mustacchi 	{ 0x04, "1000BASE-CX" },
453*45d3dd98SRobert Mustacchi 	{ 0x02, "1000BASE-LX" },
454*45d3dd98SRobert Mustacchi 	{ 0x01, "1000BASE-SX" },
455*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
456*45d3dd98SRobert Mustacchi };
457*45d3dd98SRobert Mustacchi 
458*45d3dd98SRobert Mustacchi /*
459*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
460*45d3dd98SRobert Mustacchi  */
461*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_FCLEN_MASK	0xf8
462*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_fclen[] = {
463*45d3dd98SRobert Mustacchi 	{ 0x80, "very long distance (V)" },
464*45d3dd98SRobert Mustacchi 	{ 0x40, "short distance (S)" },
465*45d3dd98SRobert Mustacchi 	{ 0x20, "intermeddiate distance (I)" },
466*45d3dd98SRobert Mustacchi 	{ 0x10, "long distance (L)" },
467*45d3dd98SRobert Mustacchi 	{ 0x08, "medium distance (M)" },
468*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
469*45d3dd98SRobert Mustacchi };
470*45d3dd98SRobert Mustacchi 
471*45d3dd98SRobert Mustacchi /*
472*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
473*45d3dd98SRobert Mustacchi  */
474*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_TECH_MASK	0xf003
475*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_tech[] = {
476*45d3dd98SRobert Mustacchi 	{ 0x2, "Longwave laser (LC)" },
477*45d3dd98SRobert Mustacchi 	{ 0x1, "Electrical inter-enclosure (EL)" },
478*45d3dd98SRobert Mustacchi 	{ 0x8000, "Electrical intra-enclosure (EL)" },
479*45d3dd98SRobert Mustacchi 	{ 0x4000, "Shortwave laser w/o OFC (SN)" },
480*45d3dd98SRobert Mustacchi 	{ 0x2000, "Shortwave laser with OFC (SL)" },
481*45d3dd98SRobert Mustacchi 	{ 0x1000, "Longwave laser (LL)" },
482*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
483*45d3dd98SRobert Mustacchi };
484*45d3dd98SRobert Mustacchi 
485*45d3dd98SRobert Mustacchi /*
486*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
487*45d3dd98SRobert Mustacchi  */
488*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_MEDIA_MASK	0xff
489*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_media[] = {
490*45d3dd98SRobert Mustacchi 	{ 0x80, "Twin Axial Pair (TW)" },
491*45d3dd98SRobert Mustacchi 	{ 0x40, "Twisted Pair (TP)" },
492*45d3dd98SRobert Mustacchi 	{ 0x20, "Miniature Coax (MI)" },
493*45d3dd98SRobert Mustacchi 	{ 0x10, "Video Coax (TV)" },
494*45d3dd98SRobert Mustacchi 	{ 0x08, "Multimode, 62.5um (M6)" },
495*45d3dd98SRobert Mustacchi 	{ 0x04, "Multimode, 50m (M5)" },
496*45d3dd98SRobert Mustacchi 	{ 0x02, "Multimode, 50um (OM3)" },
497*45d3dd98SRobert Mustacchi 	{ 0x01, "Single Mode (SM)" },
498*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
499*45d3dd98SRobert Mustacchi };
500*45d3dd98SRobert Mustacchi 
501*45d3dd98SRobert Mustacchi /*
502*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-17.
503*45d3dd98SRobert Mustacchi  */
504*45d3dd98SRobert Mustacchi #define	SFF_8636_COMP_SPEED_MASK	0xfd
505*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_comp_speed[] = {
506*45d3dd98SRobert Mustacchi 	{ 0x80, "1200 MBytes/sec" },
507*45d3dd98SRobert Mustacchi 	{ 0x40, "800 MBytes/sec" },
508*45d3dd98SRobert Mustacchi 	{ 0x20, "1600 MBytes/sec" },
509*45d3dd98SRobert Mustacchi 	{ 0x10, "400 MBytes/sec" },
510*45d3dd98SRobert Mustacchi 	{ 0x08, "3200 MBytes/sec" },
511*45d3dd98SRobert Mustacchi 	{ 0x04, "200 MBytes/sec" },
512*45d3dd98SRobert Mustacchi 	{ 0x01, "100 MBytes/sec" },
513*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
514*45d3dd98SRobert Mustacchi };
515*45d3dd98SRobert Mustacchi 
516*45d3dd98SRobert Mustacchi /*
517*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-20.
518*45d3dd98SRobert Mustacchi  */
519*45d3dd98SRobert Mustacchi static const char *sff_8636_trans_tech[] = {
520*45d3dd98SRobert Mustacchi 	"850 nm VCSEL",
521*45d3dd98SRobert Mustacchi 	"1310 nm VCSEL",
522*45d3dd98SRobert Mustacchi 	"1550 nm VCSEL",
523*45d3dd98SRobert Mustacchi 	"1310 nm FP",
524*45d3dd98SRobert Mustacchi 	"1310 nm DFB",
525*45d3dd98SRobert Mustacchi 	"1550 nm DFB",
526*45d3dd98SRobert Mustacchi 	"1310 nm EML",
527*45d3dd98SRobert Mustacchi 	"1550 nm EML",
528*45d3dd98SRobert Mustacchi 	"Other / Undefined",
529*45d3dd98SRobert Mustacchi 	"1490 nm DFB",
530*45d3dd98SRobert Mustacchi 	"Copper cable unequalized",
531*45d3dd98SRobert Mustacchi 	"Copper cable passive equalized",
532*45d3dd98SRobert Mustacchi 	"Copper cable, near and far end limiting active equalizers",
533*45d3dd98SRobert Mustacchi 	"Copper cable, far end limiting active equalizers",
534*45d3dd98SRobert Mustacchi 	"Copper cable, near end limiting active equalizers",
535*45d3dd98SRobert Mustacchi 	"Copper cable, linear active equalizers"
536*45d3dd98SRobert Mustacchi };
537*45d3dd98SRobert Mustacchi 
538*45d3dd98SRobert Mustacchi /*
539*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-21.
540*45d3dd98SRobert Mustacchi  */
541*45d3dd98SRobert Mustacchi #define	SFF_8636_EXTMOD_CODES	0x1f
542*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_extmod_codes[] = {
543*45d3dd98SRobert Mustacchi 	{ 0x10, "EDR" },
544*45d3dd98SRobert Mustacchi 	{ 0x08, "FDR" },
545*45d3dd98SRobert Mustacchi 	{ 0x04, "QDR" },
546*45d3dd98SRobert Mustacchi 	{ 0x02, "DDR" },
547*45d3dd98SRobert Mustacchi 	{ 0x01, "SDR" },
548*45d3dd98SRobert Mustacchi 	{ 0x00, NULL }
549*45d3dd98SRobert Mustacchi };
550*45d3dd98SRobert Mustacchi 
551*45d3dd98SRobert Mustacchi /*
552*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-22. This combines bytes 193-195.
553*45d3dd98SRobert Mustacchi  * We treat byte 193 as the most significant.
554*45d3dd98SRobert Mustacchi  */
555*45d3dd98SRobert Mustacchi #define	SFF_8636_OPTION_MASK	0x0ffffe
556*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_options[] = {
557*45d3dd98SRobert Mustacchi 	{ 0x080000, "TX Input Equalization Auto Adaptive Capable" },
558*45d3dd98SRobert Mustacchi 	{ 0x040000, "TX Input Equalization Fixed Programmable" },
559*45d3dd98SRobert Mustacchi 	{ 0x020000, "RX Output Emphasis Fixed Programmable Settings" },
560*45d3dd98SRobert Mustacchi 	{ 0x010000, "RX Output Amplitude Fixed Programmable Settings" },
561*45d3dd98SRobert Mustacchi 	{ 0x008000, "TX CDR On/Off Control implemented" },
562*45d3dd98SRobert Mustacchi 	{ 0x004000, "RX CDR On/Off Control implemented" },
563*45d3dd98SRobert Mustacchi 	{ 0x002000, "Tx CDR Loss of Lock Flag implemented" },
564*45d3dd98SRobert Mustacchi 	{ 0x001000, "Rx CDR Loss of Lock Flag implemented" },
565*45d3dd98SRobert Mustacchi 	{ 0x000800, "Rx Squelch Disable implemented" },
566*45d3dd98SRobert Mustacchi 	{ 0x000400, "Rx Output Disable capable" },
567*45d3dd98SRobert Mustacchi 	{ 0x000200, "Tx Squelch Disable implemented" },
568*45d3dd98SRobert Mustacchi 	{ 0x000100, "Tx Squelch implemented" },
569*45d3dd98SRobert Mustacchi 	{ 0x000080, "Memory page 02h provided" },
570*45d3dd98SRobert Mustacchi 	{ 0x000040, "Memory page 01h provided" },
571*45d3dd98SRobert Mustacchi 	{ 0x000020, "Rate Select implemented" },
572*45d3dd98SRobert Mustacchi 	{ 0x000010, "Tx_DISABLE implemented" },
573*45d3dd98SRobert Mustacchi 	{ 0x000008, "Tx_FAULT implemented" },
574*45d3dd98SRobert Mustacchi 	{ 0x000004, "Tx Squelch for Pave" },
575*45d3dd98SRobert Mustacchi 	{ 0x000002, "Tx Loss of Signal implemented" },
576*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
577*45d3dd98SRobert Mustacchi };
578*45d3dd98SRobert Mustacchi 
579*45d3dd98SRobert Mustacchi /*
580*45d3dd98SRobert Mustacchi  * This is derived from SFF 8636 r2.7 Table 6-25.
581*45d3dd98SRobert Mustacchi  */
582*45d3dd98SRobert Mustacchi #define	SFF_8636_ENHANCED_OPTIONS_MASK	0x1c
583*45d3dd98SRobert Mustacchi static sff_pair_t sff_8636_eopt[] = {
584*45d3dd98SRobert Mustacchi 	{ 0x10, "Initialization Complete Flag Implemented" },
585*45d3dd98SRobert Mustacchi 	{ 0x08, "Extended Rate Selection Supported" },
586*45d3dd98SRobert Mustacchi 	{ 0x04, "Application Select Table Supported" },
587*45d3dd98SRobert Mustacchi 	{ 0x0, NULL }
588*45d3dd98SRobert Mustacchi };
589*45d3dd98SRobert Mustacchi 
590*45d3dd98SRobert Mustacchi static const char *
sff_pair_find(uint_t val,sff_pair_t * pairs)591*45d3dd98SRobert Mustacchi sff_pair_find(uint_t val, sff_pair_t *pairs)
592*45d3dd98SRobert Mustacchi {
593*45d3dd98SRobert Mustacchi 	while (pairs->sp_name != NULL) {
594*45d3dd98SRobert Mustacchi 		if (val == pairs->sp_val)
595*45d3dd98SRobert Mustacchi 			return (pairs->sp_name);
596*45d3dd98SRobert Mustacchi 		pairs++;
597*45d3dd98SRobert Mustacchi 	}
598*45d3dd98SRobert Mustacchi 
599*45d3dd98SRobert Mustacchi 	return (NULL);
600*45d3dd98SRobert Mustacchi }
601*45d3dd98SRobert Mustacchi 
602*45d3dd98SRobert Mustacchi static int
sff_parse_id(uint8_t id,nvlist_t * nvl)603*45d3dd98SRobert Mustacchi sff_parse_id(uint8_t id, nvlist_t *nvl)
604*45d3dd98SRobert Mustacchi {
605*45d3dd98SRobert Mustacchi 	const char *val;
606*45d3dd98SRobert Mustacchi 
607*45d3dd98SRobert Mustacchi 	if (id >= SFF_8024_VENDOR) {
608*45d3dd98SRobert Mustacchi 		val = "Vendor Specific";
609*45d3dd98SRobert Mustacchi 	} else if (id >= SFF_8024_NIDS) {
610*45d3dd98SRobert Mustacchi 		val = "Reserved";
611*45d3dd98SRobert Mustacchi 	} else {
612*45d3dd98SRobert Mustacchi 		val = sff_8024_id_strs[id];
613*45d3dd98SRobert Mustacchi 	}
614*45d3dd98SRobert Mustacchi 
615*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, LIBSFF_KEY_IDENTIFIER, val));
616*45d3dd98SRobert Mustacchi }
617*45d3dd98SRobert Mustacchi 
618*45d3dd98SRobert Mustacchi static int
sff_add_unit_string(uint64_t val,uint64_t factor,const char * unit,nvlist_t * nvl,const char * key)619*45d3dd98SRobert Mustacchi sff_add_unit_string(uint64_t val, uint64_t factor, const char *unit,
620*45d3dd98SRobert Mustacchi     nvlist_t *nvl, const char *key)
621*45d3dd98SRobert Mustacchi {
622*45d3dd98SRobert Mustacchi 	char str[SFP_STRBUF];
623*45d3dd98SRobert Mustacchi 
624*45d3dd98SRobert Mustacchi 	val *= factor;
625*45d3dd98SRobert Mustacchi 	(void) snprintf(str, sizeof (str), "%" PRIu64 " %s", val, unit);
626*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, key, str));
627*45d3dd98SRobert Mustacchi }
628*45d3dd98SRobert Mustacchi 
629*45d3dd98SRobert Mustacchi static int
sff_parse_connector(uint8_t con,nvlist_t * nvl)630*45d3dd98SRobert Mustacchi sff_parse_connector(uint8_t con, nvlist_t *nvl)
631*45d3dd98SRobert Mustacchi {
632*45d3dd98SRobert Mustacchi 	const char *val;
633*45d3dd98SRobert Mustacchi 
634*45d3dd98SRobert Mustacchi 	if (con >= 0x80) {
635*45d3dd98SRobert Mustacchi 		val = "Vendor Specific";
636*45d3dd98SRobert Mustacchi 	} else {
637*45d3dd98SRobert Mustacchi 		if ((val = sff_pair_find(con, sff_8024_connectors)) == NULL)
638*45d3dd98SRobert Mustacchi 			val = "Reserved";
639*45d3dd98SRobert Mustacchi 	}
640*45d3dd98SRobert Mustacchi 
641*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, LIBSFF_KEY_CONNECTOR, val));
642*45d3dd98SRobert Mustacchi }
643*45d3dd98SRobert Mustacchi 
644*45d3dd98SRobert Mustacchi /*
645*45d3dd98SRobert Mustacchi  * Many of the values in the specifications are bitfields of which one or more
646*45d3dd98SRobert Mustacchi  * bits may be set. We represent that as an array of strings. One entry will be
647*45d3dd98SRobert Mustacchi  * added for each set bit that's found in pairs.
648*45d3dd98SRobert Mustacchi  */
649*45d3dd98SRobert Mustacchi static int
sff_gather_bitfield(uint32_t value,const char * name,sff_pair_t * pairs,nvlist_t * nvl)650*45d3dd98SRobert Mustacchi sff_gather_bitfield(uint32_t value, const char *name, sff_pair_t *pairs,
651*45d3dd98SRobert Mustacchi     nvlist_t *nvl)
652*45d3dd98SRobert Mustacchi {
653*45d3dd98SRobert Mustacchi 	uint32_t i;
654*45d3dd98SRobert Mustacchi 	const char *vals[32];
655*45d3dd98SRobert Mustacchi 	uint_t count;
656*45d3dd98SRobert Mustacchi 
657*45d3dd98SRobert Mustacchi 	count = 0;
658*45d3dd98SRobert Mustacchi 	for (i = 0; i < 32; i++) {
659*45d3dd98SRobert Mustacchi 		uint32_t bit;
660*45d3dd98SRobert Mustacchi 		const char *str;
661*45d3dd98SRobert Mustacchi 
662*45d3dd98SRobert Mustacchi 		bit = 1 << i;
663*45d3dd98SRobert Mustacchi 		if ((bit & value) == 0)
664*45d3dd98SRobert Mustacchi 			continue;
665*45d3dd98SRobert Mustacchi 
666*45d3dd98SRobert Mustacchi 		str = sff_pair_find(bit, pairs);
667*45d3dd98SRobert Mustacchi 		if (str != NULL) {
668*45d3dd98SRobert Mustacchi 			vals[count++] = str;
669*45d3dd98SRobert Mustacchi 		}
670*45d3dd98SRobert Mustacchi 	}
671*45d3dd98SRobert Mustacchi 
672*45d3dd98SRobert Mustacchi 	if (count == 0)
673*45d3dd98SRobert Mustacchi 		return (0);
674*45d3dd98SRobert Mustacchi 
675*45d3dd98SRobert Mustacchi 	/*
676*45d3dd98SRobert Mustacchi 	 * The nvlist routines don't touch the array, so we end up lying about
677*45d3dd98SRobert Mustacchi 	 * the type of data so that we can avoid a rash of additional
678*45d3dd98SRobert Mustacchi 	 * allocations and strdups.
679*45d3dd98SRobert Mustacchi 	 */
680*45d3dd98SRobert Mustacchi 	return (nvlist_add_string_array(nvl, name, (char **)vals, count));
681*45d3dd98SRobert Mustacchi }
682*45d3dd98SRobert Mustacchi 
683*45d3dd98SRobert Mustacchi static int
sff_parse_compliance(const uint8_t * buf,nvlist_t * nvl)684*45d3dd98SRobert Mustacchi sff_parse_compliance(const uint8_t *buf, nvlist_t *nvl)
685*45d3dd98SRobert Mustacchi {
686*45d3dd98SRobert Mustacchi 	int ret;
687*45d3dd98SRobert Mustacchi 	uint16_t v;
688*45d3dd98SRobert Mustacchi 
689*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_10GE] &
690*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_10GETH_MASK, LIBSFF_KEY_COMPLIANCE_10GBE,
691*45d3dd98SRobert Mustacchi 	    sff_8472_comp_10geth, nvl)) != 0)
692*45d3dd98SRobert Mustacchi 		return (ret);
693*45d3dd98SRobert Mustacchi 
694*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_IB] &
695*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_IB_MASK, LIBSFF_KEY_COMPLIANCE_IB,
696*45d3dd98SRobert Mustacchi 	    sff_8472_comp_ib, nvl)) != 0)
697*45d3dd98SRobert Mustacchi 		return (ret);
698*45d3dd98SRobert Mustacchi 
699*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_ESCON] &
700*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_ESCON_MASK, LIBSFF_KEY_COMPLIANCE_ESCON,
701*45d3dd98SRobert Mustacchi 	    sff_8472_comp_escon, nvl)) != 0)
702*45d3dd98SRobert Mustacchi 		return (ret);
703*45d3dd98SRobert Mustacchi 
704*45d3dd98SRobert Mustacchi 	v = buf[SFF_8472_COMPLIANCE_SONET_LOW] |
705*45d3dd98SRobert Mustacchi 	    (buf[SFF_8472_COMPLIANCE_SONET_HIGH] << 8);
706*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(v & SFF_8472_COMP_SOCON_MASK,
707*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_COMPLIANCE_SONET, sff_8472_comp_sonet, nvl)) != 0)
708*45d3dd98SRobert Mustacchi 		return (ret);
709*45d3dd98SRobert Mustacchi 
710*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_ETHERNET] &
711*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_ETH_MASK, LIBSFF_KEY_COMPLIANCE_GBE,
712*45d3dd98SRobert Mustacchi 	    sff_8472_comp_eth, nvl)) != 0)
713*45d3dd98SRobert Mustacchi 		return (ret);
714*45d3dd98SRobert Mustacchi 
715*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FCLEN] &
716*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_FCLEN_MASK, LIBSFF_KEY_COMPLIANCE_FC_LEN,
717*45d3dd98SRobert Mustacchi 	    sff_8472_comp_fclen, nvl)) != 0)
718*45d3dd98SRobert Mustacchi 		return (ret);
719*45d3dd98SRobert Mustacchi 
720*45d3dd98SRobert Mustacchi 	v = buf[SFF_8472_COMPLIANCE_FC_LOW] |
721*45d3dd98SRobert Mustacchi 	    (buf[SFF_8472_COMPLIANCE_FC_HIGH] << 8);
722*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(v & SFF_8472_COMP_TECH_MASK,
723*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_COMPLIANCE_FC_TECH, sff_8472_comp_tech, nvl)) != 0)
724*45d3dd98SRobert Mustacchi 		return (ret);
725*45d3dd98SRobert Mustacchi 
726*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_SFP] &
727*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_CABLE_MASK, LIBSFF_KEY_COMPLIANCE_SFP,
728*45d3dd98SRobert Mustacchi 	    sff_8472_comp_cable, nvl)) != 0)
729*45d3dd98SRobert Mustacchi 		return (ret);
730*45d3dd98SRobert Mustacchi 
731*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FC_MEDIA] &
732*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_MEDIA_MASK, LIBSFF_KEY_COMPLIANCE_FC_MEDIA,
733*45d3dd98SRobert Mustacchi 	    sff_8472_comp_media, nvl)) != 0)
734*45d3dd98SRobert Mustacchi 		return (ret);
735*45d3dd98SRobert Mustacchi 
736*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_COMPLIANCE_FC_SPEED] &
737*45d3dd98SRobert Mustacchi 	    SFF_8472_COMP_SPEED_MASK, LIBSFF_KEY_COMPLIANCE_FC_SPEED,
738*45d3dd98SRobert Mustacchi 	    sff_8472_comp_speed, nvl)) != 0)
739*45d3dd98SRobert Mustacchi 		return (ret);
740*45d3dd98SRobert Mustacchi 
741*45d3dd98SRobert Mustacchi 	return (0);
742*45d3dd98SRobert Mustacchi }
743*45d3dd98SRobert Mustacchi 
744*45d3dd98SRobert Mustacchi static int
sff_parse_encoding(uint8_t val,nvlist_t * nvl,boolean_t sfp)745*45d3dd98SRobert Mustacchi sff_parse_encoding(uint8_t val, nvlist_t *nvl, boolean_t sfp)
746*45d3dd98SRobert Mustacchi {
747*45d3dd98SRobert Mustacchi 	const char *str;
748*45d3dd98SRobert Mustacchi 	if (val >= SFF_8024_NENCS) {
749*45d3dd98SRobert Mustacchi 		str = "Reserved";
750*45d3dd98SRobert Mustacchi 	} else if (sfp) {
751*45d3dd98SRobert Mustacchi 		str = sff_8024_enc_sfp[val];
752*45d3dd98SRobert Mustacchi 	} else {
753*45d3dd98SRobert Mustacchi 		str = sff_8024_enc_qsfp[val];
754*45d3dd98SRobert Mustacchi 	}
755*45d3dd98SRobert Mustacchi 
756*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, LIBSFF_KEY_ENCODING, str));
757*45d3dd98SRobert Mustacchi }
758*45d3dd98SRobert Mustacchi 
759*45d3dd98SRobert Mustacchi static int
sff_parse_br(const uint8_t * buf,nvlist_t * nvl)760*45d3dd98SRobert Mustacchi sff_parse_br(const uint8_t *buf, nvlist_t *nvl)
761*45d3dd98SRobert Mustacchi {
762*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_BR_NOMINAL] == 0xff) {
763*45d3dd98SRobert Mustacchi 		int ret;
764*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_BR_MAX],
765*45d3dd98SRobert Mustacchi 		    SFF_8472_BR_MAX_FACTOR, "MBd", nvl,
766*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_BR_MAX)) != 0)
767*45d3dd98SRobert Mustacchi 			return (ret);
768*45d3dd98SRobert Mustacchi 		return (sff_add_unit_string(buf[SFF_8472_BR_MIN],
769*45d3dd98SRobert Mustacchi 		    SFF_8472_BR_MIN_FACTOR, "MBd", nvl, LIBSFF_KEY_BR_MIN));
770*45d3dd98SRobert Mustacchi 	} else {
771*45d3dd98SRobert Mustacchi 		return (sff_add_unit_string(buf[SFF_8472_BR_NOMINAL],
772*45d3dd98SRobert Mustacchi 		    SFF_8472_BR_NOMINAL_FACTOR, "MBd", nvl,
773*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_BR_NOMINAL));
774*45d3dd98SRobert Mustacchi 	}
775*45d3dd98SRobert Mustacchi }
776*45d3dd98SRobert Mustacchi 
777*45d3dd98SRobert Mustacchi static int
sff_parse_lengths(const uint8_t * buf,nvlist_t * nvl)778*45d3dd98SRobert Mustacchi sff_parse_lengths(const uint8_t *buf, nvlist_t *nvl)
779*45d3dd98SRobert Mustacchi {
780*45d3dd98SRobert Mustacchi 	int ret;
781*45d3dd98SRobert Mustacchi 
782*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_SMF_KM] != 0) {
783*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_SMF_KM],
784*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_SMF_KM_FACTOR, "km", nvl,
785*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_SMF_KM)) != 0)
786*45d3dd98SRobert Mustacchi 			return (ret);
787*45d3dd98SRobert Mustacchi 	}
788*45d3dd98SRobert Mustacchi 
789*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_SMF] != 0) {
790*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_SMF],
791*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_SMF_FACTOR, "m", nvl,
792*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_SMF)) != 0)
793*45d3dd98SRobert Mustacchi 			return (ret);
794*45d3dd98SRobert Mustacchi 	}
795*45d3dd98SRobert Mustacchi 
796*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_50UM] != 0) {
797*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_50UM],
798*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_50UM_FACTOR, "m", nvl,
799*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM2)) != 0)
800*45d3dd98SRobert Mustacchi 			return (ret);
801*45d3dd98SRobert Mustacchi 	}
802*45d3dd98SRobert Mustacchi 
803*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_62UM] != 0) {
804*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_62UM],
805*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_62UM_FACTOR, "m", nvl,
806*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM1)) != 0)
807*45d3dd98SRobert Mustacchi 			return (ret);
808*45d3dd98SRobert Mustacchi 	}
809*45d3dd98SRobert Mustacchi 
810*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_COPPER] != 0) {
811*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_COPPER],
812*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_COPPER_FACTOR, "m", nvl,
813*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_COPPER)) != 0)
814*45d3dd98SRobert Mustacchi 			return (ret);
815*45d3dd98SRobert Mustacchi 	}
816*45d3dd98SRobert Mustacchi 
817*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_LENGTH_OM3] != 0) {
818*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8472_LENGTH_OM3],
819*45d3dd98SRobert Mustacchi 		    SFF_8472_LENGTH_OM3_FACTOR, "m", nvl,
820*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM3)) != 0)
821*45d3dd98SRobert Mustacchi 			return (ret);
822*45d3dd98SRobert Mustacchi 	}
823*45d3dd98SRobert Mustacchi 
824*45d3dd98SRobert Mustacchi 	return (0);
825*45d3dd98SRobert Mustacchi }
826*45d3dd98SRobert Mustacchi 
827*45d3dd98SRobert Mustacchi /*
828*45d3dd98SRobert Mustacchi  * Strings in the SFF specification are written into fixed sized buffers. The
829*45d3dd98SRobert Mustacchi  * strings are padded to the right with spaces (ASCII 0x20) and there is no NUL
830*45d3dd98SRobert Mustacchi  * character like in a standard C string. While the string is padded with
831*45d3dd98SRobert Mustacchi  * spaces, spaces may appear in the middle of the string and should not be
832*45d3dd98SRobert Mustacchi  * confused as padding.
833*45d3dd98SRobert Mustacchi  */
834*45d3dd98SRobert Mustacchi static int
sff_parse_string(const uint8_t * buf,uint_t start,uint_t len,const char * field,nvlist_t * nvl)835*45d3dd98SRobert Mustacchi sff_parse_string(const uint8_t *buf, uint_t start, uint_t len,
836*45d3dd98SRobert Mustacchi     const char *field, nvlist_t *nvl)
837*45d3dd98SRobert Mustacchi {
838*45d3dd98SRobert Mustacchi 	uint_t i;
839*45d3dd98SRobert Mustacchi 	char strbuf[SFP_STRBUF];
840*45d3dd98SRobert Mustacchi 
841*45d3dd98SRobert Mustacchi 	assert(len < sizeof (strbuf));
842*45d3dd98SRobert Mustacchi 	strbuf[0] = '\0';
843*45d3dd98SRobert Mustacchi 	while (len > 0) {
844*45d3dd98SRobert Mustacchi 		if (buf[start + len - 1] != ' ')
845*45d3dd98SRobert Mustacchi 			break;
846*45d3dd98SRobert Mustacchi 		len--;
847*45d3dd98SRobert Mustacchi 	}
848*45d3dd98SRobert Mustacchi 	if (len == 0)
849*45d3dd98SRobert Mustacchi 		return (0);
850*45d3dd98SRobert Mustacchi 
851*45d3dd98SRobert Mustacchi 	/*
852*45d3dd98SRobert Mustacchi 	 * This is supposed to be 7-bit printable ASCII. If we find any
853*45d3dd98SRobert Mustacchi 	 * characters that aren't, don't include this string.
854*45d3dd98SRobert Mustacchi 	 */
855*45d3dd98SRobert Mustacchi 	for (i = 0; i < len; i++) {
856*45d3dd98SRobert Mustacchi 		if (isascii(buf[start + i]) == 0 ||
857*45d3dd98SRobert Mustacchi 		    isprint(buf[start + i]) == 0) {
858*45d3dd98SRobert Mustacchi 			return (0);
859*45d3dd98SRobert Mustacchi 		}
860*45d3dd98SRobert Mustacchi 	}
861*45d3dd98SRobert Mustacchi 	bcopy(&buf[start], strbuf, len);
862*45d3dd98SRobert Mustacchi 	strbuf[len] = '\0';
863*45d3dd98SRobert Mustacchi 
864*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, field, strbuf));
865*45d3dd98SRobert Mustacchi }
866*45d3dd98SRobert Mustacchi 
867*45d3dd98SRobert Mustacchi static int
sff_parse_optical(const uint8_t * buf,nvlist_t * nvl)868*45d3dd98SRobert Mustacchi sff_parse_optical(const uint8_t *buf, nvlist_t *nvl)
869*45d3dd98SRobert Mustacchi {
870*45d3dd98SRobert Mustacchi 	/*
871*45d3dd98SRobert Mustacchi 	 * The value in byte 8 determines whether we interpret this as
872*45d3dd98SRobert Mustacchi 	 * describing aspects of a copper device or if it describes the
873*45d3dd98SRobert Mustacchi 	 * wavelength.
874*45d3dd98SRobert Mustacchi 	 */
875*45d3dd98SRobert Mustacchi 	if (buf[SFF_8472_COMPLIANCE_SFP] & SFF_8472_COMP_CABLE_PASSIVE) {
876*45d3dd98SRobert Mustacchi 		return (sff_gather_bitfield(buf[SFF_8472_PASSIVE_SPEC] &
877*45d3dd98SRobert Mustacchi 		    SFF_8472_PCABLE_COMP_MASK, LIBSFF_KEY_COMPLIANCE_PASSIVE,
878*45d3dd98SRobert Mustacchi 		    sff_8472_pcable_comp, nvl));
879*45d3dd98SRobert Mustacchi 	} else if (buf[SFF_8472_COMPLIANCE_SFP] & SFF_8472_COMP_CABLE_ACTIVE) {
880*45d3dd98SRobert Mustacchi 		return (sff_gather_bitfield(buf[SFF_8472_ACTIVE_SPEC] &
881*45d3dd98SRobert Mustacchi 		    SFF_8472_ACABLE_COMP_MASK, LIBSFF_KEY_COMPLIANCE_ACTIVE,
882*45d3dd98SRobert Mustacchi 		    sff_8472_acable_comp, nvl));
883*45d3dd98SRobert Mustacchi 
884*45d3dd98SRobert Mustacchi 	} else {
885*45d3dd98SRobert Mustacchi 		uint16_t val = (buf[SFF_8472_WAVELENGTH_HI] << 8) |
886*45d3dd98SRobert Mustacchi 		    buf[SFF_8472_WAVELENGTH_LOW];
887*45d3dd98SRobert Mustacchi 
888*45d3dd98SRobert Mustacchi 		return (sff_add_unit_string(val, SFF_8472_WAVELENGTH_FACTOR,
889*45d3dd98SRobert Mustacchi 		    "nm", nvl, LIBSFF_KEY_WAVELENGTH));
890*45d3dd98SRobert Mustacchi 	}
891*45d3dd98SRobert Mustacchi }
892*45d3dd98SRobert Mustacchi 
893*45d3dd98SRobert Mustacchi static int
sff_parse_options(const uint8_t * buf,nvlist_t * nvl)894*45d3dd98SRobert Mustacchi sff_parse_options(const uint8_t *buf, nvlist_t *nvl)
895*45d3dd98SRobert Mustacchi {
896*45d3dd98SRobert Mustacchi 	uint16_t val;
897*45d3dd98SRobert Mustacchi 
898*45d3dd98SRobert Mustacchi 	val = (buf[SFF_8472_OPTIONS_HI] << 8) | buf[SFF_8472_OPTIONS_LOW];
899*45d3dd98SRobert Mustacchi 	return (sff_gather_bitfield(val & SFF_8472_OPTION_MASK,
900*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_OPTIONS, sff_8472_options, nvl));
901*45d3dd98SRobert Mustacchi }
902*45d3dd98SRobert Mustacchi 
903*45d3dd98SRobert Mustacchi static int
sff_parse_8472_comp(uint8_t val,nvlist_t * nvl)904*45d3dd98SRobert Mustacchi sff_parse_8472_comp(uint8_t val, nvlist_t *nvl)
905*45d3dd98SRobert Mustacchi {
906*45d3dd98SRobert Mustacchi 	const char *str;
907*45d3dd98SRobert Mustacchi 
908*45d3dd98SRobert Mustacchi 	if (val >= SFF_8472_8472_COMP_NENTRIES) {
909*45d3dd98SRobert Mustacchi 		str = "Unallocated";
910*45d3dd98SRobert Mustacchi 	} else {
911*45d3dd98SRobert Mustacchi 		str = sff_8472_8472_comp[val];
912*45d3dd98SRobert Mustacchi 	}
913*45d3dd98SRobert Mustacchi 
914*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, LIBSFF_KEY_COMPLIANCE_8472, str));
915*45d3dd98SRobert Mustacchi }
916*45d3dd98SRobert Mustacchi 
917*45d3dd98SRobert Mustacchi /*
918*45d3dd98SRobert Mustacchi  * Parse an SFP that is either based on INF 8074 or SFF 8472. These are GBIC,
919*45d3dd98SRobert Mustacchi  * SFP, SFP+, and SFP28 based devices.
920*45d3dd98SRobert Mustacchi  *
921*45d3dd98SRobert Mustacchi  * The SFP parsing into an nvlist_t is incomplete. At the moment we're not
922*45d3dd98SRobert Mustacchi  * parsing the following pieces from SFF 8472 page 0xa0:
923*45d3dd98SRobert Mustacchi  *
924*45d3dd98SRobert Mustacchi  *  o  Rate Selection Logic
925*45d3dd98SRobert Mustacchi  *  o  Diagnostic Monitoring Type
926*45d3dd98SRobert Mustacchi  */
927*45d3dd98SRobert Mustacchi static int
sff_parse_sfp(const uint8_t * buf,nvlist_t * nvl)928*45d3dd98SRobert Mustacchi sff_parse_sfp(const uint8_t *buf, nvlist_t *nvl)
929*45d3dd98SRobert Mustacchi {
930*45d3dd98SRobert Mustacchi 	int ret;
931*45d3dd98SRobert Mustacchi 
932*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_id(buf[SFF_8472_IDENTIFIER], nvl)) != 0)
933*45d3dd98SRobert Mustacchi 		return (ret);
934*45d3dd98SRobert Mustacchi 
935*45d3dd98SRobert Mustacchi 	/*
936*45d3dd98SRobert Mustacchi 	 * The extended identifier is derived from SFF 8472, Table 5-2. It
937*45d3dd98SRobert Mustacchi 	 * generally is just the value 4. The other values are not well defined.
938*45d3dd98SRobert Mustacchi 	 */
939*45d3dd98SRobert Mustacchi 	if ((ret = nvlist_add_uint8(nvl, LIBSFF_KEY_8472_EXT_IDENTIFIER,
940*45d3dd98SRobert Mustacchi 	    buf[SFF_8472_EXT_IDENTIFER])) != 0)
941*45d3dd98SRobert Mustacchi 		return (ret);
942*45d3dd98SRobert Mustacchi 
943*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_connector(buf[SFF_8472_CONNECTOR], nvl)) != 0)
944*45d3dd98SRobert Mustacchi 		return (ret);
945*45d3dd98SRobert Mustacchi 
946*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_compliance(buf, nvl)) != 0)
947*45d3dd98SRobert Mustacchi 		return (ret);
948*45d3dd98SRobert Mustacchi 
949*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_encoding(buf[SFF_8472_ENCODING], nvl,
950*45d3dd98SRobert Mustacchi 	    B_TRUE)) != 0)
951*45d3dd98SRobert Mustacchi 		return (ret);
952*45d3dd98SRobert Mustacchi 
953*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_br(buf, nvl)) != 0)
954*45d3dd98SRobert Mustacchi 		return (ret);
955*45d3dd98SRobert Mustacchi 
956*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_lengths(buf, nvl)) != 0)
957*45d3dd98SRobert Mustacchi 		return (ret);
958*45d3dd98SRobert Mustacchi 
959*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8472_VENDOR, SFF_8472_VENDOR_LEN,
960*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_VENDOR, nvl)) != 0)
961*45d3dd98SRobert Mustacchi 		return (ret);
962*45d3dd98SRobert Mustacchi 
963*45d3dd98SRobert Mustacchi 	if ((ret = nvlist_add_byte_array(nvl, LIBSFF_KEY_OUI,
964*45d3dd98SRobert Mustacchi 	    (uchar_t *)&buf[SFF_8472_OUI], SFF_8472_OUI_LEN)) != 0)
965*45d3dd98SRobert Mustacchi 		return (ret);
966*45d3dd98SRobert Mustacchi 
967*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_PN,
968*45d3dd98SRobert Mustacchi 	    SFF_8472_VENDOR_PN_LEN, LIBSFF_KEY_PART, nvl)) != 0)
969*45d3dd98SRobert Mustacchi 		return (ret);
970*45d3dd98SRobert Mustacchi 
971*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_REV,
972*45d3dd98SRobert Mustacchi 	    SFF_8472_VENDOR_REV_LEN, LIBSFF_KEY_REVISION, nvl)) != 0)
973*45d3dd98SRobert Mustacchi 		return (ret);
974*45d3dd98SRobert Mustacchi 
975*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_optical(buf, nvl)) != 0)
976*45d3dd98SRobert Mustacchi 		return (ret);
977*45d3dd98SRobert Mustacchi 
978*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_options(buf, nvl)) != 0)
979*45d3dd98SRobert Mustacchi 		return (ret);
980*45d3dd98SRobert Mustacchi 
981*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8472_VENDOR_SN,
982*45d3dd98SRobert Mustacchi 	    SFF_8472_VENDOR_SN_LEN, LIBSFF_KEY_SERIAL, nvl)) != 0)
983*45d3dd98SRobert Mustacchi 		return (ret);
984*45d3dd98SRobert Mustacchi 
985*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8472_DATE_CODE,
986*45d3dd98SRobert Mustacchi 	    SFF_8472_DATE_CODE_LEN, LIBSFF_KEY_DATECODE, nvl)) != 0)
987*45d3dd98SRobert Mustacchi 		return (ret);
988*45d3dd98SRobert Mustacchi 
989*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8472_ENHANCED_OPTIONS] &
990*45d3dd98SRobert Mustacchi 	    SFF_8472_EXTOPT_MASK, LIBSFF_KEY_EXTENDED_OPTIONS,
991*45d3dd98SRobert Mustacchi 	    sff_8472_extopts, nvl)) != 0)
992*45d3dd98SRobert Mustacchi 		return (ret);
993*45d3dd98SRobert Mustacchi 
994*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_8472_comp(buf[SFF_8472_SFF_8472_COMPLIANCE],
995*45d3dd98SRobert Mustacchi 	    nvl)) != 0)
996*45d3dd98SRobert Mustacchi 		return (ret);
997*45d3dd98SRobert Mustacchi 
998*45d3dd98SRobert Mustacchi 	return (0);
999*45d3dd98SRobert Mustacchi }
1000*45d3dd98SRobert Mustacchi 
1001*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_compliance(const uint8_t * buf,nvlist_t * nvl)1002*45d3dd98SRobert Mustacchi sff_qsfp_parse_compliance(const uint8_t *buf, nvlist_t *nvl)
1003*45d3dd98SRobert Mustacchi {
1004*45d3dd98SRobert Mustacchi 	int ret;
1005*45d3dd98SRobert Mustacchi 	uint16_t fc_val;
1006*45d3dd98SRobert Mustacchi 
1007*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_10GBEP] &
1008*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_10GETH_MASK, LIBSFF_KEY_COMPLIANCE_10GBE,
1009*45d3dd98SRobert Mustacchi 	    sff_8636_comp_10geth, nvl)) != 0)
1010*45d3dd98SRobert Mustacchi 		return (ret);
1011*45d3dd98SRobert Mustacchi 
1012*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_SONET] &
1013*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_SONET_MASK, LIBSFF_KEY_COMPLIANCE_SONET,
1014*45d3dd98SRobert Mustacchi 	    sff_8636_comp_sonet, nvl)) != 0)
1015*45d3dd98SRobert Mustacchi 		return (ret);
1016*45d3dd98SRobert Mustacchi 
1017*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_SAS] &
1018*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_SAS_MASK, LIBSFF_KEY_COMPLIANCE_SAS,
1019*45d3dd98SRobert Mustacchi 	    sff_8636_comp_sas, nvl)) != 0)
1020*45d3dd98SRobert Mustacchi 		return (ret);
1021*45d3dd98SRobert Mustacchi 
1022*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_ETHERNET] &
1023*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_ETH_MASK, LIBSFF_KEY_COMPLIANCE_GBE,
1024*45d3dd98SRobert Mustacchi 	    sff_8636_comp_eth, nvl)) != 0)
1025*45d3dd98SRobert Mustacchi 		return (ret);
1026*45d3dd98SRobert Mustacchi 
1027*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FCLEN] &
1028*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_FCLEN_MASK, LIBSFF_KEY_COMPLIANCE_FC_LEN,
1029*45d3dd98SRobert Mustacchi 	    sff_8636_comp_fclen, nvl)) != 0)
1030*45d3dd98SRobert Mustacchi 		return (ret);
1031*45d3dd98SRobert Mustacchi 
1032*45d3dd98SRobert Mustacchi 	fc_val = buf[SFF_8636_COMPLIANCE_FC_LOW] |
1033*45d3dd98SRobert Mustacchi 	    (buf[SFF_8636_COMPLIANCE_FC_HIGH] << 8);
1034*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(fc_val & SFF_8636_COMP_TECH_MASK,
1035*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_COMPLIANCE_FC_TECH, sff_8636_comp_tech, nvl)) != 0)
1036*45d3dd98SRobert Mustacchi 		return (ret);
1037*45d3dd98SRobert Mustacchi 
1038*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FC_MEDIA] &
1039*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_MEDIA_MASK, LIBSFF_KEY_COMPLIANCE_FC_MEDIA,
1040*45d3dd98SRobert Mustacchi 	    sff_8636_comp_media, nvl)) != 0)
1041*45d3dd98SRobert Mustacchi 		return (ret);
1042*45d3dd98SRobert Mustacchi 
1043*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_COMPLIANCE_FC_SPEED] &
1044*45d3dd98SRobert Mustacchi 	    SFF_8636_COMP_SPEED_MASK, LIBSFF_KEY_COMPLIANCE_FC_SPEED,
1045*45d3dd98SRobert Mustacchi 	    sff_8636_comp_speed, nvl)) != 0)
1046*45d3dd98SRobert Mustacchi 		return (ret);
1047*45d3dd98SRobert Mustacchi 
1048*45d3dd98SRobert Mustacchi 	return (0);
1049*45d3dd98SRobert Mustacchi }
1050*45d3dd98SRobert Mustacchi 
1051*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_br(const uint8_t * buf,nvlist_t * nvl)1052*45d3dd98SRobert Mustacchi sff_qsfp_parse_br(const uint8_t *buf, nvlist_t *nvl)
1053*45d3dd98SRobert Mustacchi {
1054*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_BR_NOMINAL] == 0xff) {
1055*45d3dd98SRobert Mustacchi 		return (sff_add_unit_string(buf[SFF_8636_BR_NOMINAL_EXT],
1056*45d3dd98SRobert Mustacchi 		    SFF_8636_BR_NOMINAL_EXT_FACTOR, "Mbps", nvl,
1057*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_BR_NOMINAL));
1058*45d3dd98SRobert Mustacchi 	} else {
1059*45d3dd98SRobert Mustacchi 		return (sff_add_unit_string(buf[SFF_8636_BR_NOMINAL],
1060*45d3dd98SRobert Mustacchi 		    SFF_8636_BR_NOMINAL_FACTOR, "Mbps", nvl,
1061*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_BR_NOMINAL));
1062*45d3dd98SRobert Mustacchi 	}
1063*45d3dd98SRobert Mustacchi }
1064*45d3dd98SRobert Mustacchi 
1065*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_lengths(const uint8_t * buf,nvlist_t * nvl)1066*45d3dd98SRobert Mustacchi sff_qsfp_parse_lengths(const uint8_t *buf, nvlist_t *nvl)
1067*45d3dd98SRobert Mustacchi {
1068*45d3dd98SRobert Mustacchi 	int ret;
1069*45d3dd98SRobert Mustacchi 
1070*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_LENGTH_SMF] != 0) {
1071*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_SMF],
1072*45d3dd98SRobert Mustacchi 		    SFF_8636_LENGTH_SMF_FACTOR, "km", nvl,
1073*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_SMF_KM)) != 0)
1074*45d3dd98SRobert Mustacchi 			return (ret);
1075*45d3dd98SRobert Mustacchi 	}
1076*45d3dd98SRobert Mustacchi 
1077*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_LENGTH_OM3] != 0) {
1078*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM3],
1079*45d3dd98SRobert Mustacchi 		    SFF_8636_LENGTH_OM3_FACTOR, "m", nvl,
1080*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM3)) != 0)
1081*45d3dd98SRobert Mustacchi 			return (ret);
1082*45d3dd98SRobert Mustacchi 	}
1083*45d3dd98SRobert Mustacchi 
1084*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_LENGTH_OM2] != 0) {
1085*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM2],
1086*45d3dd98SRobert Mustacchi 		    SFF_8636_LENGTH_OM2_FACTOR, "m", nvl,
1087*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM2)) != 0)
1088*45d3dd98SRobert Mustacchi 			return (ret);
1089*45d3dd98SRobert Mustacchi 	}
1090*45d3dd98SRobert Mustacchi 
1091*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_LENGTH_OM1] != 0) {
1092*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_OM1],
1093*45d3dd98SRobert Mustacchi 		    SFF_8636_LENGTH_OM1_FACTOR, "m", nvl,
1094*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_OM1)) != 0)
1095*45d3dd98SRobert Mustacchi 			return (ret);
1096*45d3dd98SRobert Mustacchi 	}
1097*45d3dd98SRobert Mustacchi 
1098*45d3dd98SRobert Mustacchi 	if (buf[SFF_8636_LENGTH_COPPER] != 0) {
1099*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_LENGTH_COPPER],
1100*45d3dd98SRobert Mustacchi 		    SFF_8636_LENGTH_COPPER_FACTOR, "m", nvl,
1101*45d3dd98SRobert Mustacchi 		    LIBSFF_KEY_LENGTH_COPPER)) != 0)
1102*45d3dd98SRobert Mustacchi 			return (ret);
1103*45d3dd98SRobert Mustacchi 	}
1104*45d3dd98SRobert Mustacchi 
1105*45d3dd98SRobert Mustacchi 	return (0);
1106*45d3dd98SRobert Mustacchi }
1107*45d3dd98SRobert Mustacchi 
1108*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_tech(uint8_t val,nvlist_t * nvl)1109*45d3dd98SRobert Mustacchi sff_qsfp_parse_tech(uint8_t val, nvlist_t *nvl)
1110*45d3dd98SRobert Mustacchi {
1111*45d3dd98SRobert Mustacchi 	const char *strs[5];
1112*45d3dd98SRobert Mustacchi 
1113*45d3dd98SRobert Mustacchi 	strs[0] = sff_8636_trans_tech[(val & 0xf0) >> 4];
1114*45d3dd98SRobert Mustacchi 	if (val & 0x08) {
1115*45d3dd98SRobert Mustacchi 		strs[1] = "Active Wavelength Control";
1116*45d3dd98SRobert Mustacchi 	} else {
1117*45d3dd98SRobert Mustacchi 		strs[1] = "No Wavelength Control";
1118*45d3dd98SRobert Mustacchi 	}
1119*45d3dd98SRobert Mustacchi 
1120*45d3dd98SRobert Mustacchi 	if (val & 0x04) {
1121*45d3dd98SRobert Mustacchi 		strs[2] = "Cooled Transmitter";
1122*45d3dd98SRobert Mustacchi 	} else {
1123*45d3dd98SRobert Mustacchi 		strs[2] = "Uncooled Transmitter";
1124*45d3dd98SRobert Mustacchi 	}
1125*45d3dd98SRobert Mustacchi 
1126*45d3dd98SRobert Mustacchi 	if (val & 0x02) {
1127*45d3dd98SRobert Mustacchi 		strs[3] = "APD Detector";
1128*45d3dd98SRobert Mustacchi 	} else {
1129*45d3dd98SRobert Mustacchi 		strs[3] = "Pin Detector";
1130*45d3dd98SRobert Mustacchi 	}
1131*45d3dd98SRobert Mustacchi 
1132*45d3dd98SRobert Mustacchi 	if (val & 0x01) {
1133*45d3dd98SRobert Mustacchi 		strs[4] = "Transmitter Tunable";
1134*45d3dd98SRobert Mustacchi 	} else {
1135*45d3dd98SRobert Mustacchi 		strs[4] = "Transmitter Not Tunable";
1136*45d3dd98SRobert Mustacchi 	}
1137*45d3dd98SRobert Mustacchi 
1138*45d3dd98SRobert Mustacchi 	/*
1139*45d3dd98SRobert Mustacchi 	 * The nvlist routines don't touch the array, so we end up lying about
1140*45d3dd98SRobert Mustacchi 	 * the type of data so that we can avoid a rash of additional
1141*45d3dd98SRobert Mustacchi 	 * allocations and strdups.
1142*45d3dd98SRobert Mustacchi 	 */
1143*45d3dd98SRobert Mustacchi 	return (nvlist_add_string_array(nvl, LIBSFF_KEY_TRAN_TECH,
1144*45d3dd98SRobert Mustacchi 	    (char **)strs, 5));
1145*45d3dd98SRobert Mustacchi }
1146*45d3dd98SRobert Mustacchi 
1147*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_copperwave(const uint8_t * buf,nvlist_t * nvl)1148*45d3dd98SRobert Mustacchi sff_qsfp_parse_copperwave(const uint8_t *buf, nvlist_t *nvl)
1149*45d3dd98SRobert Mustacchi {
1150*45d3dd98SRobert Mustacchi 	int ret;
1151*45d3dd98SRobert Mustacchi 
1152*45d3dd98SRobert Mustacchi 	/*
1153*45d3dd98SRobert Mustacchi 	 * The values that we get depend on whether or not we are a copper
1154*45d3dd98SRobert Mustacchi 	 * device or not. We can determine this based on the identification
1155*45d3dd98SRobert Mustacchi 	 * information in the device technology field.
1156*45d3dd98SRobert Mustacchi 	 */
1157*45d3dd98SRobert Mustacchi 	if ((buf[SFF_8636_DEVICE_TECH] & 0xf0) >= 0xa0) {
1158*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_2G], 1,
1159*45d3dd98SRobert Mustacchi 		    "dB", nvl, LIBSFF_KEY_ATTENUATE_2G)) != 0)
1160*45d3dd98SRobert Mustacchi 			return (ret);
1161*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_5G], 1,
1162*45d3dd98SRobert Mustacchi 		    "dB", nvl, LIBSFF_KEY_ATTENUATE_5G)) != 0)
1163*45d3dd98SRobert Mustacchi 			return (ret);
1164*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_7G], 1,
1165*45d3dd98SRobert Mustacchi 		    "dB", nvl, LIBSFF_KEY_ATTENUATE_7G)) != 0)
1166*45d3dd98SRobert Mustacchi 			return (ret);
1167*45d3dd98SRobert Mustacchi 		if ((ret = sff_add_unit_string(buf[SFF_8636_ATTENUATE_12G], 1,
1168*45d3dd98SRobert Mustacchi 		    "dB", nvl, LIBSFF_KEY_ATTENUATE_12G)) != 0)
1169*45d3dd98SRobert Mustacchi 			return (ret);
1170*45d3dd98SRobert Mustacchi 	} else {
1171*45d3dd98SRobert Mustacchi 		uint16_t val;
1172*45d3dd98SRobert Mustacchi 		double d;
1173*45d3dd98SRobert Mustacchi 		char strbuf[SFP_STRBUF];
1174*45d3dd98SRobert Mustacchi 
1175*45d3dd98SRobert Mustacchi 		/*
1176*45d3dd98SRobert Mustacchi 		 * Because we need to divide the units here into doubles, we
1177*45d3dd98SRobert Mustacchi 		 * can't use the standard unit routine.
1178*45d3dd98SRobert Mustacchi 		 */
1179*45d3dd98SRobert Mustacchi 		val = (buf[SFF_8636_WAVELENGTH_NOMINAL_HI] << 8) |
1180*45d3dd98SRobert Mustacchi 		    buf[SFF_8636_WAVELENGTH_NOMINAL_LOW];
1181*45d3dd98SRobert Mustacchi 		if (val != 0) {
1182*45d3dd98SRobert Mustacchi 			d = val / 20.0;
1183*45d3dd98SRobert Mustacchi 			(void) snprintf(strbuf, sizeof (strbuf), "%.3lf nm", d);
1184*45d3dd98SRobert Mustacchi 			if ((ret = nvlist_add_string(nvl, LIBSFF_KEY_WAVELENGTH,
1185*45d3dd98SRobert Mustacchi 			    strbuf)) != 0)
1186*45d3dd98SRobert Mustacchi 				return (ret);
1187*45d3dd98SRobert Mustacchi 		}
1188*45d3dd98SRobert Mustacchi 
1189*45d3dd98SRobert Mustacchi 		val = (buf[SFF_8636_WAVELENGTH_TOLERANCE_HI] << 8) |
1190*45d3dd98SRobert Mustacchi 		    buf[SFF_8636_WAVELENGTH_TOLERANCE_LOW];
1191*45d3dd98SRobert Mustacchi 		if (val != 0) {
1192*45d3dd98SRobert Mustacchi 			d = val / 20.0;
1193*45d3dd98SRobert Mustacchi 			(void) snprintf(strbuf, sizeof (strbuf), "%.3lf nm", d);
1194*45d3dd98SRobert Mustacchi 			if ((ret = nvlist_add_string(nvl,
1195*45d3dd98SRobert Mustacchi 			    LIBSFF_KEY_WAVE_TOLERANCE, strbuf)) != 0)
1196*45d3dd98SRobert Mustacchi 				return (ret);
1197*45d3dd98SRobert Mustacchi 		}
1198*45d3dd98SRobert Mustacchi 	}
1199*45d3dd98SRobert Mustacchi 
1200*45d3dd98SRobert Mustacchi 	return (0);
1201*45d3dd98SRobert Mustacchi }
1202*45d3dd98SRobert Mustacchi 
1203*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_casetemp(uint8_t val,nvlist_t * nvl)1204*45d3dd98SRobert Mustacchi sff_qsfp_parse_casetemp(uint8_t val, nvlist_t *nvl)
1205*45d3dd98SRobert Mustacchi {
1206*45d3dd98SRobert Mustacchi 	/*
1207*45d3dd98SRobert Mustacchi 	 * The default temperature per SFF 8636 r2.7 6.3.21 'Maximum Case
1208*45d3dd98SRobert Mustacchi 	 * Temperature' is 70 C. If the value is zero, we're supposed to assume
1209*45d3dd98SRobert Mustacchi 	 * it's the default.
1210*45d3dd98SRobert Mustacchi 	 */
1211*45d3dd98SRobert Mustacchi 	if (val == 0)
1212*45d3dd98SRobert Mustacchi 		val = 70;
1213*45d3dd98SRobert Mustacchi 
1214*45d3dd98SRobert Mustacchi 	return (sff_add_unit_string(val, 1, "C", nvl,
1215*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_MAX_CASE_TEMP));
1216*45d3dd98SRobert Mustacchi }
1217*45d3dd98SRobert Mustacchi 
1218*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_extcomp(uint8_t val,nvlist_t * nvl)1219*45d3dd98SRobert Mustacchi sff_qsfp_parse_extcomp(uint8_t val, nvlist_t *nvl)
1220*45d3dd98SRobert Mustacchi {
1221*45d3dd98SRobert Mustacchi 	const char *str;
1222*45d3dd98SRobert Mustacchi 
1223*45d3dd98SRobert Mustacchi 	if (val >= SFF_8024_EXT_SPEC_NENTRIES) {
1224*45d3dd98SRobert Mustacchi 		str = "Reserved";
1225*45d3dd98SRobert Mustacchi 	} else {
1226*45d3dd98SRobert Mustacchi 		str = sff_8024_ext_spec[val];
1227*45d3dd98SRobert Mustacchi 	}
1228*45d3dd98SRobert Mustacchi 
1229*45d3dd98SRobert Mustacchi 	return (nvlist_add_string(nvl, LIBSFF_KEY_EXT_SPEC, str));
1230*45d3dd98SRobert Mustacchi }
1231*45d3dd98SRobert Mustacchi 
1232*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_options(const uint8_t * buf,nvlist_t * nvl)1233*45d3dd98SRobert Mustacchi sff_qsfp_parse_options(const uint8_t *buf, nvlist_t *nvl)
1234*45d3dd98SRobert Mustacchi {
1235*45d3dd98SRobert Mustacchi 	uint_t val;
1236*45d3dd98SRobert Mustacchi 
1237*45d3dd98SRobert Mustacchi 	val = (buf[SFF_8636_OPTIONS_HI] << 16) |
1238*45d3dd98SRobert Mustacchi 	    (buf[SFF_8636_OPTIONS_MID] << 8) | buf[SFF_8636_OPTIONS_LOW];
1239*45d3dd98SRobert Mustacchi 
1240*45d3dd98SRobert Mustacchi 	return (sff_gather_bitfield(val & SFF_8636_OPTION_MASK,
1241*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_OPTIONS, sff_8636_options, nvl));
1242*45d3dd98SRobert Mustacchi }
1243*45d3dd98SRobert Mustacchi 
1244*45d3dd98SRobert Mustacchi static int
sff_qsfp_parse_diag(uint8_t val,nvlist_t * nvl)1245*45d3dd98SRobert Mustacchi sff_qsfp_parse_diag(uint8_t val, nvlist_t *nvl)
1246*45d3dd98SRobert Mustacchi {
1247*45d3dd98SRobert Mustacchi 	const char *buf[2];
1248*45d3dd98SRobert Mustacchi 	uint_t count = 1;
1249*45d3dd98SRobert Mustacchi 
1250*45d3dd98SRobert Mustacchi 	if (val & 0x08) {
1251*45d3dd98SRobert Mustacchi 		buf[0] = "Received power measurements: Average Power";
1252*45d3dd98SRobert Mustacchi 	} else {
1253*45d3dd98SRobert Mustacchi 		buf[0] = "Received power measurements: OMA";
1254*45d3dd98SRobert Mustacchi 	}
1255*45d3dd98SRobert Mustacchi 
1256*45d3dd98SRobert Mustacchi 	if (val & 0x04) {
1257*45d3dd98SRobert Mustacchi 		count++;
1258*45d3dd98SRobert Mustacchi 		buf[1] = "Transmitter power measurement";
1259*45d3dd98SRobert Mustacchi 	}
1260*45d3dd98SRobert Mustacchi 
1261*45d3dd98SRobert Mustacchi 	/*
1262*45d3dd98SRobert Mustacchi 	 * The nvlist routines don't touch the array, so we end up lying about
1263*45d3dd98SRobert Mustacchi 	 * the type of data so that we can avoid a rash of additional
1264*45d3dd98SRobert Mustacchi 	 * allocations and strdups.
1265*45d3dd98SRobert Mustacchi 	 */
1266*45d3dd98SRobert Mustacchi 	return (nvlist_add_string_array(nvl, LIBSFF_KEY_DIAG_MONITOR,
1267*45d3dd98SRobert Mustacchi 	    (char **)buf, count));
1268*45d3dd98SRobert Mustacchi }
1269*45d3dd98SRobert Mustacchi 
1270*45d3dd98SRobert Mustacchi /*
1271*45d3dd98SRobert Mustacchi  * Parse a QSFP family device that is based on SFF-8436 / SFF-8636. Note that we
1272*45d3dd98SRobert Mustacchi  * ignore the lower half of page 0xa0 at this time and instead focus on the
1273*45d3dd98SRobert Mustacchi  * upper half of page 0xa0 which has identification information.
1274*45d3dd98SRobert Mustacchi  *
1275*45d3dd98SRobert Mustacchi  * For the moment we're not parsing the following fields:
1276*45d3dd98SRobert Mustacchi  *
1277*45d3dd98SRobert Mustacchi  *  o  Extended Identifier (byte 129)
1278*45d3dd98SRobert Mustacchi  *  o  Extended Rate Select Compliance (byte 141)
1279*45d3dd98SRobert Mustacchi  */
1280*45d3dd98SRobert Mustacchi static int
sff_parse_qsfp(const uint8_t * buf,nvlist_t * nvl)1281*45d3dd98SRobert Mustacchi sff_parse_qsfp(const uint8_t *buf, nvlist_t *nvl)
1282*45d3dd98SRobert Mustacchi {
1283*45d3dd98SRobert Mustacchi 	int ret;
1284*45d3dd98SRobert Mustacchi 
1285*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_id(buf[SFF_8636_IDENTIFIER], nvl)) != 0)
1286*45d3dd98SRobert Mustacchi 		return (ret);
1287*45d3dd98SRobert Mustacchi 
1288*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_connector(buf[SFF_8636_CONNECTOR], nvl)) != 0)
1289*45d3dd98SRobert Mustacchi 		return (ret);
1290*45d3dd98SRobert Mustacchi 
1291*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_compliance(buf, nvl)) != 0)
1292*45d3dd98SRobert Mustacchi 		return (ret);
1293*45d3dd98SRobert Mustacchi 
1294*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_encoding(buf[SFF_8636_ENCODING], nvl,
1295*45d3dd98SRobert Mustacchi 	    B_FALSE)) != 0)
1296*45d3dd98SRobert Mustacchi 		return (ret);
1297*45d3dd98SRobert Mustacchi 
1298*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_br(buf, nvl)) != 0)
1299*45d3dd98SRobert Mustacchi 		return (ret);
1300*45d3dd98SRobert Mustacchi 
1301*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_lengths(buf, nvl)) != 0)
1302*45d3dd98SRobert Mustacchi 		return (ret);
1303*45d3dd98SRobert Mustacchi 
1304*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_tech(buf[SFF_8636_DEVICE_TECH], nvl)) != 0)
1305*45d3dd98SRobert Mustacchi 		return (ret);
1306*45d3dd98SRobert Mustacchi 
1307*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8636_VENDOR, SFF_8636_VENDOR_LEN,
1308*45d3dd98SRobert Mustacchi 	    LIBSFF_KEY_VENDOR, nvl)) != 0)
1309*45d3dd98SRobert Mustacchi 		return (ret);
1310*45d3dd98SRobert Mustacchi 
1311*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_EXTENDED_MODULE] &
1312*45d3dd98SRobert Mustacchi 	    SFF_8636_EXTMOD_CODES, LIBSFF_KEY_EXT_MOD_CODES,
1313*45d3dd98SRobert Mustacchi 	    sff_8636_extmod_codes, nvl)) != 0)
1314*45d3dd98SRobert Mustacchi 		return (ret);
1315*45d3dd98SRobert Mustacchi 
1316*45d3dd98SRobert Mustacchi 	if ((ret = nvlist_add_byte_array(nvl, LIBSFF_KEY_OUI,
1317*45d3dd98SRobert Mustacchi 	    (uchar_t *)&buf[SFF_8636_OUI], SFF_8636_OUI_LEN)) != 0)
1318*45d3dd98SRobert Mustacchi 		return (ret);
1319*45d3dd98SRobert Mustacchi 
1320*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_PN,
1321*45d3dd98SRobert Mustacchi 	    SFF_8636_VENDOR_PN_LEN, LIBSFF_KEY_PART, nvl)) != 0)
1322*45d3dd98SRobert Mustacchi 		return (ret);
1323*45d3dd98SRobert Mustacchi 
1324*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_REV,
1325*45d3dd98SRobert Mustacchi 	    SFF_8636_VENDOR_REV_LEN, LIBSFF_KEY_REVISION, nvl)) != 0)
1326*45d3dd98SRobert Mustacchi 		return (ret);
1327*45d3dd98SRobert Mustacchi 
1328*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_copperwave(buf, nvl)) != 0)
1329*45d3dd98SRobert Mustacchi 		return (ret);
1330*45d3dd98SRobert Mustacchi 
1331*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_casetemp(buf[SFF_8636_MAX_CASE_TEMP],
1332*45d3dd98SRobert Mustacchi 	    nvl)) != 0)
1333*45d3dd98SRobert Mustacchi 		return (ret);
1334*45d3dd98SRobert Mustacchi 
1335*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_extcomp(buf[SFF_8636_LINK_CODES], nvl)) != 0)
1336*45d3dd98SRobert Mustacchi 		return (ret);
1337*45d3dd98SRobert Mustacchi 
1338*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_options(buf, nvl)) != 0)
1339*45d3dd98SRobert Mustacchi 		return (ret);
1340*45d3dd98SRobert Mustacchi 
1341*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8636_VENDOR_SN,
1342*45d3dd98SRobert Mustacchi 	    SFF_8636_VENDOR_SN_LEN, LIBSFF_KEY_SERIAL, nvl)) != 0)
1343*45d3dd98SRobert Mustacchi 		return (ret);
1344*45d3dd98SRobert Mustacchi 
1345*45d3dd98SRobert Mustacchi 	if ((ret = sff_parse_string(buf, SFF_8636_DATE_CODE,
1346*45d3dd98SRobert Mustacchi 	    SFF_8636_DATE_CODE_LEN, LIBSFF_KEY_DATECODE, nvl)) != 0)
1347*45d3dd98SRobert Mustacchi 		return (ret);
1348*45d3dd98SRobert Mustacchi 
1349*45d3dd98SRobert Mustacchi 	if ((ret = sff_qsfp_parse_diag(buf[SFF_8636_DIAG_MONITORING],
1350*45d3dd98SRobert Mustacchi 	    nvl)) != 0)
1351*45d3dd98SRobert Mustacchi 		return (ret);
1352*45d3dd98SRobert Mustacchi 
1353*45d3dd98SRobert Mustacchi 	if ((ret = sff_gather_bitfield(buf[SFF_8636_ENHANCED_OPTIONS] &
1354*45d3dd98SRobert Mustacchi 	    SFF_8636_ENHANCED_OPTIONS_MASK, LIBSFF_KEY_ENHANCED_OPTIONS,
1355*45d3dd98SRobert Mustacchi 	    sff_8636_eopt, nvl)) != 0)
1356*45d3dd98SRobert Mustacchi 		return (ret);
1357*45d3dd98SRobert Mustacchi 
1358*45d3dd98SRobert Mustacchi 	return (0);
1359*45d3dd98SRobert Mustacchi }
1360*45d3dd98SRobert Mustacchi 
1361*45d3dd98SRobert Mustacchi int
libsff_parse(const uint8_t * buf,size_t len,uint_t page,nvlist_t ** nvpp)1362*45d3dd98SRobert Mustacchi libsff_parse(const uint8_t *buf, size_t len, uint_t page, nvlist_t **nvpp)
1363*45d3dd98SRobert Mustacchi {
1364*45d3dd98SRobert Mustacchi 	int ret;
1365*45d3dd98SRobert Mustacchi 	nvlist_t *nvp = NULL;
1366*45d3dd98SRobert Mustacchi 	uint8_t ubuf[256];
1367*45d3dd98SRobert Mustacchi 
1368*45d3dd98SRobert Mustacchi 	/*
1369*45d3dd98SRobert Mustacchi 	 * At the moment, we only support page a0.
1370*45d3dd98SRobert Mustacchi 	 */
1371*45d3dd98SRobert Mustacchi 	if (page != 0xa0 || buf == NULL || len == 0 || nvpp == NULL)
1372*45d3dd98SRobert Mustacchi 		return (EINVAL);
1373*45d3dd98SRobert Mustacchi 
1374*45d3dd98SRobert Mustacchi 	*nvpp = NULL;
1375*45d3dd98SRobert Mustacchi 
1376*45d3dd98SRobert Mustacchi 	/*
1377*45d3dd98SRobert Mustacchi 	 * Make sure that the library has been given valid data to parse.
1378*45d3dd98SRobert Mustacchi 	 */
1379*45d3dd98SRobert Mustacchi 	if (uucopy(buf, ubuf, MIN(sizeof (ubuf), len)) != 0)
1380*45d3dd98SRobert Mustacchi 		return (errno);
1381*45d3dd98SRobert Mustacchi 
1382*45d3dd98SRobert Mustacchi 	if ((ret = nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0)) != 0)
1383*45d3dd98SRobert Mustacchi 		return (ret);
1384*45d3dd98SRobert Mustacchi 
1385*45d3dd98SRobert Mustacchi 	switch (buf[0]) {
1386*45d3dd98SRobert Mustacchi 	case SFF_8024_ID_QSFP:
1387*45d3dd98SRobert Mustacchi 	case SFF_8024_ID_QSFP_PLUS:
1388*45d3dd98SRobert Mustacchi 	case SFF_8024_ID_QSFP28:
1389*45d3dd98SRobert Mustacchi 		/*
1390*45d3dd98SRobert Mustacchi 		 * For QSFP based products, identification information is spread
1391*45d3dd98SRobert Mustacchi 		 * across both the top and bottom half of page 0xa0.
1392*45d3dd98SRobert Mustacchi 		 */
1393*45d3dd98SRobert Mustacchi 		if (len < SFP_MIN_LEN_8636) {
1394*45d3dd98SRobert Mustacchi 			ret = EINVAL;
1395*45d3dd98SRobert Mustacchi 			break;
1396*45d3dd98SRobert Mustacchi 		}
1397*45d3dd98SRobert Mustacchi 		ret = sff_parse_qsfp(ubuf, nvp);
1398*45d3dd98SRobert Mustacchi 		break;
1399*45d3dd98SRobert Mustacchi 	default:
1400*45d3dd98SRobert Mustacchi 		if (len < SFP_MIN_LEN_8472) {
1401*45d3dd98SRobert Mustacchi 			ret = EINVAL;
1402*45d3dd98SRobert Mustacchi 			break;
1403*45d3dd98SRobert Mustacchi 		}
1404*45d3dd98SRobert Mustacchi 		ret = sff_parse_sfp(ubuf, nvp);
1405*45d3dd98SRobert Mustacchi 		break;
1406*45d3dd98SRobert Mustacchi 	}
1407*45d3dd98SRobert Mustacchi 
1408*45d3dd98SRobert Mustacchi 	if (ret != 0) {
1409*45d3dd98SRobert Mustacchi 		nvlist_free(nvp);
1410*45d3dd98SRobert Mustacchi 	} else {
1411*45d3dd98SRobert Mustacchi 		*nvpp = nvp;
1412*45d3dd98SRobert Mustacchi 	}
1413*45d3dd98SRobert Mustacchi 	return (ret);
1414*45d3dd98SRobert Mustacchi }
1415