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