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