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