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