xref: /freebsd/sbin/ifconfig/sfp.c (revision 6186fd1857626de0f7cb1a9e4dff19082f9ebb11)
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 typedef int (read_i2c)(struct i2c_info *ii, uint8_t addr, uint8_t off,
53     uint8_t len, caddr_t buf);
54 
55 struct i2c_info {
56 	int s;
57 	int error;
58 	int bshift;
59 	int qsfp;
60 	int do_diag;
61 	struct ifreq *ifr;
62 	read_i2c *f;
63 	char *textbuf;
64 	size_t bufsize;
65 	int cfd;
66 	int port_id;
67 	int chip_id;
68 };
69 
70 struct _nv {
71 	int v;
72 	const char *n;
73 };
74 
75 const char *find_value(struct _nv *x, int value);
76 const char *find_zero_bit(struct _nv *x, int value, int sz);
77 
78 /* SFF-8472 Rev. 11.4 table 3.4: Connector values */
79 static struct _nv conn[] = {
80 	{ 0x00, "Unknown" },
81 	{ 0x01, "SC" },
82 	{ 0x02, "Fibre Channel Style 1 copper" },
83 	{ 0x03, "Fibre Channel Style 2 copper" },
84 	{ 0x04, "BNC/TNC" },
85 	{ 0x05, "Fibre Channel coaxial" },
86 	{ 0x06, "FiberJack" },
87 	{ 0x07, "LC" },
88 	{ 0x08, "MT-RJ" },
89 	{ 0x09, "MU" },
90 	{ 0x0A, "SG" },
91 	{ 0x0B, "Optical pigtail" },
92 	{ 0x0C, "MPO Parallel Optic" },
93 	{ 0x20, "HSSDC II" },
94 	{ 0x21, "Copper pigtail" },
95 	{ 0x22, "RJ45" },
96 	{ 0x23, "No separate connector" }, /* SFF-8436 */
97 	{ 0, NULL }
98 };
99 
100 /* SFF-8472 Rev. 11.4 table 3.5: Transceiver codes */
101 /* 10G Ethernet/IB compliance codes, byte 3 */
102 static struct _nv eth_10g[] = {
103 	{ 0x80, "10G Base-ER" },
104 	{ 0x40, "10G Base-LRM" },
105 	{ 0x20, "10G Base-LR" },
106 	{ 0x10, "10G Base-SR" },
107 	{ 0x08, "1X SX" },
108 	{ 0x04, "1X LX" },
109 	{ 0x02, "1X Copper Active" },
110 	{ 0x01, "1X Copper Passive" },
111 	{ 0, NULL }
112 };
113 
114 /* Ethernet compliance codes, byte 6 */
115 static struct _nv eth_compat[] = {
116 	{ 0x80, "BASE-PX" },
117 	{ 0x40, "BASE-BX10" },
118 	{ 0x20, "100BASE-FX" },
119 	{ 0x10, "100BASE-LX/LX10" },
120 	{ 0x08, "1000BASE-T" },
121 	{ 0x04, "1000BASE-CX" },
122 	{ 0x02, "1000BASE-LX" },
123 	{ 0x01, "1000BASE-SX" },
124 	{ 0, NULL }
125 };
126 
127 /* FC link length, byte 7 */
128 static struct _nv fc_len[] = {
129 	{ 0x80, "very long distance" },
130 	{ 0x40, "short distance" },
131 	{ 0x20, "intermediate distance" },
132 	{ 0x10, "long distance" },
133 	{ 0x08, "medium distance" },
134 	{ 0, NULL }
135 };
136 
137 /* Channel/Cable technology, byte 7-8 */
138 static struct _nv cab_tech[] = {
139 	{ 0x0400, "Shortwave laser (SA)" },
140 	{ 0x0200, "Longwave laser (LC)" },
141 	{ 0x0100, "Electrical inter-enclosure (EL)" },
142 	{ 0x80, "Electrical intra-enclosure (EL)" },
143 	{ 0x40, "Shortwave laser (SN)" },
144 	{ 0x20, "Shortwave laser (SL)" },
145 	{ 0x10, "Longwave laser (LL)" },
146 	{ 0x08, "Active Cable" },
147 	{ 0x04, "Passive Cable" },
148 	{ 0, NULL }
149 };
150 
151 /* FC Transmission media, byte 9 */
152 static struct _nv fc_media[] = {
153 	{ 0x80, "Twin Axial Pair" },
154 	{ 0x40, "Twisted Pair" },
155 	{ 0x20, "Miniature Coax" },
156 	{ 0x10, "Viao Coax" },
157 	{ 0x08, "Miltimode, 62.5um" },
158 	{ 0x04, "Multimode, 50um" },
159 	{ 0x02, "" },
160 	{ 0x01, "Single Mode" },
161 	{ 0, NULL }
162 };
163 
164 /* FC Speed, byte 10 */
165 static struct _nv fc_speed[] = {
166 	{ 0x80, "1200 MBytes/sec" },
167 	{ 0x40, "800 MBytes/sec" },
168 	{ 0x20, "1600 MBytes/sec" },
169 	{ 0x10, "400 MBytes/sec" },
170 	{ 0x08, "3200 MBytes/sec" },
171 	{ 0x04, "200 MBytes/sec" },
172 	{ 0x01, "100 MBytes/sec" },
173 	{ 0, NULL }
174 };
175 
176 /* SFF-8436 Rev. 4.8 table 33: Specification compliance  */
177 
178 /* 10/40G Ethernet compliance codes, byte 128 + 3 */
179 static struct _nv eth_1040g[] = {
180 	{ 0x80, "Reserved" },
181 	{ 0x40, "10GBASE-LRM" },
182 	{ 0x20, "10GBASE-LR" },
183 	{ 0x10, "10GBASE-SR" },
184 	{ 0x08, "40GBASE-CR4" },
185 	{ 0x04, "40GBASE-SR4" },
186 	{ 0x02, "40GBASE-LR4" },
187 	{ 0x01, "40G Active Cable" },
188 	{ 0, NULL }
189 };
190 
191 const char *
192 find_value(struct _nv *x, int value)
193 {
194 	for (; x->n != NULL; x++)
195 		if (x->v == value)
196 			return (x->n);
197 	return (NULL);
198 }
199 
200 const char *
201 find_zero_bit(struct _nv *x, int value, int sz)
202 {
203 	int v, m;
204 	const char *s;
205 
206 	v = 1;
207 	for (v = 1, m = 1 << (8 * sz); v < m; v *= 2) {
208 		if ((value & v) == 0)
209 			continue;
210 		if ((s = find_value(x, value & v)) != NULL) {
211 			value &= ~v;
212 			return (s);
213 		}
214 	}
215 
216 	return (NULL);
217 }
218 
219 static void
220 convert_sff_identifier(char *buf, size_t size, uint8_t value)
221 {
222 	const char *x;
223 
224 	x = NULL;
225 	if (value <= SFF_8024_ID_LAST)
226 		x = sff_8024_id[value];
227 	else {
228 		if (value > 0x80)
229 			x = "Vendor specific";
230 		else
231 			x = "Reserved";
232 	}
233 
234 	snprintf(buf, size, "%s", x);
235 }
236 
237 static void
238 convert_sff_connector(char *buf, size_t size, uint8_t value)
239 {
240 	const char *x;
241 
242 	if ((x = find_value(conn, value)) == NULL) {
243 		if (value >= 0x0D && value <= 0x1F)
244 			x = "Unallocated";
245 		else if (value >= 0x24 && value <= 0x7F)
246 			x = "Unallocated";
247 		else
248 			x = "Vendor specific";
249 	}
250 
251 	snprintf(buf, size, "%s", x);
252 }
253 
254 static void
255 get_sfp_identifier(struct i2c_info *ii, char *buf, size_t size)
256 {
257 	uint8_t data;
258 
259 	ii->f(ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&data);
260 	convert_sff_identifier(buf, size, data);
261 }
262 
263 static void
264 get_sfp_connector(struct i2c_info *ii, char *buf, size_t size)
265 {
266 	uint8_t data;
267 
268 	ii->f(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, (caddr_t)&data);
269 	convert_sff_connector(buf, size, data);
270 }
271 
272 static void
273 get_qsfp_identifier(struct i2c_info *ii, char *buf, size_t size)
274 {
275 	uint8_t data;
276 
277 	ii->f(ii, SFF_8436_BASE, SFF_8436_ID, 1, (caddr_t)&data);
278 	convert_sff_identifier(buf, size, data);
279 }
280 
281 static void
282 get_qsfp_connector(struct i2c_info *ii, char *buf, size_t size)
283 {
284 	uint8_t data;
285 
286 	ii->f(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, (caddr_t)&data);
287 	convert_sff_connector(buf, size, data);
288 }
289 
290 static void
291 printf_sfp_transceiver_descr(struct i2c_info *ii, char *buf, size_t size)
292 {
293 	char xbuf[12];
294 	const char *tech_class, *tech_len, *tech_tech, *tech_media, *tech_speed;
295 
296 	tech_class = NULL;
297 	tech_len = NULL;
298 	tech_tech = NULL;
299 	tech_media = NULL;
300 	tech_speed = NULL;
301 
302 	/* Read bytes 3-10 at once */
303 	ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, &xbuf[3]);
304 
305 	/* Check 10G ethernet first */
306 	tech_class = find_zero_bit(eth_10g, xbuf[3], 1);
307 	if (tech_class == NULL) {
308 		/* No match. Try 1G */
309 		tech_class = find_zero_bit(eth_compat, xbuf[6], 1);
310 	}
311 
312 	tech_len = find_zero_bit(fc_len, xbuf[7], 1);
313 	tech_tech = find_zero_bit(cab_tech, xbuf[7] << 8 | xbuf[8], 2);
314 	tech_media = find_zero_bit(fc_media, xbuf[9], 1);
315 	tech_speed = find_zero_bit(fc_speed, xbuf[10], 1);
316 
317 	printf("Class: %s\n", tech_class);
318 	printf("Length: %s\n", tech_len);
319 	printf("Tech: %s\n", tech_tech);
320 	printf("Media: %s\n", tech_media);
321 	printf("Speed: %s\n", tech_speed);
322 }
323 
324 static void
325 get_sfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size)
326 {
327 	const char *tech_class;
328 	uint8_t code;
329 
330 	/* Check 10G Ethernet/IB first */
331 	ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, (caddr_t)&code);
332 	tech_class = find_zero_bit(eth_10g, code, 1);
333 	if (tech_class == NULL) {
334 		/* No match. Try Ethernet 1G */
335 		ii->f(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3,
336 		    1, (caddr_t)&code);
337 		tech_class = find_zero_bit(eth_compat, code, 1);
338 	}
339 
340 	if (tech_class == NULL)
341 		tech_class = "Unknown";
342 
343 	snprintf(buf, size, "%s", tech_class);
344 }
345 
346 static void
347 get_qsfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size)
348 {
349 	const char *tech_class;
350 	uint8_t code;
351 
352 	/* Check 10/40G Ethernet class only */
353 	ii->f(ii, SFF_8436_BASE, SFF_8436_CODE_E1040G, 1, (caddr_t)&code);
354 	tech_class = find_zero_bit(eth_1040g, code, 1);
355 	if (tech_class == NULL)
356 		tech_class = "Unknown";
357 
358 	snprintf(buf, size, "%s", tech_class);
359 }
360 
361 /*
362  * Print SFF-8472/SFF-8436 string to supplied buffer.
363  * All (vendor-specific) strings are padded right with '0x20'.
364  */
365 static void
366 convert_sff_name(char *buf, size_t size, char *xbuf)
367 {
368 	char *p;
369 
370 	for (p = &xbuf[16]; *(p - 1) == 0x20; p--)
371 		;
372 	*p = '\0';
373 	snprintf(buf, size, "%s", xbuf);
374 }
375 
376 static void
377 convert_sff_date(char *buf, size_t size, char *xbuf)
378 {
379 
380 	snprintf(buf, size, "20%c%c-%c%c-%c%c", xbuf[0], xbuf[1],
381 	    xbuf[2], xbuf[3], xbuf[4], xbuf[5]);
382 }
383 
384 static void
385 get_sfp_vendor_name(struct i2c_info *ii, char *buf, size_t size)
386 {
387 	char xbuf[17];
388 
389 	memset(xbuf, 0, sizeof(xbuf));
390 	ii->f(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, 16, xbuf);
391 	convert_sff_name(buf, size, xbuf);
392 }
393 
394 static void
395 get_sfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size)
396 {
397 	char xbuf[17];
398 
399 	memset(xbuf, 0, sizeof(xbuf));
400 	ii->f(ii, SFF_8472_BASE, SFF_8472_PN_START, 16, xbuf);
401 	convert_sff_name(buf, size, xbuf);
402 }
403 
404 static void
405 get_sfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size)
406 {
407 	char xbuf[17];
408 
409 	memset(xbuf, 0, sizeof(xbuf));
410 	ii->f(ii, SFF_8472_BASE, SFF_8472_SN_START, 16, xbuf);
411 	convert_sff_name(buf, size, xbuf);
412 }
413 
414 static void
415 get_sfp_vendor_date(struct i2c_info *ii, char *buf, size_t size)
416 {
417 	char xbuf[6];
418 
419 	memset(xbuf, 0, sizeof(xbuf));
420 	/* Date code, see Table 3.8 for description */
421 	ii->f(ii, SFF_8472_BASE, SFF_8472_DATE_START, 6, xbuf);
422 	convert_sff_date(buf, size, xbuf);
423 }
424 
425 static void
426 get_qsfp_vendor_name(struct i2c_info *ii, char *buf, size_t size)
427 {
428 	char xbuf[17];
429 
430 	memset(xbuf, 0, sizeof(xbuf));
431 	ii->f(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, 16, xbuf);
432 	convert_sff_name(buf, size, xbuf);
433 }
434 
435 static void
436 get_qsfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size)
437 {
438 	char xbuf[17];
439 
440 	memset(xbuf, 0, sizeof(xbuf));
441 	ii->f(ii, SFF_8436_BASE, SFF_8436_PN_START, 16, xbuf);
442 	convert_sff_name(buf, size, xbuf);
443 }
444 
445 static void
446 get_qsfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size)
447 {
448 	char xbuf[17];
449 
450 	memset(xbuf, 0, sizeof(xbuf));
451 	ii->f(ii, SFF_8436_BASE, SFF_8436_SN_START, 16, xbuf);
452 	convert_sff_name(buf, size, xbuf);
453 }
454 
455 static void
456 get_qsfp_vendor_date(struct i2c_info *ii, char *buf, size_t size)
457 {
458 	char xbuf[6];
459 
460 	memset(xbuf, 0, sizeof(xbuf));
461 	ii->f(ii, SFF_8436_BASE, SFF_8436_DATE_START, 6, xbuf);
462 	convert_sff_date(buf, size, xbuf);
463 }
464 
465 static void
466 print_sfp_vendor(struct i2c_info *ii, char *buf, size_t size)
467 {
468 	char xbuf[80];
469 
470 	memset(xbuf, 0, sizeof(xbuf));
471 	if (ii->qsfp != 0) {
472 		get_qsfp_vendor_name(ii, xbuf, 20);
473 		get_qsfp_vendor_pn(ii, &xbuf[20], 20);
474 		get_qsfp_vendor_sn(ii, &xbuf[40], 20);
475 		get_qsfp_vendor_date(ii, &xbuf[60], 20);
476 	} else {
477 		get_sfp_vendor_name(ii, xbuf, 20);
478 		get_sfp_vendor_pn(ii, &xbuf[20], 20);
479 		get_sfp_vendor_sn(ii, &xbuf[40], 20);
480 		get_sfp_vendor_date(ii, &xbuf[60], 20);
481 	}
482 
483 	snprintf(buf, size, "vendor: %s PN: %s SN: %s DATE: %s",
484 	    xbuf, &xbuf[20],  &xbuf[40], &xbuf[60]);
485 }
486 
487 /*
488  * Converts internal templerature (SFF-8472, SFF-8436)
489  * 16-bit unsigned value to human-readable representation:
490  *
491  * Internally measured Module temperature are represented
492  * as a 16-bit signed twos complement value in increments of
493  * 1/256 degrees Celsius, yielding a total range of –128C to +128C
494  * that is considered valid between –40 and +125C.
495  *
496  */
497 static void
498 convert_sff_temp(char *buf, size_t size, char *xbuf)
499 {
500 	double d;
501 
502 	d = (double)(int8_t)xbuf[0];
503 	d += (double)(uint8_t)xbuf[1] / 256;
504 
505 	snprintf(buf, size, "%.2f C", d);
506 }
507 
508 /*
509  * Retrieves supplied voltage (SFF-8472, SFF-8436).
510  * 16-bit usigned value, treated as range 0..+6.55 Volts
511  */
512 static void
513 convert_sff_voltage(char *buf, size_t size, char *xbuf)
514 {
515 	double d;
516 
517 	d = (double)(((uint8_t)xbuf[0] << 8) | (uint8_t)xbuf[1]);
518 	snprintf(buf, size, "%.2f Volts", d / 10000);
519 }
520 
521 /*
522  * Converts value in @xbuf to both milliwats and dBm
523  * human representation.
524  */
525 static void
526 convert_sff_power(struct i2c_info *ii, char *buf, size_t size, char *xbuf)
527 {
528 	uint16_t mW;
529 	double dbm;
530 
531 	mW = ((uint8_t)xbuf[0] << 8) + (uint8_t)xbuf[1];
532 
533 	/* Convert mw to dbm */
534 	dbm = 10.0 * log10(1.0 * mW / 10000);
535 
536 	/*
537 	 * Assume internally-calibrated data.
538 	 * This is always true for SFF-8346, and explicitly
539 	 * checked for SFF-8472.
540 	 */
541 
542 	/* Table 3.9, bit 5 is set, internally calibrated */
543 	snprintf(buf, size, "%d.%02d mW (%.2f dBm)",
544     	    mW / 10000, (mW % 10000) / 100, dbm);
545 }
546 
547 static void
548 get_sfp_temp(struct i2c_info *ii, char *buf, size_t size)
549 {
550 	char xbuf[2];
551 
552 	memset(xbuf, 0, sizeof(xbuf));
553 	ii->f(ii, SFF_8472_DIAG, SFF_8472_TEMP, 2, xbuf);
554 	convert_sff_temp(buf, size, xbuf);
555 }
556 
557 static void
558 get_sfp_voltage(struct i2c_info *ii, char *buf, size_t size)
559 {
560 	char xbuf[2];
561 
562 	memset(xbuf, 0, sizeof(xbuf));
563 	ii->f(ii, SFF_8472_DIAG, SFF_8472_VCC, 2, xbuf);
564 	convert_sff_voltage(buf, size, xbuf);
565 }
566 
567 static void
568 get_qsfp_temp(struct i2c_info *ii, char *buf, size_t size)
569 {
570 	char xbuf[2];
571 
572 	memset(xbuf, 0, sizeof(xbuf));
573 	ii->f(ii, SFF_8436_BASE, SFF_8436_TEMP, 2, xbuf);
574 	convert_sff_temp(buf, size, xbuf);
575 }
576 
577 static void
578 get_qsfp_voltage(struct i2c_info *ii, char *buf, size_t size)
579 {
580 	char xbuf[2];
581 
582 	memset(xbuf, 0, sizeof(xbuf));
583 	ii->f(ii, SFF_8436_BASE, SFF_8436_VCC, 2, xbuf);
584 	convert_sff_voltage(buf, size, xbuf);
585 }
586 
587 static void
588 get_sfp_rx_power(struct i2c_info *ii, char *buf, size_t size)
589 {
590 	char xbuf[2];
591 
592 	memset(xbuf, 0, sizeof(xbuf));
593 	ii->f(ii, SFF_8472_DIAG, SFF_8472_RX_POWER, 2, xbuf);
594 	convert_sff_power(ii, buf, size, xbuf);
595 }
596 
597 static void
598 get_sfp_tx_power(struct i2c_info *ii, char *buf, size_t size)
599 {
600 	char xbuf[2];
601 
602 	memset(xbuf, 0, sizeof(xbuf));
603 	ii->f(ii, SFF_8472_DIAG, SFF_8472_TX_POWER, 2, xbuf);
604 	convert_sff_power(ii, buf, size, xbuf);
605 }
606 
607 static void
608 get_qsfp_rx_power(struct i2c_info *ii, char *buf, size_t size, int chan)
609 {
610 	char xbuf[2];
611 
612 	memset(xbuf, 0, sizeof(xbuf));
613 	ii->f(ii, SFF_8436_BASE, SFF_8436_RX_CH1_MSB + (chan - 1) * 2, 2, xbuf);
614 	convert_sff_power(ii, buf, size, xbuf);
615 }
616 
617 static void
618 get_qsfp_tx_power(struct i2c_info *ii, char *buf, size_t size, int chan)
619 {
620 	char xbuf[2];
621 
622 	memset(xbuf, 0, sizeof(xbuf));
623 	ii->f(ii, SFF_8436_BASE, SFF_8436_TX_CH1_MSB + (chan -1) * 2, 2, xbuf);
624 	convert_sff_power(ii, buf, size, xbuf);
625 }
626 
627 /* Generic handler */
628 static int
629 read_i2c_generic(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len,
630     caddr_t buf)
631 {
632 	struct ifi2creq req;
633 	int i, l;
634 
635 	if (ii->error != 0)
636 		return (ii->error);
637 
638 	ii->ifr->ifr_data = (caddr_t)&req;
639 
640 	i = 0;
641 	l = 0;
642 	memset(&req, 0, sizeof(req));
643 	req.dev_addr = addr;
644 	req.offset = off;
645 	req.len = len;
646 
647 	while (len > 0) {
648 		l = (len > sizeof(req.data)) ? sizeof(req.data) : len;
649 		req.len = l;
650 		if (ioctl(ii->s, SIOCGI2C, ii->ifr) != 0) {
651 			ii->error = errno;
652 			return (errno);
653 		}
654 
655 		memcpy(&buf[i], req.data, l);
656 		len -= l;
657 		i += l;
658 		req.offset += l;
659 	}
660 
661 	return (0);
662 }
663 
664 static void
665 print_qsfp_status(struct i2c_info *ii, int verbose)
666 {
667 	char buf[80], buf2[40], buf3[40];
668 	uint8_t diag_type;
669 	int i;
670 
671 	/* Read diagnostic monitoring type */
672 	ii->f(ii, SFF_8436_BASE, SFF_8436_DIAG_TYPE, 1, (caddr_t)&diag_type);
673 	if (ii->error != 0)
674 		return;
675 
676 	/*
677 	 * Read monitoring data it is supplied.
678 	 * XXX: It is not exactly clear from standard
679 	 * how one can specify lack of measurements (passive cables case).
680 	 */
681 	if (diag_type != 0)
682 		ii->do_diag = 1;
683 	ii->qsfp = 1;
684 
685 	/* Transceiver type */
686 	get_qsfp_identifier(ii, buf, sizeof(buf));
687 	get_qsfp_transceiver_class(ii, buf2, sizeof(buf2));
688 	get_qsfp_connector(ii, buf3, sizeof(buf3));
689 	if (ii->error == 0)
690 		printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3);
691 	print_sfp_vendor(ii, buf, sizeof(buf));
692 	if (ii->error == 0)
693 		printf("\t%s\n", buf);
694 
695 	/* Request current measurements if they are provided: */
696 	if (ii->do_diag != 0) {
697 		get_qsfp_temp(ii, buf, sizeof(buf));
698 		get_qsfp_voltage(ii, buf2, sizeof(buf2));
699 		printf("\tmodule temperature: %s voltage: %s\n", buf, buf2);
700 		for (i = 1; i <= 4; i++) {
701 			get_qsfp_rx_power(ii, buf, sizeof(buf), i);
702 			get_qsfp_tx_power(ii, buf2, sizeof(buf2), i);
703 			printf("\tlane %d: RX: %s TX: %s\n", i, buf, buf2);
704 		}
705 	}
706 }
707 
708 static void
709 print_sfp_status(struct i2c_info *ii, int verbose)
710 {
711 	char buf[80], buf2[40], buf3[40];
712 	uint8_t diag_type, flags;
713 
714 	/* Read diagnostic monitoring type */
715 	ii->f(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type);
716 	if (ii->error != 0)
717 		return;
718 
719 	/*
720 	 * Read monitoring data IFF it is supplied AND is
721 	 * internally calibrated
722 	 */
723 	flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL;
724 	if ((diag_type & flags) == flags)
725 		ii->do_diag = 1;
726 
727 	/* Transceiver type */
728 	get_sfp_identifier(ii, buf, sizeof(buf));
729 	get_sfp_transceiver_class(ii, buf2, sizeof(buf2));
730 	get_sfp_connector(ii, buf3, sizeof(buf3));
731 	if (ii->error == 0)
732 		printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3);
733 	if (verbose > 2)
734 		printf_sfp_transceiver_descr(ii, buf, sizeof(buf));
735 	print_sfp_vendor(ii, buf, sizeof(buf));
736 	if (ii->error == 0)
737 		printf("\t%s\n", buf);
738 
739 	/*
740 	 * Request current measurements iff they are provided:
741 	 */
742 	if (ii->do_diag != 0) {
743 		get_sfp_temp(ii, buf, sizeof(buf));
744 		get_sfp_voltage(ii, buf2, sizeof(buf2));
745 		printf("\tmodule temperature: %s Voltage: %s\n", buf, buf2);
746 		get_sfp_rx_power(ii, buf, sizeof(buf));
747 		get_sfp_tx_power(ii, buf2, sizeof(buf2));
748 		printf("\tRX: %s TX: %s\n", buf, buf2);
749 	}
750 }
751 
752 void
753 sfp_status(int s, struct ifreq *ifr, int verbose)
754 {
755 	struct i2c_info ii;
756 	uint8_t id_byte;
757 
758 	memset(&ii, 0, sizeof(ii));
759 	/* Prepare necessary into to pass to NIC handler */
760 	ii.s = s;
761 	ii.ifr = ifr;
762 	ii.f = read_i2c_generic;
763 
764 	/*
765 	 * Try to read byte 0 from i2c:
766 	 * Both SFF-8472 and SFF-8436 use it as
767 	 * 'identification byte'.
768 	 * Stop reading status on zero as value -
769 	 * this might happen in case of empty transceiver slot.
770 	 */
771 	id_byte = 0;
772 	ii.f(&ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&id_byte);
773 	if (ii.error != 0 || id_byte == 0)
774 		return;
775 
776 	switch (id_byte) {
777 	case SFF_8024_ID_QSFP:
778 	case SFF_8024_ID_QSFPPLUS:
779 		print_qsfp_status(&ii, verbose);
780 		break;
781 	default:
782 		print_sfp_status(&ii, verbose);
783 	};
784 }
785 
786