2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 3 of the License, or
9 ## (at your option) any later version.
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ## GNU General Public License for more details.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 # XFP protocol decoder
22 import sigrokdecode as srd
27 0x02: 'Integrated module/connector',
41 4: "Temp high warning",
42 6: "Temp low warning",
43 16: "Bias high alarm",
45 20: "Bias high warning",
46 22: "Bias low warning",
47 24: "TX power high alarm",
48 26: "TX power low alarm",
49 28: "TX power high warning",
50 30: "TX power low warning",
51 32: "RX power high alarm",
52 34: "RX power low alarm",
53 36: "RX power high warning",
54 38: "RX power low warning",
55 40: "AUX 1 high alarm",
56 42: "AUX 1 low alarm",
57 44: "AUX 1 high warning",
58 46: "AUX 1 low warning",
59 48: "AUX 2 high alarm",
60 50: "AUX 2 low alarm",
61 52: "AUX 2 high warning",
62 54: "AUX 2 low warning",
66 0: "Module temperature",
68 6: "Measured TX output power",
69 8: "Measured RX input power",
70 10: "AUX 1 measurement",
71 12: "AUX 2 measurement",
92 0x02: "Fibre Channel style 1 copper",
93 0x03: "Fibre Channel style 2 copper",
95 0x05: "Fibre Channel coax",
101 0x0b: "Optical pigtail",
103 0x21: "Copper pigtail",
108 ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW",
109 "10GBASE-LW", "10GBASE-EW"],
111 ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm",
112 "Intermediate reach 1300 nm FP"],
116 ["1000BASE-SX / 1xFC MMF", "1000BASE-LX / 1xFC SMF", "2xFC MMF",
117 "2xFC SMF", "OC48-SR", "OC48-IR", "OC48-LR"],
118 # 10GB SONET/SDH interconnect
119 ["I-64.1r", "I-64.1", "I-64.2r", "I-64.2", "I-64.3", "I-64.5"],
120 # 10GB SONET/SDH short haul
121 ["S-64.1", "S-64.2a", "S-64.2b", "S-64.3a", "S-64.3b", "S-64.5a", "S-64.5b"],
122 # 10GB SONET/SDH long haul
123 ["L-64.1", "L-64.2a", "L-64.2b", "L-64.2c", "L-64.3", "G.959.1 P1L1-2D2"],
124 # 10GB SONET/SDH very long haul
125 ["V-64.2a", "V-64.2b", "V-64.3"],
155 "lineside loopback mode",
160 ["no wavelength control", "sctive wavelength control"],
161 ["uncooled transmitter device", "cooled transmitter"],
162 ["PIN detector", "APD detector"],
163 ["transmitter not tunable", "transmitter tunable"],
170 "VPS LV regulator mode",
171 "VPS bypassed regulator mode",
172 "active FEC control",
173 "wavelength tunability",
185 "3.3V supply voltage",
186 "1.8V supply voltage",
187 "-5.2V supply voltage",
191 "3.3V supply current",
192 "1.8V supply current",
193 "-5.2V supply current",
196 class Decoder(srd.Decoder):
200 longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
201 desc = 'Data structure describing display device capabilities.'
209 ['XFP field names and values', 'XFP structure field names and values'],
210 ['XFP fields', 'XFP structure fields'],
213 def __init__(self, **kwargs):
214 # Received data items, used as an index into samplenum/data
216 # Start/end sample numbers per data item
218 # Multi-byte structure buffer
220 # Filled in by address 0x7f in low memory
221 self.cur_highmem_page = 0
222 # Filled in by extended ID value in table 2
223 self.have_clei = False
224 # Handlers for each field in the structure, keyed by the end
225 # index of that field. Each handler is fed all unhandled bytes
226 # up until that point, so mark unused space with the dummy
227 # handler self.ignore().
228 self.MAP_LOWER_MEMORY = {
231 57: self.alarm_warnings,
235 75: self.wavelength_cr,
238 109: self.ad_readout,
244 127: self.page_select,
246 self.MAP_HIGH_TABLE_1 = {
248 129: self.ext_module_id,
250 138: self.transceiver,
251 139: self.serial_encoding,
254 142: self.link_length_smf,
255 143: self.link_length_e50,
256 144: self.link_length_50um,
257 145: self.link_length_625um,
258 146: self.link_length_copper,
259 147: self.device_tech,
262 167: self.vendor_oui,
264 185: self.vendor_rev,
265 187: self.wavelength,
266 189: self.wavelength_tolerance,
267 190: self.max_case_temp,
269 195: self.power_supply,
271 219: self.manuf_date,
273 221: self.enhanced_opts,
276 255: self.maybe_ascii,
279 def start(self, metadata):
280 self.out_ann = self.add(srd.OUTPUT_ANN, 'xfp')
282 def decode(self, ss, es, data):
285 # We only care about actual data bytes that are read (for now).
286 if cmd != 'DATA READ':
290 self.sn.append([ss, es])
292 self.buf.append(data)
294 if self.cnt in self.MAP_LOWER_MEMORY:
295 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
297 elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01:
298 # Serial ID memory map
299 if self.cnt in self.MAP_HIGH_TABLE_1:
300 self.MAP_HIGH_TABLE_1[self.cnt](self.buf)
304 def annotate(self, key, value, start_cnt=None, end_cnt=None):
305 if start_cnt is None:
306 start_cnt = self.cnt - len(self.buf) + 1
309 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
310 self.out_ann, [0, [key + ": " + value]])
311 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
312 self.out_ann, [1, [value]])
314 # Placeholder handler, needed to advance the buffer past unused or
315 # reserved space in the structures.
316 def ignore(self, data):
319 # Show as ASCII if possible
320 def maybe_ascii(self, data):
321 for i in range(len(data)):
322 if data[i] >= 0x20 and data[i] < 0x7f:
323 cnt = self.cnt - len(data) + 1
324 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
326 # Convert 16-bit two's complement values, with each increment
327 # representing 1/256C, to degrees Celcius.
328 def to_temp(self, value):
330 value = -((value ^ 0xffff) + 1)
332 return "%.1f C" % temp
334 # TX bias current in uA. Each increment represents 0.2uA
335 def to_current(self, value):
336 current = value / 500000.0
337 return "%.1f mA" % current
339 # Power in mW, with each increment representing 0.1uW
340 def to_power(self, value):
341 power = value / 10000.0
342 return "%.2f mW" % power
344 # Wavelength in increments of 0.05nm
345 def to_wavelength(self, value):
349 # Wavelength in increments of 0.005nm
350 def to_wavelength_tolerance(self, value):
352 return "%.1f nm" % wl
354 def module_id(self, data):
355 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
357 def signal_cc(self, data):
358 # No good data available.
359 if (data[0] != 0x00):
360 self.annotate("Signal Conditioner Control", "%.2x" % data[0])
362 def alarm_warnings(self, data):
363 cnt_idx = self.cnt - len(data)
367 # Skip over reserved A/D flag thresholds
369 value = (data[idx] << 8) | data[idx + 1]
371 name = ALARM_THRESHOLDS.get(idx, "...")
372 if idx in (0, 2, 4, 6):
373 self.annotate(name, self.to_temp(value),
374 cnt_idx + idx, cnt_idx + idx + 1)
375 elif idx in (16, 18, 20, 22):
376 self.annotate(name, self.to_current(value),
377 cnt_idx + idx, cnt_idx + idx + 1)
378 elif idx in (24, 26, 28, 30, 32, 34, 36, 38):
379 self.annotate(name, self.to_power(value),
380 cnt_idx + idx, cnt_idx + idx + 1)
382 self.annotate(name, "%d" % name, value, cnt_idx + idx,
387 # No good data available.
389 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
392 # No good data available.
394 self.annotate("BER", str(data))
396 def wavelength_cr(self, data):
397 # No good data available.
398 if (data != [0, 0, 0, 0]):
399 self.annotate("WCR", str(data))
401 def fec_cr(self, data):
402 if (data != [0, 0, 0, 0]):
403 self.annotate("FEC", str(data))
405 def int_ctrl(self, data):
406 # No good data available. Also boring.
409 out.append("%.2x" % d)
410 self.annotate("Interrupt bits", ' '.join(out))
412 def ad_readout(self, data):
413 cnt_idx = self.cnt - len(data) + 1
417 # Skip over reserved field
419 value = (data[idx] << 8) | data[idx + 1]
420 name = AD_READOUTS.get(idx, "...")
423 self.annotate(name, self.to_temp(value),
424 cnt_idx + idx, cnt_idx + idx + 1)
426 self.annotate(name, self.to_current(value),
427 cnt_idx + idx, cnt_idx + idx + 1)
429 self.annotate(name, self.to_power(value),
430 cnt_idx + idx, cnt_idx + idx + 1)
432 self.annotate(name, str(value), cnt_idx + idx,
437 allbits = (data[0] << 8) | data[1]
441 out.append(GCS_BITS[b])
443 self.annotate("General Control/Status", ', '.join(out))
445 def page_select(self, data):
446 self.cur_highmem_page = data[0]
448 def ext_module_id(self, data):
449 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
450 if data[0] & 0x20 == 0:
452 if data[0] & 0x10 == 0:
453 out.append("TX ref clock input required")
454 if data[0] & 0x08 == 0:
455 self.have_clei = True
456 self.annotate("Extended id", ', '.join(out))
458 def connector(self, data):
459 if data[0] in CONNECTOR:
460 self.annotate("Connector", CONNECTOR[data[0]])
462 def transceiver(self, data):
470 if len(TRANSCEIVER[t]) < b + 1:
471 out.append("(unknown)")
473 out.append(TRANSCEIVER[t][b])
475 self.annotate("Transceiver compliance", ', '.join(out))
477 def serial_encoding(self, data):
482 if len(SERIAL_ENCODING) < b + 1:
483 out.append("(unknown)")
485 out.append(SERIAL_ENCODING[b])
487 self.annotate("Serial encoding support", ', '.join(out))
489 def br_min(self, data):
490 # Increments represent 100Mb/s
491 rate = data[0] / 10.0
492 self.annotate("Minimum bit rate", "%.3f GB/s" % rate)
494 def br_max(self, data):
495 # Increments represent 100Mb/s
496 rate = data[0] / 10.0
497 self.annotate("Maximum bit rate", "%.3f GB/s" % rate)
499 def link_length_smf(self, data):
501 length = "(standard)"
505 length = "%d km" % data[0]
506 self.annotate("Link length (SMF)", length)
508 def link_length_e50(self, data):
510 length = "(standard)"
514 length = "%d m" % (data[0] * 2)
515 self.annotate("Link length (extended, 50μm MMF)", length)
517 def link_length_50um(self, data):
519 length = "(standard)"
523 length = "%d m" % data[0]
524 self.annotate("Link length (50μm MMF)", length)
526 def link_length_625um(self, data):
528 length = "(standard)"
532 length = "%d m" % (data[0])
533 self.annotate("Link length (62.5μm MMF)", length)
535 def link_length_copper(self, data):
541 length = "%d m" % (data[0] * 2)
542 self.annotate("Link length (copper)", length)
544 def device_tech(self, data):
547 if xmit <= len(XMIT_TECH) - 1:
548 out.append("%s transmitter" % XMIT_TECH[xmit])
551 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
552 self.annotate("Device technology", ', '.join(out))
554 def vendor(self, data):
555 name = bytes(data).strip().decode('ascii').strip('\x00')
557 self.annotate("Vendor", name)
566 self.annotate("CDR support", ', '.join(out))
568 def vendor_oui(self, data):
569 if data != [0, 0, 0]:
570 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
572 def vendor_pn(self, data):
573 name = bytes(data).strip().decode('ascii').strip('\x00')
575 self.annotate("Vendor part number", name)
577 def vendor_rev(self, data):
578 name = bytes(data).strip().decode('ascii').strip('\x00')
580 self.annotate("Vendor revision", name)
582 def wavelength(self, data):
583 value = (data[0] << 8) | data[1]
584 self.annotate("Wavelength", self.to_wavelength(value))
586 def wavelength_tolerance(self, data):
587 value = (data[0] << 8) | data[1]
588 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
590 def max_case_temp(self, data):
591 self.annotate("Maximum case temperature", "%d C" % data[0])
593 def power_supply(self, data):
595 self.annotate("Max power dissipation",
596 "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3)
597 self.annotate("Max power dissipation (powered down)",
598 "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2)
599 value = (data[2] >> 4) * 0.050
600 self.annotate("Max current required (5V supply)",
601 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
602 value = (data[2] & 0x0f) * 0.100
603 self.annotate("Max current required (3.3V supply)",
604 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
605 value = (data[3] >> 4) * 0.100
606 self.annotate("Max current required (1.8V supply)",
607 "%.3f A" % value, self.cnt, self.cnt)
608 value = (data[3] & 0x0f) * 0.050
609 self.annotate("Max current required (-5.2V supply)",
610 "%.3f A" % value, self.cnt, self.cnt)
612 def vendor_sn(self, data):
613 name = bytes(data).strip().decode('ascii').strip('\x00')
615 self.annotate("Vendor serial number", name)
617 def manuf_date(self, data):
618 y = int(bytes(data[0:2])) + 2000
619 m = int(bytes(data[2:4]))
620 d = int(bytes(data[4:6]))
621 mnf = "%.4d-%.2d-%.2d" % (y, m, d)
622 lot = bytes(data[6:]).strip().decode('ascii').strip('\x00')
625 self.annotate("Manufacturing date", mnf)
627 def diag_mon(self, data):
630 out.append("BER support")
632 out.append("no BER support")
634 out.append("average power measurement")
636 out.append("OMA power measurement")
637 self.annotate("Diagnostic monitoring", ', '.join(out))
639 def enhanced_opts(self, data):
644 out.append(ENHANCED_OPTS[b])
646 self.annotate("Enhanced option support", ', '.join(out))
648 def aux_mon(self, data):
649 aux = AUX_TYPES[data[0] >> 4]
650 self.annotate("AUX1 monitoring", aux)
651 aux = AUX_TYPES[data[0] & 0x0f]
652 self.annotate("AUX2 monitoring", aux)