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