1 /* $OpenBSD: sff.c,v 1.23 2019/10/24 18:54:10 bluhm Exp $ */
2
3 /*
4 * Copyright (c) 2019 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #ifndef SMALL
20
21 #include <sys/ioctl.h>
22
23 #include <net/if.h>
24
25 #include <math.h>
26 #include <ctype.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <limits.h>
35 #include <vis.h>
36
37 #include "ifconfig.h"
38
39 #ifndef nitems
40 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
41 #endif
42
43 #ifndef ISSET
44 #define ISSET(_w, _m) ((_w) & (_m))
45 #endif
46
47 #define SFF_THRESH_HI_ALARM 0
48 #define SFF_THRESH_LO_ALARM 1
49 #define SFF_THRESH_HI_WARN 2
50 #define SFF_THRESH_LO_WARN 3
51 #define SFF_THRESH_COUNT 4
52
53 #define SFF_THRESH_REG(_i) ((_i) * 2)
54
55 struct sff_thresholds {
56 float thresholds[SFF_THRESH_COUNT];
57 };
58
59 struct sff_media_map {
60 float factor_wavelength;
61 int scale_om1;
62 int scale_om2;
63 int scale_om3;
64 uint8_t connector_type;
65 uint8_t wavelength;
66 uint8_t dist_smf_m;
67 uint8_t dist_smf_km;
68 uint8_t dist_om1;
69 uint8_t dist_om2;
70 uint8_t dist_om3;
71 uint8_t dist_cu;
72 };
73
74 #define SFF8024_ID_UNKNOWN 0x00
75 #define SFF8024_ID_GBIC 0x01
76 #define SFF8024_ID_MOBO 0x02 /* Module/connector soldered to mobo */
77 /* using SFF-8472 */
78 #define SFF8024_ID_SFP 0x03 /* SFP/SFP+/SFP28 */
79 #define SFF8024_ID_300PIN_XBI 0x04 /* 300 pin XBI */
80 #define SFF8024_ID_XENPAK 0x05
81 #define SFF8024_ID_XFP 0x06
82 #define SFF8024_ID_XFF 0x07
83 #define SFF8024_ID_XFPE 0x08 /* XFP-E */
84 #define SFF8024_ID_XPAK 0x09
85 #define SFF8024_ID_X2 0x0a
86 #define SFF8024_ID_DWDM_SFP 0x0b /* DWDM-SFP/SFP+ */
87 /* not using SFF-8472 */
88 #define SFF8024_ID_QSFP 0x0c
89 #define SFF8024_ID_QSFP_PLUS 0x0d /* or later */
90 /* using SFF-8436/8665/8685 et al */
91 #define SFF8024_ID_CXP 0x0e /* or later */
92 #define SFF8024_ID_HD4X 0x0f /* shielded mini multilane HD 4X */
93 #define SFF8024_ID_HD8X 0x10 /* shielded mini multilane HD 8X */
94 #define SFF8024_ID_QSFP28 0x11 /* or later */
95 /* using SFF-8665 et al */
96 #define SFF8024_ID_CXP2 0x12 /* aka CXP28, or later */
97 #define SFF8024_ID_CDFP 0x13 /* style 1/style 2 */
98 #define SFF8024_ID_HD4X_FAN 0x14 /* shielded mini multilane HD 4X fanout */
99 #define SFF8024_ID_HD8X_FAN 0x15 /* shielded mini multilane HD 8X fanout */
100 #define SFF8024_ID_CDFP3 0x16 /* style 3 */
101 #define SFF8024_ID_uQSFP 0x17 /* microQSFP */
102 #define SFF8024_ID_QSFP_DD 0x18 /* QSFP-DD double density 8x */
103 /* INF-8628 */
104 #define SFF8024_ID_RESERVED 0x7f /* up to here is reserved */
105 /* 0x80 to 0xff is vendor specific */
106
107 #define SFF8024_ID_IS_RESERVED(_id) ((_id) <= SFF8024_ID_RESERVED)
108 #define SFF8024_ID_IS_VENDOR(_id) ((_id) > SFF8024_ID_RESERVED)
109
110 #define SFF8024_CON_UNKNOWN 0x00
111 #define SFF8024_CON_SC 0x01 /* Subscriber Connector */
112 #define SFF8024_CON_FC_1 0x02 /* Fibre Channel Style 1 copper */
113 #define SFF8024_CON_FC_2 0x03 /* Fibre Channel Style 2 copper */
114 #define SFF8024_CON_BNC_TNC 0x04 /* BNC/TNC */
115 #define SFF8024_CON_FC_COAX 0x05 /* Fibre Channel coax headers */
116 #define SFF8024_CON_FJ 0x06 /* Fibre Jack */
117 #define SFF8024_CON_LC 0x07 /* Lucent Connector */
118 #define SFF8024_CON_MT_RJ 0x08 /* Mechanical Transfer - Registered Jack */
119 #define SFF8024_CON_MU 0x09 /* Multiple Optical */
120 #define SFF8024_CON_SG 0x0a
121 #define SFF8024_CON_O_PIGTAIL 0x0b /* Optical Pigtail */
122 #define SFF8024_CON_MPO_1x12 0x0c /* Multifiber Parallel Optic 1x12 */
123 #define SFF8024_CON_MPO_2x16 0x0e /* Multifiber Parallel Optic 2x16 */
124 #define SFF8024_CON_HSSDC2 0x20 /* High Speed Serial Data Connector */
125 #define SFF8024_CON_Cu_PIGTAIL 0x21 /* Copper Pigtail */
126 #define SFF8024_CON_RJ45 0x22
127 #define SFF8024_CON_NO 0x23 /* No separable connector */
128 #define SFF8024_CON_MXC_2x16 0x24
129 #define SFF8024_CON_RESERVED 0x7f /* up to here is reserved */
130 /* 0x80 to 0xff is vendor specific */
131
132 #define SFF8024_CON_IS_RESERVED(_id) ((_id) <= SFF8024_CON_RESERVED)
133 #define SFF8024_CON_IS_VENDOR(_id) ((_id) > SFF8024_CON_RESERVED)
134
135 static const char *sff8024_id_names[] = {
136 [SFF8024_ID_UNKNOWN] = "Unknown",
137 [SFF8024_ID_GBIC] = "GBIC",
138 [SFF8024_ID_SFP] = "SFP",
139 [SFF8024_ID_300PIN_XBI] = "XBI",
140 [SFF8024_ID_XENPAK] = "XENPAK",
141 [SFF8024_ID_XFP] = "XFP",
142 [SFF8024_ID_XFF] = "XFF",
143 [SFF8024_ID_XFPE] = "XFPE",
144 [SFF8024_ID_XPAK] = "XPAK",
145 [SFF8024_ID_X2] = "X2",
146 [SFF8024_ID_DWDM_SFP] = "DWDM-SFP",
147 [SFF8024_ID_QSFP] = "QSFP",
148 [SFF8024_ID_QSFP_PLUS] = "QSFP+",
149 [SFF8024_ID_CXP] = "CXP",
150 [SFF8024_ID_HD4X] = "HD 4X",
151 [SFF8024_ID_HD8X] = "HD 8X",
152 [SFF8024_ID_QSFP28] = "QSFP28",
153 [SFF8024_ID_CXP2] = "CXP2",
154 [SFF8024_ID_CDFP] = "CDFP Style 1/2",
155 [SFF8024_ID_HD4X_FAN] = "HD 4X Fanout",
156 [SFF8024_ID_HD8X_FAN] = "HD 8X Fanout",
157 [SFF8024_ID_CDFP3] = "CDFP Style 3",
158 [SFF8024_ID_uQSFP] = "microQSFP",
159 [SFF8024_ID_QSFP_DD] = "QSFP-DD",
160 };
161
162 static const char *sff8024_con_names[] = {
163 [SFF8024_CON_UNKNOWN] = "Unknown",
164 [SFF8024_CON_SC] = "SC",
165 [SFF8024_CON_FC_1] = "FC Style 1",
166 [SFF8024_CON_FC_2] = "FC Style 2",
167 [SFF8024_CON_BNC_TNC] = "BNC/TNC",
168 [SFF8024_CON_FC_COAX] = "FC coax headers",
169 [SFF8024_CON_FJ] = "FJ",
170 [SFF8024_CON_LC] = "LC",
171 [SFF8024_CON_MT_RJ] = "MT-RJ",
172 [SFF8024_CON_MU] = "MU",
173 [SFF8024_CON_SG] = "SG",
174 [SFF8024_CON_O_PIGTAIL] = "AOC",
175 [SFF8024_CON_MPO_1x12] = "MPO 1x12",
176 [SFF8024_CON_MPO_2x16] = "MPO 2x16",
177 [SFF8024_CON_HSSDC2] = "HSSDC II",
178 [SFF8024_CON_Cu_PIGTAIL]
179 = "DAC",
180 [SFF8024_CON_RJ45] = "RJ45",
181 [SFF8024_CON_NO] = "No connector",
182 [SFF8024_CON_MXC_2x16] = "MXC 2x16",
183 };
184
185 #define SFF8472_ID 0 /* SFF8027 for identifier values */
186 #define SFF8472_EXT_ID 1
187 #define SFF8472_EXT_ID_UNSPECIFIED 0x00
188 #define SFF8472_EXT_ID_MOD_DEF_1 0x01
189 #define SFF8472_EXT_ID_MOD_DEF_2 0x02
190 #define SFF8472_EXT_ID_MOD_DEF_3 0x03
191 #define SFF8472_EXT_ID_2WIRE 0x04
192 #define SFF8472_EXT_ID_MOD_DEF_5 0x05
193 #define SFF8472_EXT_ID_MOD_DEF_6 0x06
194 #define SFF8472_EXT_ID_MOD_DEF_7 0x07
195 #define SFF8472_CON 2 /* SFF8027 for connector values */
196 #define SFF8472_DIST_SMF_KM 14
197 #define SFF8472_DIST_SMF_M 15
198 #define SFF8472_DIST_OM2 16
199 #define SFF8472_DIST_OM1 17
200 #define SFF8472_DIST_CU 18
201 #define SFF8472_DIST_OM3 19
202 #define SFF8472_VENDOR_START 20
203 #define SFF8472_VENDOR_END 35
204 #define SFF8472_PRODUCT_START 40
205 #define SFF8472_PRODUCT_END 55
206 #define SFF8472_REVISION_START 56
207 #define SFF8472_REVISION_END 59
208 #define SFF8472_WAVELENGTH 60
209 #define SFF8472_SERIAL_START 68
210 #define SFF8472_SERIAL_END 83
211 #define SFF8472_DATECODE 84
212 #define SFF8472_DDM_TYPE 92
213 #define SFF8472_DDM_TYPE_AVG_POWER (1U << 3)
214 #define SFF8472_DDM_TYPE_CAL_EXT (1U << 4)
215 #define SFF8472_DDM_TYPE_CAL_INT (1U << 5)
216 #define SFF8472_DDM_TYPE_IMPL (1U << 6)
217 #define SFF8472_COMPLIANCE 94
218 #define SFF8472_COMPLIANCE_NONE 0x00
219 #define SFF8472_COMPLIANCE_9_3 0x01 /* SFF-8472 Rev 9.3 */
220 #define SFF8472_COMPLIANCE_9_5 0x02 /* SFF-8472 Rev 9.5 */
221 #define SFF8472_COMPLIANCE_10_2 0x03 /* SFF-8472 Rev 10.2 */
222 #define SFF8472_COMPLIANCE_10_4 0x04 /* SFF-8472 Rev 10.4 */
223 #define SFF8472_COMPLIANCE_11_0 0x05 /* SFF-8472 Rev 11.0 */
224 #define SFF8472_COMPLIANCE_11_3 0x06 /* SFF-8472 Rev 11.3 */
225 #define SFF8472_COMPLIANCE_11_4 0x07 /* SFF-8472 Rev 11.4 */
226 #define SFF8472_COMPLIANCE_12_3 0x08 /* SFF-8472 Rev 12.3 */
227
228 static const struct sff_media_map sff8472_media_map = {
229 .connector_type = SFF8472_CON,
230 .wavelength = SFF8472_WAVELENGTH,
231 .factor_wavelength = 1.0,
232 .dist_smf_m = SFF8472_DIST_SMF_M,
233 .dist_smf_km = SFF8472_DIST_SMF_KM,
234 .dist_om1 = SFF8472_DIST_OM1,
235 .scale_om1 = 10,
236 .dist_om2 = SFF8472_DIST_OM2,
237 .scale_om2 = 10,
238 .dist_om3 = SFF8472_DIST_OM3,
239 .scale_om3 = 20,
240 .dist_cu = SFF8472_DIST_CU,
241 };
242
243 /*
244 * page 0xa2
245 */
246 #define SFF8472_AW_TEMP 0
247 #define SFF8472_AW_VCC 8
248 #define SFF8472_AW_TX_BIAS 16
249 #define SFF8472_AW_TX_POWER 24
250 #define SFF8472_AW_RX_POWER 32
251 #define ALRM_HIGH 0
252 #define ALRM_LOW 2
253 #define WARN_HIGH 4
254 #define WARN_LOW 6
255 #define SFF8472_DDM_TEMP 96
256 #define SFF8472_DDM_VCC 98
257 #define SFF8472_DDM_TX_BIAS 100
258 #define SFF8472_DDM_TX_POWER 102
259 #define SFF8472_DDM_RX_POWER 104
260 #define SFF8472_DDM_LASER 106 /* laser temp/wavelength */
261 /* optional */
262 #define SFF8472_DDM_TEC 108 /* Measured TEC current */
263 /* optional */
264
265 #define SFF_TEMP_FACTOR 256.0
266 #define SFF_VCC_FACTOR 10000.0
267 #define SFF_BIAS_FACTOR 500.0
268 #define SFF_POWER_FACTOR 10000.0
269
270 /*
271 * QSFP is defined by SFF-8436, but the management interface is
272 * updated and maintained by SFF-8636.
273 */
274
275 #define SFF8436_STATUS1 1
276 #define SFF8436_STATUS2 2
277 #define SFF8436_STATUS2_DNR (1 << 0) /* Data_Not_Ready */
278 #define SFF8436_STATUS2_INTL (1 << 1) /* Interrupt output state */
279 #define SFF8436_STATUS2_FLAT_MEM (1 << 2) /* Upper memory flat/paged */
280
281 #define SFF8436_TEMP 22
282 #define SFF8436_VCC 26
283
284 #define SFF8436_CHANNELS 4 /* number of TX and RX channels */
285 #define SFF8436_RX_POWER_BASE 34
286 #define SFF8436_RX_POWER(_i) (SFF8436_RX_POWER_BASE + ((_i) * 2))
287 #define SFF8436_TX_BIAS_BASE 42
288 #define SFF8436_TX_BIAS(_i) (SFF8436_TX_BIAS_BASE + ((_i) * 2))
289 #define SFF8436_TX_POWER_BASE 50
290 #define SFF8436_TX_POWER(_i) (SFF8436_TX_POWER_BASE + ((_i) * 2))
291
292 /* Upper Page 00h */
293
294 #define SFF8436_MAXCASETEMP 190 /* C */
295 #define SFF8436_MAXCASETEMP_DEFAULT 70 /* if SFF8436_MAXCASETEMP is 0 */
296
297 /* Upper page 03h */
298
299 #define SFF8436_AW_TEMP 128
300 #define SFF8436_AW_VCC 144
301 #define SFF8436_AW_RX_POWER 176
302 #define SFF8436_AW_TX_BIAS 184
303 #define SFF8436_AW_TX_POWER 192
304
305 /*
306 * XFP stuff is defined by INF-8077.
307 *
308 * The "Serial ID Memory Map" on page 1 contains the interesting strings
309 */
310
311 /* SFF-8636 and INF-8077 share a layout for various strings */
312
313 #define UPPER_CON 130 /* connector type */
314 #define UPPER_DIST_SMF 142
315 #define UPPER_DIST_OM3 143
316 #define UPPER_DIST_OM2 144
317 #define UPPER_DIST_OM1 145
318 #define UPPER_DIST_CU 146
319 #define UPPER_WAVELENGTH 186
320
321 #define UPPER_VENDOR_START 148
322 #define UPPER_VENDOR_END 163
323 #define UPPER_PRODUCT_START 168
324 #define UPPER_PRODUCT_END 183
325 #define UPPER_REVISION_START 184
326 #define UPPER_REVISION_END 185
327 #define UPPER_SERIAL_START 196
328 #define UPPER_SERIAL_END 211
329 #define UPPER_DATECODE 212
330 #define UPPER_LOT_START 218
331 #define UPPER_LOT_END 219
332
333 static const struct sff_media_map upper_media_map = {
334 .connector_type = UPPER_CON,
335 .wavelength = UPPER_WAVELENGTH,
336 .factor_wavelength = 20.0,
337 .dist_smf_m = 0,
338 .dist_smf_km = UPPER_DIST_SMF,
339 .dist_om1 = UPPER_DIST_OM1,
340 .scale_om1 = 1,
341 .dist_om2 = UPPER_DIST_OM1,
342 .scale_om2 = 1,
343 .dist_om3 = UPPER_DIST_OM3,
344 .scale_om3 = 2,
345 .dist_cu = UPPER_DIST_CU,
346 };
347
348 static void hexdump(const void *, size_t);
349 static int if_sff8472(int, const struct if_sffpage *);
350 static int if_sff8636(int, const struct if_sffpage *);
351 static int if_inf8077(int, const struct if_sffpage *);
352
353 static const char *
sff_id_name(uint8_t id)354 sff_id_name(uint8_t id)
355 {
356 const char *name = NULL;
357
358 if (id < nitems(sff8024_id_names)) {
359 name = sff8024_id_names[id];
360 if (name != NULL)
361 return (name);
362 }
363
364 if (SFF8024_ID_IS_VENDOR(id))
365 return ("Vendor Specific");
366
367 return ("Reserved");
368 }
369
370 static const char *
sff_con_name(uint8_t id)371 sff_con_name(uint8_t id)
372 {
373 const char *name = NULL;
374
375 if (id < nitems(sff8024_con_names)) {
376 name = sff8024_con_names[id];
377 if (name != NULL)
378 return (name);
379 }
380
381 if (SFF8024_CON_IS_VENDOR(id))
382 return ("Vendor Specific");
383
384 return ("Reserved");
385 }
386
387 static void
if_sffpage_init(struct if_sffpage * sff,uint8_t addr,uint8_t page)388 if_sffpage_init(struct if_sffpage *sff, uint8_t addr, uint8_t page)
389 {
390 memset(sff, 0, sizeof(*sff));
391
392 if (strlcpy(sff->sff_ifname, ifname, sizeof(sff->sff_ifname)) >=
393 sizeof(sff->sff_ifname))
394 errx(1, "interface name too long");
395
396 sff->sff_addr = addr;
397 sff->sff_page = page;
398 }
399
400 static void
if_sffpage_dump(const struct if_sffpage * sff)401 if_sffpage_dump(const struct if_sffpage *sff)
402 {
403 printf("%s: addr %02x", ifname, sff->sff_addr);
404 if (sff->sff_addr == IFSFF_ADDR_EEPROM)
405 printf(" page %u", sff->sff_page);
406 putchar('\n');
407 hexdump(sff->sff_data, sizeof(sff->sff_data));
408 }
409
410 int
if_sff_info(int dump)411 if_sff_info(int dump)
412 {
413 struct if_sffpage pg0;
414 int error = 0;
415 uint8_t id, ext_id;
416
417 if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 0);
418 if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1) {
419 if (errno == ENXIO) {
420 /* try 1 for XFP cos myx which can't switch pages... */
421 if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
422 if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
423 return (-1);
424 } else
425 return (-1);
426 }
427
428 if (dump)
429 if_sffpage_dump(&pg0);
430
431 id = pg0.sff_data[0]; /* SFF8472_ID */
432
433 printf("\ttransceiver: %s ", sff_id_name(id));
434 switch (id) {
435 case SFF8024_ID_SFP:
436 ext_id = pg0.sff_data[SFF8472_EXT_ID];
437 if (ext_id != SFF8472_EXT_ID_2WIRE) {
438 printf("extended-id %02xh\n", ext_id);
439 break;
440 }
441 /* FALLTHROUGH */
442 case SFF8024_ID_GBIC:
443 error = if_sff8472(dump, &pg0);
444 break;
445 case SFF8024_ID_XFP:
446 if (pg0.sff_page != 1) {
447 if_sffpage_init(&pg0, IFSFF_ADDR_EEPROM, 1);
448 if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg0) == -1)
449 return (-1);
450 if (dump)
451 if_sffpage_dump(&pg0);
452 }
453 error = if_inf8077(dump, &pg0);
454 break;
455 case SFF8024_ID_QSFP:
456 case SFF8024_ID_QSFP_PLUS:
457 case SFF8024_ID_QSFP28:
458 error = if_sff8636(dump, &pg0);
459 break;
460 default:
461 printf("\n");
462 break;
463 }
464
465 return (error);
466 }
467
468 static void
if_sff_ascii_print(const struct if_sffpage * sff,const char * name,size_t start,size_t end,const char * trailer)469 if_sff_ascii_print(const struct if_sffpage *sff, const char *name,
470 size_t start, size_t end, const char *trailer)
471 {
472 const uint8_t *d = sff->sff_data;
473 int ch;
474
475 for (;;) {
476 ch = d[start];
477 if (!isspace(ch) && ch != '\0')
478 break;
479
480 start++;
481 if (start == end)
482 return;
483 }
484
485 printf("%s", name);
486
487 for (;;) {
488 ch = d[end];
489 if (!isspace(ch) && ch != '\0')
490 break;
491
492 end--;
493 }
494
495 do {
496 char dst[8];
497 vis(dst, d[start], VIS_TAB | VIS_NL, 0);
498 printf("%s", dst);
499 } while (++start <= end);
500
501 printf("%s", trailer);
502 }
503
504 static void
if_sff_date_print(const struct if_sffpage * sff,const char * name,size_t start,const char * trailer)505 if_sff_date_print(const struct if_sffpage *sff, const char *name,
506 size_t start, const char *trailer)
507 {
508 const uint8_t *d = sff->sff_data + start;
509 size_t i;
510
511 /* YYMMDD */
512 for (i = 0; i < 6; i++) {
513 if (!isdigit(d[i])) {
514 if_sff_ascii_print(sff, name, start,
515 start + 5, trailer);
516 return;
517 }
518 }
519
520 printf("%s20%c%c-%c%c-%c%c%s", name,
521 d[0], d[1], d[2], d[3], d[4], d[5], trailer);
522 }
523
524 static int16_t
if_sff_int(const struct if_sffpage * sff,size_t start)525 if_sff_int(const struct if_sffpage *sff, size_t start)
526 {
527 const uint8_t *d = sff->sff_data + start;
528
529 return (d[0] << 8 | d[1]);
530 }
531
532 static uint16_t
if_sff_uint(const struct if_sffpage * sff,size_t start)533 if_sff_uint(const struct if_sffpage *sff, size_t start)
534 {
535 const uint8_t *d = sff->sff_data + start;
536
537 return (d[0] << 8 | d[1]);
538 }
539
540 static float
if_sff_power2dbm(const struct if_sffpage * sff,size_t start)541 if_sff_power2dbm(const struct if_sffpage *sff, size_t start)
542 {
543 const uint8_t *d = sff->sff_data + start;
544
545 int power = d[0] << 8 | d[1];
546 return (10.0 * log10f((float)power / 10000.0));
547 }
548
549 static void
if_sff_printalarm(const char * unit,int range,float actual,float alrm_high,float alrm_low,float warn_high,float warn_log)550 if_sff_printalarm(const char *unit, int range, float actual,
551 float alrm_high, float alrm_low, float warn_high, float warn_log)
552 {
553 printf("%.02f%s", actual, unit);
554 if (range == 1)
555 printf(" (low %.02f%s, high %.02f%s)", alrm_low,
556 unit, alrm_high, unit);
557
558 if(actual > alrm_high || actual < alrm_low)
559 printf(" [ALARM]");
560 else if(actual > warn_high || actual < warn_log)
561 printf(" [WARNING]");
562 }
563
564 static void
if_sff_printdist(const char * type,int value,int scale)565 if_sff_printdist(const char *type, int value, int scale)
566 {
567 int distance = value * scale;
568
569 if (value == 0)
570 return;
571
572 if (distance < 10000)
573 printf (", %s%u%s", value > 254 ? ">" : "", distance, type);
574 else
575 printf (", %s%0.1fk%s", value > 254 ? ">" : "",
576 distance / 1000.0, type);
577 }
578
579 static void
if_sff_printmedia(const struct if_sffpage * pg,const struct sff_media_map * m)580 if_sff_printmedia(const struct if_sffpage *pg, const struct sff_media_map *m)
581 {
582 uint8_t con;
583 unsigned int wavelength;
584
585 con = pg->sff_data[m->connector_type];
586 printf("%s", sff_con_name(con));
587
588 wavelength = if_sff_uint(pg, m->wavelength);
589 switch (wavelength) {
590 case 0x0000:
591 /* not known or is unavailable */
592 break;
593 /* Copper Cable */
594 case 0x0100: /* SFF-8431 Appendix E */
595 case 0x0400: /* SFF-8431 limiting */
596 case 0x0c00: /* SFF-8431 limiting and FC-PI-4 limiting */
597 break;
598 default:
599 printf(", %.f nm", wavelength / m->factor_wavelength);
600 }
601
602 if (m->dist_smf_m != 0 &&
603 pg->sff_data[m->dist_smf_m] > 0 &&
604 pg->sff_data[m->dist_smf_m] < 255)
605 if_sff_printdist("m SMF", pg->sff_data[m->dist_smf_m], 100);
606 else
607 if_sff_printdist("km SMF", pg->sff_data[m->dist_smf_km], 1);
608 if_sff_printdist("m OM1", pg->sff_data[m->dist_om1], m->scale_om1);
609 if_sff_printdist("m OM2", pg->sff_data[m->dist_om2], m->scale_om2);
610 if_sff_printdist("m OM3", pg->sff_data[m->dist_om3], m->scale_om3);
611 if_sff_printdist("m", pg->sff_data[m->dist_cu], 1);
612 }
613
614 static int
if_sff8472(int dump,const struct if_sffpage * pg0)615 if_sff8472(int dump, const struct if_sffpage *pg0)
616 {
617 struct if_sffpage ddm;
618 uint8_t ddm_types;
619
620 if_sff_printmedia(pg0, &sff8472_media_map);
621
622 printf("\n\tmodel: ");
623 if_sff_ascii_print(pg0, "",
624 SFF8472_VENDOR_START, SFF8472_VENDOR_END, " ");
625 if_sff_ascii_print(pg0, "",
626 SFF8472_PRODUCT_START, SFF8472_PRODUCT_END, "");
627 if_sff_ascii_print(pg0, " rev ",
628 SFF8472_REVISION_START, SFF8472_REVISION_END, "");
629
630 if_sff_ascii_print(pg0, "\n\tserial: ",
631 SFF8472_SERIAL_START, SFF8472_SERIAL_END, ", ");
632 if_sff_date_print(pg0, "date: ", SFF8472_DATECODE, "\n");
633
634 ddm_types = pg0->sff_data[SFF8472_DDM_TYPE];
635 if (pg0->sff_data[SFF8472_COMPLIANCE] == SFF8472_COMPLIANCE_NONE ||
636 !ISSET(ddm_types, SFF8472_DDM_TYPE_IMPL))
637 return (0);
638
639 if_sffpage_init(&ddm, IFSFF_ADDR_DDM, 0);
640 if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&ddm) == -1)
641 return (-1);
642
643 if (dump)
644 if_sffpage_dump(&ddm);
645
646 if (ISSET(ddm_types, SFF8472_DDM_TYPE_CAL_EXT)) {
647 printf("\tcalibration: external "
648 "(WARNING: needs more code)\n");
649 }
650
651 printf("\tvoltage: ");
652 if_sff_printalarm(" V", 0,
653 if_sff_uint(&ddm, SFF8472_DDM_VCC) / SFF_VCC_FACTOR,
654 if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_HIGH) / SFF_VCC_FACTOR,
655 if_sff_uint(&ddm, SFF8472_AW_VCC + ALRM_LOW) / SFF_VCC_FACTOR,
656 if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_HIGH) / SFF_VCC_FACTOR,
657 if_sff_uint(&ddm, SFF8472_AW_VCC + WARN_LOW) / SFF_VCC_FACTOR);
658
659 printf(", bias current: ");
660 if_sff_printalarm(" mA", 0,
661 if_sff_uint(&ddm, SFF8472_DDM_TX_BIAS) / SFF_BIAS_FACTOR,
662 if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_HIGH) / SFF_BIAS_FACTOR,
663 if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + ALRM_LOW) / SFF_BIAS_FACTOR,
664 if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_HIGH) / SFF_BIAS_FACTOR,
665 if_sff_uint(&ddm, SFF8472_AW_TX_BIAS + WARN_LOW) / SFF_BIAS_FACTOR);
666
667 printf("\n\ttemp: ");
668 if_sff_printalarm(" C", 1,
669 if_sff_int(&ddm, SFF8472_DDM_TEMP) / SFF_TEMP_FACTOR,
670 if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_HIGH) / SFF_TEMP_FACTOR,
671 if_sff_int(&ddm, SFF8472_AW_TEMP + ALRM_LOW) / SFF_TEMP_FACTOR,
672 if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_HIGH) / SFF_TEMP_FACTOR,
673 if_sff_int(&ddm, SFF8472_AW_TEMP + WARN_LOW) / SFF_TEMP_FACTOR);
674
675 printf("\n\ttx: ");
676 if_sff_printalarm(" dBm", 1,
677 if_sff_power2dbm(&ddm, SFF8472_DDM_TX_POWER),
678 if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_HIGH),
679 if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + ALRM_LOW),
680 if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_HIGH),
681 if_sff_power2dbm(&ddm, SFF8472_AW_TX_POWER + WARN_LOW));
682
683 printf("\n\trx: ");
684 if_sff_printalarm(" dBm", 1,
685 if_sff_power2dbm(&ddm, SFF8472_DDM_RX_POWER),
686 if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_HIGH),
687 if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + ALRM_LOW),
688 if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_HIGH),
689 if_sff_power2dbm(&ddm, SFF8472_AW_RX_POWER + WARN_LOW));
690
691 putchar('\n');
692 return (0);
693 }
694
695 static void
if_upper_strings(const struct if_sffpage * pg)696 if_upper_strings(const struct if_sffpage *pg)
697 {
698 if_sff_printmedia(pg, &upper_media_map);
699
700 printf("\n\tmodel: ");
701 if_sff_ascii_print(pg, "",
702 UPPER_VENDOR_START, UPPER_VENDOR_END, " ");
703 if_sff_ascii_print(pg, "",
704 UPPER_PRODUCT_START, UPPER_PRODUCT_END, "");
705 if_sff_ascii_print(pg, " rev ",
706 UPPER_REVISION_START, UPPER_REVISION_END, "");
707
708 if_sff_ascii_print(pg, "\n\tserial: ",
709 UPPER_SERIAL_START, UPPER_SERIAL_END, " ");
710 if_sff_date_print(pg, "date: ", UPPER_DATECODE, " ");
711 if_sff_ascii_print(pg, "lot: ",
712 UPPER_LOT_START, UPPER_LOT_END, "");
713
714 putchar('\n');
715 }
716
717 static int
if_inf8077(int dump,const struct if_sffpage * pg1)718 if_inf8077(int dump, const struct if_sffpage *pg1)
719 {
720 if_upper_strings(pg1);
721
722 return (0);
723 }
724
725 static int
if_sff8636_thresh(int dump,const struct if_sffpage * pg0)726 if_sff8636_thresh(int dump, const struct if_sffpage *pg0)
727 {
728 struct if_sffpage pg3;
729 unsigned int i;
730 struct sff_thresholds temp, vcc, tx, rx, bias;
731
732 if_sffpage_init(&pg3, IFSFF_ADDR_EEPROM, 3);
733 if (ioctl(sock, SIOCGIFSFFPAGE, (caddr_t)&pg3) == -1) {
734 if (dump)
735 warn("%s SIOCGIFSFFPAGE page 3", ifname);
736 return (-1);
737 }
738
739 if (dump)
740 if_sffpage_dump(&pg3);
741
742 if (pg3.sff_data[0x7f] != 3) { /* just in case... */
743 if (dump) {
744 warnx("%s SIOCGIFSFFPAGE: page select unsupported",
745 ifname);
746 }
747 return (-1);
748 }
749
750 for (i = 0; i < SFF_THRESH_COUNT; i++) {
751 temp.thresholds[i] = if_sff_int(&pg3,
752 SFF8436_AW_TEMP + SFF_THRESH_REG(i)) / SFF_TEMP_FACTOR;
753
754 vcc.thresholds[i] = if_sff_uint(&pg3,
755 SFF8436_AW_VCC + SFF_THRESH_REG(i)) / SFF_VCC_FACTOR;
756
757 rx.thresholds[i] = if_sff_power2dbm(&pg3,
758 SFF8436_AW_RX_POWER + SFF_THRESH_REG(i));
759
760 bias.thresholds[i] = if_sff_uint(&pg3,
761 SFF8436_AW_TX_BIAS + SFF_THRESH_REG(i)) / SFF_BIAS_FACTOR;
762
763 tx.thresholds[i] = if_sff_power2dbm(&pg3,
764 SFF8436_AW_TX_POWER + SFF_THRESH_REG(i));
765 }
766
767 printf("\ttemp: ");
768 if_sff_printalarm(" C", 1,
769 if_sff_int(&pg3, SFF8436_TEMP) / SFF_TEMP_FACTOR,
770 temp.thresholds[SFF_THRESH_HI_ALARM],
771 temp.thresholds[SFF_THRESH_LO_ALARM],
772 temp.thresholds[SFF_THRESH_HI_WARN],
773 temp.thresholds[SFF_THRESH_LO_WARN]);
774 printf("\n");
775
776 printf("\tvoltage: ");
777 if_sff_printalarm(" V", 1,
778 if_sff_uint(&pg3, SFF8436_VCC) / SFF_VCC_FACTOR,
779 vcc.thresholds[SFF_THRESH_HI_ALARM],
780 vcc.thresholds[SFF_THRESH_LO_ALARM],
781 vcc.thresholds[SFF_THRESH_HI_WARN],
782 vcc.thresholds[SFF_THRESH_LO_WARN]);
783 printf("\n");
784
785 for (i = 0; i < SFF8436_CHANNELS; i++) {
786 unsigned int channel = i + 1;
787
788 printf("\tchannel %u bias current: ", channel);
789 if_sff_printalarm(" mA", 1,
790 if_sff_uint(&pg3, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR,
791 bias.thresholds[SFF_THRESH_HI_ALARM],
792 bias.thresholds[SFF_THRESH_LO_ALARM],
793 bias.thresholds[SFF_THRESH_HI_WARN],
794 bias.thresholds[SFF_THRESH_LO_WARN]);
795 printf("\n");
796
797 printf("\tchannel %u tx: ", channel);
798 if_sff_printalarm(" dBm", 1,
799 if_sff_power2dbm(&pg3, SFF8436_TX_POWER(i)),
800 tx.thresholds[SFF_THRESH_HI_ALARM],
801 tx.thresholds[SFF_THRESH_LO_ALARM],
802 tx.thresholds[SFF_THRESH_HI_WARN],
803 tx.thresholds[SFF_THRESH_LO_WARN]);
804 printf("\n");
805
806 printf("\tchannel %u rx: ", channel);
807 if_sff_printalarm(" dBm", 1,
808 if_sff_power2dbm(&pg3, SFF8436_RX_POWER(i)),
809 rx.thresholds[SFF_THRESH_HI_ALARM],
810 rx.thresholds[SFF_THRESH_LO_ALARM],
811 rx.thresholds[SFF_THRESH_HI_WARN],
812 rx.thresholds[SFF_THRESH_LO_WARN]);
813 printf("\n");
814 }
815
816 return (0);
817 }
818
819 static int
if_sff8636(int dump,const struct if_sffpage * pg0)820 if_sff8636(int dump, const struct if_sffpage *pg0)
821 {
822 int16_t temp;
823 uint8_t maxcasetemp;
824 uint8_t flat;
825 unsigned int i;
826
827 if_upper_strings(pg0);
828
829 if (pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_DNR) {
830 printf("\tmonitor data not ready\n");
831 return (0);
832 }
833
834 maxcasetemp = pg0->sff_data[SFF8436_MAXCASETEMP];
835 if (maxcasetemp == 0x00)
836 maxcasetemp = SFF8436_MAXCASETEMP_DEFAULT;
837 printf("\tmax case temp: %u C\n", maxcasetemp);
838
839 temp = if_sff_int(pg0, SFF8436_TEMP);
840 /* the temp reading look unset, assume the rest will be unset too */
841 if ((uint16_t)temp == 0 || (uint16_t)temp == 0xffffU) {
842 if (!dump)
843 return (0);
844 }
845
846 flat = pg0->sff_data[SFF8436_STATUS2] & SFF8436_STATUS2_FLAT_MEM;
847 if (!flat && if_sff8636_thresh(dump, pg0) == 0) {
848 if (!dump)
849 return (0);
850 }
851
852 printf("\t");
853 printf("temp: %.02f%s", temp / SFF_TEMP_FACTOR, " C");
854 printf(", ");
855 printf("voltage: %.02f%s",
856 if_sff_uint(pg0, SFF8436_VCC) / SFF_VCC_FACTOR, " V");
857 printf("\n");
858
859 for (i = 0; i < SFF8436_CHANNELS; i++) {
860 printf("\t");
861 printf("channel %u: ", i + 1);
862 printf("bias current: %.02f mA",
863 if_sff_uint(pg0, SFF8436_TX_BIAS(i)) / SFF_BIAS_FACTOR);
864 printf(", ");
865 printf("rx: %.02f dBm",
866 if_sff_power2dbm(pg0, SFF8436_RX_POWER(i)));
867 printf(", ");
868 printf("tx: %.02f dBm",
869 if_sff_power2dbm(pg0, SFF8436_TX_POWER(i)));
870 printf("\n");
871 }
872
873 return (0);
874 }
875
876 static int
printable(int ch)877 printable(int ch)
878 {
879 if (ch == '\0')
880 return ('_');
881 if (!isprint(ch))
882 return ('~');
883
884 return (ch);
885 }
886
887 static void
hexdump(const void * d,size_t datalen)888 hexdump(const void *d, size_t datalen)
889 {
890 const uint8_t *data = d;
891 int i, j = 0;
892
893 for (i = 0; i < datalen; i += j) {
894 printf("% 4d: ", i);
895 for (j = 0; j < 16 && i+j < datalen; j++)
896 printf("%02x ", data[i + j]);
897 while (j++ < 16)
898 printf(" ");
899 printf("|");
900 for (j = 0; j < 16 && i+j < datalen; j++)
901 putchar(printable(data[i + j]));
902 printf("|\n");
903 }
904 }
905
906 #endif /* SMALL */
907