xref: /freebsd/sbin/ifconfig/sfp.c (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
1 /*-
2  * Copyright (c) 2014 Alexander V. Chernikov. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #ifndef lint
27 static const char rcsid[] =
28   "$FreeBSD$";
29 #endif /* not lint */
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 
36 #include <net/if.h>
37 #include <net/sff8436.h>
38 #include <net/sff8472.h>
39 
40 #include <math.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 
49 #include "ifconfig.h"
50 
51 struct i2c_info {
52 	int fd;			/* fd to issue SIOCGI2C */
53 	int error;		/* Store first error */
54 	int qsfp;		/* True if transceiver is QSFP */
55 	int do_diag;		/* True if we need to request DDM */
56 	struct ifreq *ifr;	/* Pointer to pre-filled ifreq */
57 };
58 
59 static int read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off,
60     uint8_t len, uint8_t *buf);
61 static void dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off,
62     uint8_t len);
63 
64 struct _nv {
65 	int v;
66 	const char *n;
67 };
68 
69 const char *find_value(struct _nv *x, int value);
70 const char *find_zero_bit(struct _nv *x, int value, int sz);
71 
72 /* SFF-8472 Rev. 11.4 table 3.4: Connector values */
73 static struct _nv conn[] = {
74 	{ 0x00, "Unknown" },
75 	{ 0x01, "SC" },
76 	{ 0x02, "Fibre Channel Style 1 copper" },
77 	{ 0x03, "Fibre Channel Style 2 copper" },
78 	{ 0x04, "BNC/TNC" },
79 	{ 0x05, "Fibre Channel coaxial" },
80 	{ 0x06, "FiberJack" },
81 	{ 0x07, "LC" },
82 	{ 0x08, "MT-RJ" },
83 	{ 0x09, "MU" },
84 	{ 0x0A, "SG" },
85 	{ 0x0B, "Optical pigtail" },
86 	{ 0x0C, "MPO Parallel Optic" },
87 	{ 0x20, "HSSDC II" },
88 	{ 0x21, "Copper pigtail" },
89 	{ 0x22, "RJ45" },
90 	{ 0x23, "No separate connector" }, /* SFF-8436 */
91 	{ 0, NULL }
92 };
93 
94 /* SFF-8472 Rev. 11.4 table 3.5: Transceiver codes */
95 /* 10G Ethernet/IB compliance codes, byte 3 */
96 static struct _nv eth_10g[] = {
97 	{ 0x80, "10G Base-ER" },
98 	{ 0x40, "10G Base-LRM" },
99 	{ 0x20, "10G Base-LR" },
100 	{ 0x10, "10G Base-SR" },
101 	{ 0x08, "1X SX" },
102 	{ 0x04, "1X LX" },
103 	{ 0x02, "1X Copper Active" },
104 	{ 0x01, "1X Copper Passive" },
105 	{ 0, NULL }
106 };
107 
108 /* Ethernet compliance codes, byte 6 */
109 static struct _nv eth_compat[] = {
110 	{ 0x80, "BASE-PX" },
111 	{ 0x40, "BASE-BX10" },
112 	{ 0x20, "100BASE-FX" },
113 	{ 0x10, "100BASE-LX/LX10" },
114 	{ 0x08, "1000BASE-T" },
115 	{ 0x04, "1000BASE-CX" },
116 	{ 0x02, "1000BASE-LX" },
117 	{ 0x01, "1000BASE-SX" },
118 	{ 0, NULL }
119 };
120 
121 /* FC link length, byte 7 */
122 static struct _nv fc_len[] = {
123 	{ 0x80, "very long distance" },
124 	{ 0x40, "short distance" },
125 	{ 0x20, "intermediate distance" },
126 	{ 0x10, "long distance" },
127 	{ 0x08, "medium distance" },
128 	{ 0, NULL }
129 };
130 
131 /* Channel/Cable technology, byte 7-8 */
132 static struct _nv cab_tech[] = {
133 	{ 0x0400, "Shortwave laser (SA)" },
134 	{ 0x0200, "Longwave laser (LC)" },
135 	{ 0x0100, "Electrical inter-enclosure (EL)" },
136 	{ 0x80, "Electrical intra-enclosure (EL)" },
137 	{ 0x40, "Shortwave laser (SN)" },
138 	{ 0x20, "Shortwave laser (SL)" },
139 	{ 0x10, "Longwave laser (LL)" },
140 	{ 0x08, "Active Cable" },
141 	{ 0x04, "Passive Cable" },
142 	{ 0, NULL }
143 };
144 
145 /* FC Transmission media, byte 9 */
146 static struct _nv fc_media[] = {
147 	{ 0x80, "Twin Axial Pair" },
148 	{ 0x40, "Twisted Pair" },
149 	{ 0x20, "Miniature Coax" },
150 	{ 0x10, "Viao Coax" },
151 	{ 0x08, "Miltimode, 62.5um" },
152 	{ 0x04, "Multimode, 50um" },
153 	{ 0x02, "" },
154 	{ 0x01, "Single Mode" },
155 	{ 0, NULL }
156 };
157 
158 /* FC Speed, byte 10 */
159 static struct _nv fc_speed[] = {
160 	{ 0x80, "1200 MBytes/sec" },
161 	{ 0x40, "800 MBytes/sec" },
162 	{ 0x20, "1600 MBytes/sec" },
163 	{ 0x10, "400 MBytes/sec" },
164 	{ 0x08, "3200 MBytes/sec" },
165 	{ 0x04, "200 MBytes/sec" },
166 	{ 0x01, "100 MBytes/sec" },
167 	{ 0, NULL }
168 };
169 
170 /* SFF-8436 Rev. 4.8 table 33: Specification compliance  */
171 
172 /* 10/40G Ethernet compliance codes, byte 128 + 3 */
173 static struct _nv eth_1040g[] = {
174 	{ 0x80, "Reserved" },
175 	{ 0x40, "10GBASE-LRM" },
176 	{ 0x20, "10GBASE-LR" },
177 	{ 0x10, "10GBASE-SR" },
178 	{ 0x08, "40GBASE-CR4" },
179 	{ 0x04, "40GBASE-SR4" },
180 	{ 0x02, "40GBASE-LR4" },
181 	{ 0x01, "40G Active Cable" },
182 	{ 0, NULL }
183 };
184 
185 /* SFF-8636 Rev. 2.5 table 6.3: Revision compliance */
186 static struct _nv rev_compl[] = {
187 	{ 0x1, "SFF-8436 rev <=4.8" },
188 	{ 0x2, "SFF-8436 rev <=4.8" },
189 	{ 0x3, "SFF-8636 rev <=1.3" },
190 	{ 0x4, "SFF-8636 rev <=1.4" },
191 	{ 0x5, "SFF-8636 rev <=1.5" },
192 	{ 0x6, "SFF-8636 rev <=2.0" },
193 	{ 0x7, "SFF-8636 rev <=2.5" },
194 	{ 0x0, "Unspecified" }
195 };
196 
197 const char *
198 find_value(struct _nv *x, int value)
199 {
200 	for (; x->n != NULL; x++)
201 		if (x->v == value)
202 			return (x->n);
203 	return (NULL);
204 }
205 
206 const char *
207 find_zero_bit(struct _nv *x, int value, int sz)
208 {
209 	int v, m;
210 	const char *s;
211 
212 	v = 1;
213 	for (v = 1, m = 1 << (8 * sz); v < m; v *= 2) {
214 		if ((value & v) == 0)
215 			continue;
216 		if ((s = find_value(x, value & v)) != NULL) {
217 			value &= ~v;
218 			return (s);
219 		}
220 	}
221 
222 	return (NULL);
223 }
224 
225 static void
226 convert_sff_identifier(char *buf, size_t size, uint8_t value)
227 {
228 	const char *x;
229 
230 	x = NULL;
231 	if (value <= SFF_8024_ID_LAST)
232 		x = sff_8024_id[value];
233 	else {
234 		if (value > 0x80)
235 			x = "Vendor specific";
236 		else
237 			x = "Reserved";
238 	}
239 
240 	snprintf(buf, size, "%s", x);
241 }
242 
243 static void
244 convert_sff_connector(char *buf, size_t size, uint8_t value)
245 {
246 	const char *x;
247 
248 	if ((x = find_value(conn, value)) == NULL) {
249 		if (value >= 0x0D && value <= 0x1F)
250 			x = "Unallocated";
251 		else if (value >= 0x24 && value <= 0x7F)
252 			x = "Unallocated";
253 		else
254 			x = "Vendor specific";
255 	}
256 
257 	snprintf(buf, size, "%s", x);
258 }
259 
260 static void
261 convert_sff_rev_compliance(char *buf, size_t size, uint8_t value)
262 {
263 	const char *x;
264 
265 	if (value > 0x07)
266 		x = "Unallocated";
267 	else
268 		x = find_value(rev_compl, value);
269 
270 	snprintf(buf, size, "%s", x);
271 }
272 
273 static void
274 get_sfp_identifier(struct i2c_info *ii, char *buf, size_t size)
275 {
276 	uint8_t data;
277 
278 	read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &data);
279 	convert_sff_identifier(buf, size, data);
280 }
281 
282 static void
283 get_sfp_connector(struct i2c_info *ii, char *buf, size_t size)
284 {
285 	uint8_t data;
286 
287 	read_i2c(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, &data);
288 	convert_sff_connector(buf, size, data);
289 }
290 
291 static void
292 get_qsfp_identifier(struct i2c_info *ii, char *buf, size_t size)
293 {
294 	uint8_t data;
295 
296 	read_i2c(ii, SFF_8436_BASE, SFF_8436_ID, 1, &data);
297 	convert_sff_identifier(buf, size, data);
298 }
299 
300 static void
301 get_qsfp_connector(struct i2c_info *ii, char *buf, size_t size)
302 {
303 	uint8_t data;
304 
305 	read_i2c(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, &data);
306 	convert_sff_connector(buf, size, data);
307 }
308 
309 static void
310 printf_sfp_transceiver_descr(struct i2c_info *ii, char *buf, size_t size)
311 {
312 	char xbuf[12];
313 	const char *tech_class, *tech_len, *tech_tech, *tech_media, *tech_speed;
314 
315 	tech_class = NULL;
316 	tech_len = NULL;
317 	tech_tech = NULL;
318 	tech_media = NULL;
319 	tech_speed = NULL;
320 
321 	/* Read bytes 3-10 at once */
322 	read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, &xbuf[3]);
323 
324 	/* Check 10G ethernet first */
325 	tech_class = find_zero_bit(eth_10g, xbuf[3], 1);
326 	if (tech_class == NULL) {
327 		/* No match. Try 1G */
328 		tech_class = find_zero_bit(eth_compat, xbuf[6], 1);
329 	}
330 
331 	tech_len = find_zero_bit(fc_len, xbuf[7], 1);
332 	tech_tech = find_zero_bit(cab_tech, xbuf[7] << 8 | xbuf[8], 2);
333 	tech_media = find_zero_bit(fc_media, xbuf[9], 1);
334 	tech_speed = find_zero_bit(fc_speed, xbuf[10], 1);
335 
336 	printf("Class: %s\n", tech_class);
337 	printf("Length: %s\n", tech_len);
338 	printf("Tech: %s\n", tech_tech);
339 	printf("Media: %s\n", tech_media);
340 	printf("Speed: %s\n", tech_speed);
341 }
342 
343 static void
344 get_sfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size)
345 {
346 	const char *tech_class;
347 	uint8_t code;
348 
349 	unsigned char qbuf[8];
350 	read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, (uint8_t *)qbuf);
351 
352 	/* Check 10G Ethernet/IB first */
353 	read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, &code);
354 	tech_class = find_zero_bit(eth_10g, code, 1);
355 	if (tech_class == NULL) {
356 		/* No match. Try Ethernet 1G */
357 		read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3,
358 		    1, (caddr_t)&code);
359 		tech_class = find_zero_bit(eth_compat, code, 1);
360 	}
361 
362 	if (tech_class == NULL)
363 		tech_class = "Unknown";
364 
365 	snprintf(buf, size, "%s", tech_class);
366 }
367 
368 static void
369 get_qsfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size)
370 {
371 	const char *tech_class;
372 	uint8_t code;
373 
374 	/* Check 10/40G Ethernet class only */
375 	read_i2c(ii, SFF_8436_BASE, SFF_8436_CODE_E1040G, 1, &code);
376 	tech_class = find_zero_bit(eth_1040g, code, 1);
377 	if (tech_class == NULL)
378 		tech_class = "Unknown";
379 
380 	snprintf(buf, size, "%s", tech_class);
381 }
382 
383 /*
384  * Print SFF-8472/SFF-8436 string to supplied buffer.
385  * All (vendor-specific) strings are padded right with '0x20'.
386  */
387 static void
388 convert_sff_name(char *buf, size_t size, char *xbuf)
389 {
390 	char *p;
391 
392 	for (p = &xbuf[16]; *(p - 1) == 0x20; p--)
393 		;
394 	*p = '\0';
395 	snprintf(buf, size, "%s", xbuf);
396 }
397 
398 static void
399 convert_sff_date(char *buf, size_t size, char *xbuf)
400 {
401 
402 	snprintf(buf, size, "20%c%c-%c%c-%c%c", xbuf[0], xbuf[1],
403 	    xbuf[2], xbuf[3], xbuf[4], xbuf[5]);
404 }
405 
406 static void
407 get_sfp_vendor_name(struct i2c_info *ii, char *buf, size_t size)
408 {
409 	char xbuf[17];
410 
411 	memset(xbuf, 0, sizeof(xbuf));
412 	read_i2c(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, 16, (uint8_t *)xbuf);
413 	convert_sff_name(buf, size, xbuf);
414 }
415 
416 static void
417 get_sfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size)
418 {
419 	char xbuf[17];
420 
421 	memset(xbuf, 0, sizeof(xbuf));
422 	read_i2c(ii, SFF_8472_BASE, SFF_8472_PN_START, 16, (uint8_t *)xbuf);
423 	convert_sff_name(buf, size, xbuf);
424 }
425 
426 static void
427 get_sfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size)
428 {
429 	char xbuf[17];
430 
431 	memset(xbuf, 0, sizeof(xbuf));
432 	read_i2c(ii, SFF_8472_BASE, SFF_8472_SN_START, 16, (uint8_t *)xbuf);
433 	convert_sff_name(buf, size, xbuf);
434 }
435 
436 static void
437 get_sfp_vendor_date(struct i2c_info *ii, char *buf, size_t size)
438 {
439 	char xbuf[6];
440 
441 	memset(xbuf, 0, sizeof(xbuf));
442 	/* Date code, see Table 3.8 for description */
443 	read_i2c(ii, SFF_8472_BASE, SFF_8472_DATE_START, 6, (uint8_t *)xbuf);
444 	convert_sff_date(buf, size, xbuf);
445 }
446 
447 static void
448 get_qsfp_vendor_name(struct i2c_info *ii, char *buf, size_t size)
449 {
450 	char xbuf[17];
451 
452 	memset(xbuf, 0, sizeof(xbuf));
453 	read_i2c(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, 16, (uint8_t *)xbuf);
454 	convert_sff_name(buf, size, xbuf);
455 }
456 
457 static void
458 get_qsfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size)
459 {
460 	char xbuf[17];
461 
462 	memset(xbuf, 0, sizeof(xbuf));
463 	read_i2c(ii, SFF_8436_BASE, SFF_8436_PN_START, 16, (uint8_t *)xbuf);
464 	convert_sff_name(buf, size, xbuf);
465 }
466 
467 static void
468 get_qsfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size)
469 {
470 	char xbuf[17];
471 
472 	memset(xbuf, 0, sizeof(xbuf));
473 	read_i2c(ii, SFF_8436_BASE, SFF_8436_SN_START, 16, (uint8_t *)xbuf);
474 	convert_sff_name(buf, size, xbuf);
475 }
476 
477 static void
478 get_qsfp_vendor_date(struct i2c_info *ii, char *buf, size_t size)
479 {
480 	char xbuf[6];
481 
482 	memset(xbuf, 0, sizeof(xbuf));
483 	read_i2c(ii, SFF_8436_BASE, SFF_8436_DATE_START, 6, (uint8_t *)xbuf);
484 	convert_sff_date(buf, size, xbuf);
485 }
486 
487 static void
488 print_sfp_vendor(struct i2c_info *ii, char *buf, size_t size)
489 {
490 	char xbuf[80];
491 
492 	memset(xbuf, 0, sizeof(xbuf));
493 	if (ii->qsfp != 0) {
494 		get_qsfp_vendor_name(ii, xbuf, 20);
495 		get_qsfp_vendor_pn(ii, &xbuf[20], 20);
496 		get_qsfp_vendor_sn(ii, &xbuf[40], 20);
497 		get_qsfp_vendor_date(ii, &xbuf[60], 20);
498 	} else {
499 		get_sfp_vendor_name(ii, xbuf, 20);
500 		get_sfp_vendor_pn(ii, &xbuf[20], 20);
501 		get_sfp_vendor_sn(ii, &xbuf[40], 20);
502 		get_sfp_vendor_date(ii, &xbuf[60], 20);
503 	}
504 
505 	snprintf(buf, size, "vendor: %s PN: %s SN: %s DATE: %s",
506 	    xbuf, &xbuf[20],  &xbuf[40], &xbuf[60]);
507 }
508 
509 /*
510  * Converts internal templerature (SFF-8472, SFF-8436)
511  * 16-bit unsigned value to human-readable representation:
512  *
513  * Internally measured Module temperature are represented
514  * as a 16-bit signed twos complement value in increments of
515  * 1/256 degrees Celsius, yielding a total range of –128C to +128C
516  * that is considered valid between –40 and +125C.
517  *
518  */
519 static void
520 convert_sff_temp(char *buf, size_t size, uint8_t *xbuf)
521 {
522 	double d;
523 
524 	d = (double)xbuf[0];
525 	d += (double)xbuf[1] / 256;
526 
527 	snprintf(buf, size, "%.2f C", d);
528 }
529 
530 /*
531  * Retrieves supplied voltage (SFF-8472, SFF-8436).
532  * 16-bit usigned value, treated as range 0..+6.55 Volts
533  */
534 static void
535 convert_sff_voltage(char *buf, size_t size, uint8_t *xbuf)
536 {
537 	double d;
538 
539 	d = (double)((xbuf[0] << 8) | xbuf[1]);
540 	snprintf(buf, size, "%.2f Volts", d / 10000);
541 }
542 
543 /*
544  * Converts value in @xbuf to both milliwats and dBm
545  * human representation.
546  */
547 static void
548 convert_sff_power(struct i2c_info *ii, char *buf, size_t size, uint8_t *xbuf)
549 {
550 	uint16_t mW;
551 	double dbm;
552 
553 	mW = (xbuf[0] << 8) + xbuf[1];
554 
555 	/* Convert mw to dbm */
556 	dbm = 10.0 * log10(1.0 * mW / 10000);
557 
558 	/*
559 	 * Assume internally-calibrated data.
560 	 * This is always true for SFF-8346, and explicitly
561 	 * checked for SFF-8472.
562 	 */
563 
564 	/* Table 3.9, bit 5 is set, internally calibrated */
565 	snprintf(buf, size, "%d.%02d mW (%.2f dBm)",
566     	    mW / 10000, (mW % 10000) / 100, dbm);
567 }
568 
569 static void
570 get_sfp_temp(struct i2c_info *ii, char *buf, size_t size)
571 {
572 	uint8_t xbuf[2];
573 
574 	memset(xbuf, 0, sizeof(xbuf));
575 	read_i2c(ii, SFF_8472_DIAG, SFF_8472_TEMP, 2, xbuf);
576 	convert_sff_temp(buf, size, xbuf);
577 }
578 
579 static void
580 get_sfp_voltage(struct i2c_info *ii, char *buf, size_t size)
581 {
582 	uint8_t xbuf[2];
583 
584 	memset(xbuf, 0, sizeof(xbuf));
585 	read_i2c(ii, SFF_8472_DIAG, SFF_8472_VCC, 2, xbuf);
586 	convert_sff_voltage(buf, size, xbuf);
587 }
588 
589 static void
590 get_qsfp_temp(struct i2c_info *ii, char *buf, size_t size)
591 {
592 	uint8_t xbuf[2];
593 
594 	memset(xbuf, 0, sizeof(xbuf));
595 	read_i2c(ii, SFF_8436_BASE, SFF_8436_TEMP, 2, xbuf);
596 	convert_sff_temp(buf, size, xbuf);
597 }
598 
599 static void
600 get_qsfp_voltage(struct i2c_info *ii, char *buf, size_t size)
601 {
602 	uint8_t xbuf[2];
603 
604 	memset(xbuf, 0, sizeof(xbuf));
605 	read_i2c(ii, SFF_8436_BASE, SFF_8436_VCC, 2, xbuf);
606 	convert_sff_voltage(buf, size, xbuf);
607 }
608 
609 static void
610 get_sfp_rx_power(struct i2c_info *ii, char *buf, size_t size)
611 {
612 	uint8_t xbuf[2];
613 
614 	memset(xbuf, 0, sizeof(xbuf));
615 	read_i2c(ii, SFF_8472_DIAG, SFF_8472_RX_POWER, 2, xbuf);
616 	convert_sff_power(ii, buf, size, xbuf);
617 }
618 
619 static void
620 get_sfp_tx_power(struct i2c_info *ii, char *buf, size_t size)
621 {
622 	uint8_t xbuf[2];
623 
624 	memset(xbuf, 0, sizeof(xbuf));
625 	read_i2c(ii, SFF_8472_DIAG, SFF_8472_TX_POWER, 2, xbuf);
626 	convert_sff_power(ii, buf, size, xbuf);
627 }
628 
629 static void
630 get_qsfp_rx_power(struct i2c_info *ii, char *buf, size_t size, int chan)
631 {
632 	uint8_t xbuf[2];
633 
634 	memset(xbuf, 0, sizeof(xbuf));
635 	read_i2c(ii, SFF_8436_BASE, SFF_8436_RX_CH1_MSB + (chan-1)*2, 2, xbuf);
636 	convert_sff_power(ii, buf, size, xbuf);
637 }
638 
639 static void
640 get_qsfp_tx_power(struct i2c_info *ii, char *buf, size_t size, int chan)
641 {
642 	uint8_t xbuf[2];
643 
644 	memset(xbuf, 0, sizeof(xbuf));
645 	read_i2c(ii, SFF_8436_BASE, SFF_8436_TX_CH1_MSB + (chan-1)*2, 2, xbuf);
646 	convert_sff_power(ii, buf, size, xbuf);
647 }
648 
649 static void
650 get_qsfp_rev_compliance(struct i2c_info *ii, char *buf, size_t size)
651 {
652 	uint8_t xbuf;
653 
654 	xbuf = 0;
655 	read_i2c(ii, SFF_8436_BASE, SFF_8436_STATUS, 1, &xbuf);
656 	convert_sff_rev_compliance(buf, size, xbuf);
657 }
658 
659 static uint32_t
660 get_qsfp_br(struct i2c_info *ii)
661 {
662 	uint8_t xbuf;
663 	uint32_t rate;
664 
665 	xbuf = 0;
666 	read_i2c(ii, SFF_8436_BASE, SFF_8436_BITRATE, 1, &xbuf);
667 	rate = xbuf * 100;
668 	if (xbuf == 0xFF) {
669 		read_i2c(ii, SFF_8436_BASE, SFF_8636_BITRATE, 1, &xbuf);
670 		rate = xbuf * 250;
671 	}
672 
673 	return (rate);
674 }
675 
676 /*
677  * Reads i2c data from opened kernel socket.
678  */
679 static int
680 read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len,
681     uint8_t *buf)
682 {
683 	struct ifi2creq req;
684 	int i, l;
685 
686 	if (ii->error != 0)
687 		return (ii->error);
688 
689 	ii->ifr->ifr_data = (caddr_t)&req;
690 
691 	i = 0;
692 	l = 0;
693 	memset(&req, 0, sizeof(req));
694 	req.dev_addr = addr;
695 	req.offset = off;
696 	req.len = len;
697 
698 	while (len > 0) {
699 		l = (len > sizeof(req.data)) ? sizeof(req.data) : len;
700 		req.len = l;
701 		if (ioctl(ii->fd, SIOCGI2C, ii->ifr) != 0) {
702 			ii->error = errno;
703 			return (errno);
704 		}
705 
706 		memcpy(&buf[i], req.data, l);
707 		len -= l;
708 		i += l;
709 		req.offset += l;
710 	}
711 
712 	return (0);
713 }
714 
715 static void
716 dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len)
717 {
718 	unsigned char buf[16];
719 	int i, read;
720 
721 	while (len > 0) {
722 		memset(buf, 0, sizeof(buf));
723 		read = (len > sizeof(buf)) ? sizeof(buf) : len;
724 		read_i2c(ii, addr, off, read, buf);
725 		if (ii->error != 0) {
726 			fprintf(stderr, "Error reading i2c info\n");
727 			return;
728 		}
729 
730 		printf("\t");
731 		for (i = 0; i < read; i++)
732 			printf("%02X ", buf[i]);
733 		printf("\n");
734 		len -= read;
735 		off += read;
736 	}
737 }
738 
739 static void
740 print_qsfp_status(struct i2c_info *ii, int verbose)
741 {
742 	char buf[80], buf2[40], buf3[40];
743 	uint8_t diag_type;
744 	uint32_t bitrate;
745 	int i;
746 
747 	/* Read diagnostic monitoring type */
748 	read_i2c(ii, SFF_8436_BASE, SFF_8436_DIAG_TYPE, 1, (caddr_t)&diag_type);
749 	if (ii->error != 0)
750 		return;
751 
752 	/*
753 	 * Read monitoring data it is supplied.
754 	 * XXX: It is not exactly clear from standard
755 	 * how one can specify lack of measurements (passive cables case).
756 	 */
757 	if (diag_type != 0)
758 		ii->do_diag = 1;
759 	ii->qsfp = 1;
760 
761 	/* Transceiver type */
762 	get_qsfp_identifier(ii, buf, sizeof(buf));
763 	get_qsfp_transceiver_class(ii, buf2, sizeof(buf2));
764 	get_qsfp_connector(ii, buf3, sizeof(buf3));
765 	if (ii->error == 0)
766 		printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3);
767 	print_sfp_vendor(ii, buf, sizeof(buf));
768 	if (ii->error == 0)
769 		printf("\t%s\n", buf);
770 
771 	if (verbose > 1) {
772 		get_qsfp_rev_compliance(ii, buf, sizeof(buf));
773 		if (ii->error == 0)
774 			printf("\tcompliance level: %s\n", buf);
775 
776 		bitrate = get_qsfp_br(ii);
777 		if (ii->error == 0 && bitrate > 0)
778 			printf("\tnominal bitrate: %u Mbps\n", bitrate);
779 	}
780 
781 	/* Request current measurements if they are provided: */
782 	if (ii->do_diag != 0) {
783 		get_qsfp_temp(ii, buf, sizeof(buf));
784 		get_qsfp_voltage(ii, buf2, sizeof(buf2));
785 		printf("\tmodule temperature: %s voltage: %s\n", buf, buf2);
786 		for (i = 1; i <= 4; i++) {
787 			get_qsfp_rx_power(ii, buf, sizeof(buf), i);
788 			get_qsfp_tx_power(ii, buf2, sizeof(buf2), i);
789 			printf("\tlane %d: RX: %s TX: %s\n", i, buf, buf2);
790 		}
791 	}
792 
793 	if (verbose > 2) {
794 		printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n");
795 		dump_i2c_data(ii, SFF_8436_BASE, 128, 128);
796 		printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n");
797 		dump_i2c_data(ii, SFF_8436_BASE, 0, 82);
798 	}
799 }
800 
801 static void
802 print_sfp_status(struct i2c_info *ii, int verbose)
803 {
804 	char buf[80], buf2[40], buf3[40];
805 	uint8_t diag_type, flags;
806 
807 	/* Read diagnostic monitoring type */
808 	read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type);
809 	if (ii->error != 0)
810 		return;
811 
812 	/*
813 	 * Read monitoring data IFF it is supplied AND is
814 	 * internally calibrated
815 	 */
816 	flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL;
817 	if ((diag_type & flags) == flags)
818 		ii->do_diag = 1;
819 
820 	/* Transceiver type */
821 	get_sfp_identifier(ii, buf, sizeof(buf));
822 	get_sfp_transceiver_class(ii, buf2, sizeof(buf2));
823 	get_sfp_connector(ii, buf3, sizeof(buf3));
824 	if (ii->error == 0)
825 		printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3);
826 	print_sfp_vendor(ii, buf, sizeof(buf));
827 	if (ii->error == 0)
828 		printf("\t%s\n", buf);
829 
830 	if (verbose > 5)
831 		printf_sfp_transceiver_descr(ii, buf, sizeof(buf));
832 	/*
833 	 * Request current measurements iff they are provided:
834 	 */
835 	if (ii->do_diag != 0) {
836 		get_sfp_temp(ii, buf, sizeof(buf));
837 		get_sfp_voltage(ii, buf2, sizeof(buf2));
838 		printf("\tmodule temperature: %s Voltage: %s\n", buf, buf2);
839 		get_sfp_rx_power(ii, buf, sizeof(buf));
840 		get_sfp_tx_power(ii, buf2, sizeof(buf2));
841 		printf("\tRX: %s TX: %s\n", buf, buf2);
842 	}
843 
844 	if (verbose > 2) {
845 		printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n");
846 		dump_i2c_data(ii, SFF_8472_BASE, 0, 128);
847 	}
848 }
849 
850 void
851 sfp_status(int s, struct ifreq *ifr, int verbose)
852 {
853 	struct i2c_info ii;
854 	uint8_t id_byte;
855 
856 	/* Prepare necessary into pass to i2c reader */
857 	memset(&ii, 0, sizeof(ii));
858 	ii.fd = s;
859 	ii.ifr = ifr;
860 
861 	/*
862 	 * Try to read byte 0 from i2c:
863 	 * Both SFF-8472 and SFF-8436 use it as
864 	 * 'identification byte'.
865 	 * Stop reading status on zero as value -
866 	 * this might happen in case of empty transceiver slot.
867 	 */
868 	id_byte = 0;
869 	read_i2c(&ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&id_byte);
870 	if (ii.error != 0 || id_byte == 0)
871 		return;
872 
873 	switch (id_byte) {
874 	case SFF_8024_ID_QSFP:
875 	case SFF_8024_ID_QSFPPLUS:
876 		print_qsfp_status(&ii, verbose);
877 		break;
878 	default:
879 		print_sfp_status(&ii, verbose);
880 	};
881 }
882 
883