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 *
sff_pair_find(uint_t val,sff_pair_t * pairs)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
sff_parse_id(uint8_t id,nvlist_t * nvl)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
sff_add_unit_string(uint64_t val,uint64_t factor,const char * unit,nvlist_t * nvl,const char * key)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
sff_parse_connector(uint8_t con,nvlist_t * nvl)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
sff_gather_bitfield(uint32_t value,const char * name,sff_pair_t * pairs,nvlist_t * nvl)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
sff_parse_compliance(const uint8_t * buf,nvlist_t * nvl)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
sff_parse_encoding(uint8_t val,nvlist_t * nvl,boolean_t sfp)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
sff_parse_br(const uint8_t * buf,nvlist_t * nvl)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
sff_parse_lengths(const uint8_t * buf,nvlist_t * nvl)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
sff_parse_string(const uint8_t * buf,uint_t start,uint_t len,const char * field,nvlist_t * nvl)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
sff_parse_optical(const uint8_t * buf,nvlist_t * nvl)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
sff_parse_options(const uint8_t * buf,nvlist_t * nvl)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
sff_parse_8472_comp(uint8_t val,nvlist_t * nvl)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
sff_parse_sfp(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_compliance(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_br(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_lengths(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_tech(uint8_t val,nvlist_t * nvl)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
sff_qsfp_parse_copperwave(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_casetemp(uint8_t val,nvlist_t * nvl)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
sff_qsfp_parse_extcomp(uint8_t val,nvlist_t * nvl)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
sff_qsfp_parse_options(const uint8_t * buf,nvlist_t * nvl)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
sff_qsfp_parse_diag(uint8_t val,nvlist_t * nvl)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
sff_parse_qsfp(const uint8_t * buf,nvlist_t * nvl)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
libsff_parse(const uint8_t * buf,size_t len,uint_t page,nvlist_t ** nvpp)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