]> sigrok.org Git - libsigrokdecode.git/blame - decoders/xfp/pd.py
The start() method no longer takes a metadata parameter
[libsigrokdecode.git] / decoders / xfp / pd.py
CommitLineData
845f0d40
BV
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
5##
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.
10##
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.
15##
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/>.
18##
19
20# XFP protocol decoder
21
22import sigrokdecode as srd
23import os
24
25MODULE_ID = {
26 0x01: 'GBIC',
27 0x02: 'Integrated module/connector',
28 0x03: 'SFP',
29 0x04: '300-pin XBI',
30 0x05: 'XENPAK',
31 0x06: 'XFP',
32 0x07: 'XFF',
33 0x08: 'XFP-E',
34 0x09: 'XPAK',
35 0x0a: 'X2',
36}
37
38ALARM_THRESHOLDS = {
39 0: "Temp high alarm",
40 2: "Temp low alarm",
41 4: "Temp high warning",
42 6: "Temp low warning",
43 16: "Bias high alarm",
44 18: "Bias low 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",
63}
64
65AD_READOUTS = {
66 0: "Module temperature",
67 4: "TX bias current",
68 6: "Measured TX output power",
69 8: "Measured RX input power",
70 10: "AUX 1 measurement",
71 12: "AUX 2 measurement",
72}
73
74GCS_BITS = [
75 "TX disable",
76 "Soft TX disable",
77 "MOD_NR",
78 "P_Down",
79 "Soft P_Down",
80 "Interrupt",
81 "RX_LOS",
82 "Data_Not_Ready",
83 "TX_NR",
84 "TX_Fault",
85 "TX_CDR not locked",
86 "RX_NR",
87 "RX_CDR not locked",
88]
89
90CONNECTOR = {
91 0x01: "SC",
92 0x02: "Fibre Channel style 1 copper",
93 0x03: "Fibre Channel style 2 copper",
94 0x04: "BNC/TNC",
95 0x05: "Fibre Channel coax",
96 0x06: "FiberJack",
97 0x07: "LC",
98 0x08: "MT-RJ",
99 0x09: "MU",
100 0x0a: "SG",
101 0x0b: "Optical pigtail",
102 0x20: "HSSDC II",
103 0x21: "Copper pigtail",
104}
105
106TRANSCEIVER = [
107 # 10GB Ethernet
108 ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW",
109 "10GBASE-LW", "10GBASE-EW"],
110 # 10GB Fibre Channel
111 ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm",
112 "Intermediate reach 1300 nm FP"],
113 # 10GB Copper
114 [],
115 # 10GB low speed
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"],
126]
127
128SERIAL_ENCODING = [
129 "64B/66B",
130 "8B/10B",
131 "SONET scrambled",
132 "NRZ",
133 "RZ",
134]
135
136XMIT_TECH = [
137 "850 nm VCSEL",
138 "1310 nm VCSEL",
139 "1550 nm VCSEL",
140 "1310 nm FP",
141 "1310 nm DFB",
142 "1550 nm DFB",
143 "1310 nm EML"
144 "1550 nm EML"
145 "copper",
146]
147
148CDR = [
149 "9.95Gb/s",
150 "10.3Gb/s",
151 "10.5Gb/s",
152 "10.7Gb/s",
153 "11.1Gb/s",
154 "(unknown)",
155 "lineside loopback mode",
156 "XFI loopback mode",
157]
158
159DEVICE_TECH = [
160 ["no wavelength control", "sctive wavelength control"],
161 ["uncooled transmitter device", "cooled transmitter"],
162 ["PIN detector", "APD detector"],
163 ["transmitter not tunable", "transmitter tunable"],
164]
165
166ENHANCED_OPTS = [
167 "VPS",
168 "soft TX_DISABLE",
169 "soft P_Down",
170 "VPS LV regulator mode",
171 "VPS bypassed regulator mode",
172 "active FEC control",
173 "wavelength tunability",
174 "CMU",
175]
176
177AUX_TYPES = [
178 "not implemented",
179 "APD bias voltage",
180 "(unknown)",
181 "TEC current",
182 "laser temperature",
183 "laser wavelength",
184 "5V supply voltage",
185 "3.3V supply voltage",
186 "1.8V supply voltage",
187 "-5.2V supply voltage",
188 "5V supply current",
189 "(unknown)",
190 "(unknown)",
191 "3.3V supply current",
192 "1.8V supply current",
193 "-5.2V supply current",
194]
195
196class Decoder(srd.Decoder):
197 api_version = 1
198 id = 'xfp'
199 name = 'XFP'
200 longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
201 desc = 'Data structure describing display device capabilities.'
202 license = 'gplv3+'
203 inputs = ['i2c']
204 outputs = ['xfp']
205 probes = []
206 optional_probes = []
207 options = {}
208 annotations = [
209 ['XFP field names and values', 'XFP structure field names and values'],
210 ['XFP fields', 'XFP structure fields'],
211 ]
212
213 def __init__(self, **kwargs):
214 # Received data items, used as an index into samplenum/data
215 self.cnt = -1
216 # Start/end sample numbers per data item
217 self.sn = []
218 # Multi-byte structure buffer
219 self.buf = []
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 = {
229 0: self.module_id,
230 1: self.signal_cc,
231 57: self.alarm_warnings,
232 59: self.vps,
233 69: self.ignore,
234 71: self.ber,
235 75: self.wavelength_cr,
236 79: self.fec_cr,
237 95: self.int_ctrl,
238 109: self.ad_readout,
239 111: self.gcs,
240 117: self.ignore,
241 118: self.ignore,
242 122: self.ignore,
243 126: self.ignore,
244 127: self.page_select,
245 }
246 self.MAP_HIGH_TABLE_1 = {
247 128: self.module_id,
248 129: self.ext_module_id,
249 130: self.connector,
250 138: self.transceiver,
251 139: self.serial_encoding,
252 140: self.br_min,
253 141: self.br_max,
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,
260 163: self.vendor,
261 164: self.cdr,
262 167: self.vendor_oui,
263 183: self.vendor_pn,
264 185: self.vendor_rev,
265 187: self.wavelength,
266 189: self.wavelength_tolerance,
267 190: self.max_case_temp,
268 191: self.ignore,
269 195: self.power_supply,
270 211: self.vendor_sn,
271 219: self.manuf_date,
272 220: self.diag_mon,
273 221: self.enhanced_opts,
274 222: self.aux_mon,
275 223: self.ignore,
276 255: self.maybe_ascii,
277 }
278
8915b346 279 def start(self):
845f0d40
BV
280 self.out_ann = self.add(srd.OUTPUT_ANN, 'xfp')
281
282 def decode(self, ss, es, data):
283 cmd, data = data
284
285 # We only care about actual data bytes that are read (for now).
286 if cmd != 'DATA READ':
287 return
288
289 self.cnt += 1
290 self.sn.append([ss, es])
291
292 self.buf.append(data)
293 if self.cnt < 0x80:
294 if self.cnt in self.MAP_LOWER_MEMORY:
295 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
296 self.buf.clear()
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)
301 self.buf.clear()
302
303 # Annotation helper
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
307 if end_cnt is None:
308 end_cnt = self.cnt
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]])
313
314 # Placeholder handler, needed to advance the buffer past unused or
315 # reserved space in the structures.
316 def ignore(self, data):
317 pass
318
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)
325
326 # Convert 16-bit two's complement values, with each increment
327 # representing 1/256C, to degrees Celcius.
328 def to_temp(self, value):
329 if value & 0x8000:
330 value = -((value ^ 0xffff) + 1)
331 temp = value / 256.0
332 return "%.1f C" % temp
333
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
338
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
343
344 # Wavelength in increments of 0.05nm
345 def to_wavelength(self, value):
346 wl = value / 20
347 return "%d nm" % wl
348
349 # Wavelength in increments of 0.005nm
350 def to_wavelength_tolerance(self, value):
351 wl = value / 200.0
352 return "%.1f nm" % wl
353
354 def module_id(self, data):
355 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
356
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])
361
362 def alarm_warnings(self, data):
363 cnt_idx = self.cnt - len(data)
364 idx = 0
365 while idx < 56:
366 if idx == 8:
367 # Skip over reserved A/D flag thresholds
368 idx += 8
369 value = (data[idx] << 8) | data[idx + 1]
370 if value != 0:
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)
381 else:
382 self.annotate(name, "%d" % name, value, cnt_idx + idx,
383 cnt_idx + idx + 1)
384 idx += 2
385
386 def vps(self, data):
387 # No good data available.
388 if (data != [0, 0]):
389 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
390
391 def ber(self, data):
392 # No good data available.
393 if (data != [0, 0]):
394 self.annotate("BER", str(data))
395
396 def wavelength_cr(self, data):
397 # No good data available.
398 if (data != [0, 0, 0, 0]):
399 self.annotate("WCR", str(data))
400
401 def fec_cr(self, data):
402 if (data != [0, 0, 0, 0]):
403 self.annotate("FEC", str(data))
404
405 def int_ctrl(self, data):
406 # No good data available. Also boring.
407 out = []
408 for d in data:
409 out.append("%.2x" % d)
410 self.annotate("Interrupt bits", ' '.join(out))
411
412 def ad_readout(self, data):
413 cnt_idx = self.cnt - len(data) + 1
414 idx = 0
415 while idx < 14:
416 if idx == 2:
417 # Skip over reserved field
418 idx += 2
419 value = (data[idx] << 8) | data[idx + 1]
420 name = AD_READOUTS.get(idx, "...")
421 if value != 0:
422 if idx == 0:
423 self.annotate(name, self.to_temp(value),
424 cnt_idx + idx, cnt_idx + idx + 1)
425 elif idx == 4:
426 self.annotate(name, self.to_current(value),
427 cnt_idx + idx, cnt_idx + idx + 1)
428 elif idx in (6, 8):
429 self.annotate(name, self.to_power(value),
430 cnt_idx + idx, cnt_idx + idx + 1)
431 else:
432 self.annotate(name, str(value), cnt_idx + idx,
433 cnt_idx + idx + 1)
434 idx += 2
435
436 def gcs(self, data):
437 allbits = (data[0] << 8) | data[1]
438 out = []
439 for b in range(13):
440 if allbits & 0x8000:
441 out.append(GCS_BITS[b])
442 allbits <<= 1
443 self.annotate("General Control/Status", ', '.join(out))
444
445 def page_select(self, data):
446 self.cur_highmem_page = data[0]
447
448 def ext_module_id(self, data):
449 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
450 if data[0] & 0x20 == 0:
451 out.append("CDR")
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))
457
458 def connector(self, data):
459 if data[0] in CONNECTOR:
460 self.annotate("Connector", CONNECTOR[data[0]])
461
462 def transceiver(self, data):
463 out = []
464 for t in range(8):
465 if data[t] == 0:
466 continue
467 value = data[t]
468 for b in range(8):
469 if value & 0x80:
470 if len(TRANSCEIVER[t]) < b + 1:
471 out.append("(unknown)")
472 else:
473 out.append(TRANSCEIVER[t][b])
474 value <<= 1
475 self.annotate("Transceiver compliance", ', '.join(out))
476
477 def serial_encoding(self, data):
478 out = []
479 value = data[0]
480 for b in range(8):
481 if value & 0x80:
482 if len(SERIAL_ENCODING) < b + 1:
483 out.append("(unknown)")
484 else:
485 out.append(SERIAL_ENCODING[b])
486 value <<= 1
487 self.annotate("Serial encoding support", ', '.join(out))
488
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)
493
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)
498
499 def link_length_smf(self, data):
500 if data[0] == 0:
501 length = "(standard)"
502 elif data[0] == 255:
503 length = "> 254 km"
504 else:
505 length = "%d km" % data[0]
506 self.annotate("Link length (SMF)", length)
507
508 def link_length_e50(self, data):
509 if data[0] == 0:
510 length = "(standard)"
511 elif data[0] == 255:
512 length = "> 508 m"
513 else:
514 length = "%d m" % (data[0] * 2)
515 self.annotate("Link length (extended, 50μm MMF)", length)
516
517 def link_length_50um(self, data):
518 if data[0] == 0:
519 length = "(standard)"
520 elif data[0] == 255:
521 length = "> 254 m"
522 else:
523 length = "%d m" % data[0]
524 self.annotate("Link length (50μm MMF)", length)
525
526 def link_length_625um(self, data):
527 if data[0] == 0:
528 length = "(standard)"
529 elif data[0] == 255:
530 length = "> 254 m"
531 else:
532 length = "%d m" % (data[0])
533 self.annotate("Link length (62.5μm MMF)", length)
534
535 def link_length_copper(self, data):
536 if data[0] == 0:
537 length = "(unknown)"
538 elif data[0] == 255:
539 length = "> 254 m"
540 else:
541 length = "%d m" % (data[0] * 2)
542 self.annotate("Link length (copper)", length)
543
544 def device_tech(self, data):
545 out = []
546 xmit = data[0] >> 4
547 if xmit <= len(XMIT_TECH) - 1:
548 out.append("%s transmitter" % XMIT_TECH[xmit])
549 dev = data[0] & 0x0f
550 for b in range(4):
551 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
552 self.annotate("Device technology", ', '.join(out))
553
554 def vendor(self, data):
555 name = bytes(data).strip().decode('ascii').strip('\x00')
556 if name:
557 self.annotate("Vendor", name)
558
559 def cdr(self, data):
560 out = []
561 value = data[0]
562 for b in range(8):
563 if value & 0x80:
564 out.append(CDR[b])
565 value <<= 1
566 self.annotate("CDR support", ', '.join(out))
567
568 def vendor_oui(self, data):
569 if data != [0, 0, 0]:
570 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
571
572 def vendor_pn(self, data):
573 name = bytes(data).strip().decode('ascii').strip('\x00')
574 if name:
575 self.annotate("Vendor part number", name)
576
577 def vendor_rev(self, data):
578 name = bytes(data).strip().decode('ascii').strip('\x00')
579 if name:
580 self.annotate("Vendor revision", name)
581
582 def wavelength(self, data):
583 value = (data[0] << 8) | data[1]
584 self.annotate("Wavelength", self.to_wavelength(value))
585
586 def wavelength_tolerance(self, data):
587 value = (data[0] << 8) | data[1]
588 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
589
590 def max_case_temp(self, data):
591 self.annotate("Maximum case temperature", "%d C" % data[0])
592
593 def power_supply(self, data):
594 out = []
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)
611
612 def vendor_sn(self, data):
613 name = bytes(data).strip().decode('ascii').strip('\x00')
614 if name:
615 self.annotate("Vendor serial number", name)
616
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')
623 if lot:
624 mnf += " lot " + lot
625 self.annotate("Manufacturing date", mnf)
626
627 def diag_mon(self, data):
628 out = []
629 if data[0] & 0x10:
630 out.append("BER support")
631 else:
632 out.append("no BER support")
633 if data[0] & 0x08:
634 out.append("average power measurement")
635 else:
636 out.append("OMA power measurement")
637 self.annotate("Diagnostic monitoring", ', '.join(out))
638
639 def enhanced_opts(self, data):
640 out = []
641 value = data[0]
642 for b in range(8):
643 if value & 0x80:
644 out.append(ENHANCED_OPTS[b])
645 value <<= 1
646 self.annotate("Enhanced option support", ', '.join(out))
647
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)
653