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