]> sigrok.org Git - libsigrokdecode.git/blame - decoders/xfp/pd.py
All PDs: Minor whitespace and consistency fixes.
[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):
12851357 194 api_version = 2
845f0d40
BV
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']
da9bcbd9
BV
202 annotations = (
203 ('fieldnames-and-values', 'XFP structure field names and values'),
204 ('fields', 'XFP structure fields'),
205 )
845f0d40
BV
206
207 def __init__(self, **kwargs):
208 # Received data items, used as an index into samplenum/data
209 self.cnt = -1
210 # Start/end sample numbers per data item
211 self.sn = []
212 # Multi-byte structure buffer
213 self.buf = []
214 # Filled in by address 0x7f in low memory
215 self.cur_highmem_page = 0
216 # Filled in by extended ID value in table 2
217 self.have_clei = False
218 # Handlers for each field in the structure, keyed by the end
219 # index of that field. Each handler is fed all unhandled bytes
220 # up until that point, so mark unused space with the dummy
221 # handler self.ignore().
222 self.MAP_LOWER_MEMORY = {
223 0: self.module_id,
224 1: self.signal_cc,
225 57: self.alarm_warnings,
226 59: self.vps,
227 69: self.ignore,
228 71: self.ber,
229 75: self.wavelength_cr,
230 79: self.fec_cr,
231 95: self.int_ctrl,
232 109: self.ad_readout,
233 111: self.gcs,
234 117: self.ignore,
235 118: self.ignore,
236 122: self.ignore,
237 126: self.ignore,
238 127: self.page_select,
239 }
240 self.MAP_HIGH_TABLE_1 = {
241 128: self.module_id,
242 129: self.ext_module_id,
243 130: self.connector,
244 138: self.transceiver,
245 139: self.serial_encoding,
246 140: self.br_min,
247 141: self.br_max,
248 142: self.link_length_smf,
249 143: self.link_length_e50,
250 144: self.link_length_50um,
251 145: self.link_length_625um,
252 146: self.link_length_copper,
253 147: self.device_tech,
254 163: self.vendor,
255 164: self.cdr,
256 167: self.vendor_oui,
257 183: self.vendor_pn,
258 185: self.vendor_rev,
259 187: self.wavelength,
260 189: self.wavelength_tolerance,
261 190: self.max_case_temp,
262 191: self.ignore,
263 195: self.power_supply,
264 211: self.vendor_sn,
265 219: self.manuf_date,
266 220: self.diag_mon,
267 221: self.enhanced_opts,
268 222: self.aux_mon,
269 223: self.ignore,
270 255: self.maybe_ascii,
271 }
272
8915b346 273 def start(self):
be465111 274 self.out_ann = self.register(srd.OUTPUT_ANN)
845f0d40
BV
275
276 def decode(self, ss, es, data):
277 cmd, data = data
278
279 # We only care about actual data bytes that are read (for now).
280 if cmd != 'DATA READ':
281 return
282
283 self.cnt += 1
284 self.sn.append([ss, es])
285
286 self.buf.append(data)
287 if self.cnt < 0x80:
288 if self.cnt in self.MAP_LOWER_MEMORY:
289 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
290 self.buf.clear()
291 elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01:
292 # Serial ID memory map
293 if self.cnt in self.MAP_HIGH_TABLE_1:
294 self.MAP_HIGH_TABLE_1[self.cnt](self.buf)
295 self.buf.clear()
296
297 # Annotation helper
298 def annotate(self, key, value, start_cnt=None, end_cnt=None):
299 if start_cnt is None:
300 start_cnt = self.cnt - len(self.buf) + 1
301 if end_cnt is None:
302 end_cnt = self.cnt
303 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
304 self.out_ann, [0, [key + ": " + value]])
305 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
306 self.out_ann, [1, [value]])
307
308 # Placeholder handler, needed to advance the buffer past unused or
309 # reserved space in the structures.
310 def ignore(self, data):
311 pass
312
313 # Show as ASCII if possible
314 def maybe_ascii(self, data):
315 for i in range(len(data)):
316 if data[i] >= 0x20 and data[i] < 0x7f:
317 cnt = self.cnt - len(data) + 1
318 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
319
320 # Convert 16-bit two's complement values, with each increment
321 # representing 1/256C, to degrees Celcius.
322 def to_temp(self, value):
323 if value & 0x8000:
324 value = -((value ^ 0xffff) + 1)
325 temp = value / 256.0
326 return "%.1f C" % temp
327
328 # TX bias current in uA. Each increment represents 0.2uA
329 def to_current(self, value):
330 current = value / 500000.0
331 return "%.1f mA" % current
332
333 # Power in mW, with each increment representing 0.1uW
334 def to_power(self, value):
335 power = value / 10000.0
336 return "%.2f mW" % power
337
338 # Wavelength in increments of 0.05nm
339 def to_wavelength(self, value):
340 wl = value / 20
341 return "%d nm" % wl
342
343 # Wavelength in increments of 0.005nm
344 def to_wavelength_tolerance(self, value):
345 wl = value / 200.0
346 return "%.1f nm" % wl
347
348 def module_id(self, data):
349 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
350
351 def signal_cc(self, data):
352 # No good data available.
353 if (data[0] != 0x00):
354 self.annotate("Signal Conditioner Control", "%.2x" % data[0])
355
356 def alarm_warnings(self, data):
357 cnt_idx = self.cnt - len(data)
358 idx = 0
359 while idx < 56:
360 if idx == 8:
361 # Skip over reserved A/D flag thresholds
362 idx += 8
363 value = (data[idx] << 8) | data[idx + 1]
364 if value != 0:
365 name = ALARM_THRESHOLDS.get(idx, "...")
366 if idx in (0, 2, 4, 6):
367 self.annotate(name, self.to_temp(value),
368 cnt_idx + idx, cnt_idx + idx + 1)
369 elif idx in (16, 18, 20, 22):
370 self.annotate(name, self.to_current(value),
371 cnt_idx + idx, cnt_idx + idx + 1)
372 elif idx in (24, 26, 28, 30, 32, 34, 36, 38):
373 self.annotate(name, self.to_power(value),
374 cnt_idx + idx, cnt_idx + idx + 1)
375 else:
376 self.annotate(name, "%d" % name, value, cnt_idx + idx,
377 cnt_idx + idx + 1)
378 idx += 2
379
380 def vps(self, data):
381 # No good data available.
382 if (data != [0, 0]):
383 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
384
385 def ber(self, data):
386 # No good data available.
387 if (data != [0, 0]):
388 self.annotate("BER", str(data))
389
390 def wavelength_cr(self, data):
391 # No good data available.
392 if (data != [0, 0, 0, 0]):
393 self.annotate("WCR", str(data))
394
395 def fec_cr(self, data):
396 if (data != [0, 0, 0, 0]):
397 self.annotate("FEC", str(data))
398
399 def int_ctrl(self, data):
400 # No good data available. Also boring.
401 out = []
402 for d in data:
403 out.append("%.2x" % d)
404 self.annotate("Interrupt bits", ' '.join(out))
405
406 def ad_readout(self, data):
407 cnt_idx = self.cnt - len(data) + 1
408 idx = 0
409 while idx < 14:
410 if idx == 2:
411 # Skip over reserved field
412 idx += 2
413 value = (data[idx] << 8) | data[idx + 1]
414 name = AD_READOUTS.get(idx, "...")
415 if value != 0:
416 if idx == 0:
417 self.annotate(name, self.to_temp(value),
418 cnt_idx + idx, cnt_idx + idx + 1)
419 elif idx == 4:
420 self.annotate(name, self.to_current(value),
421 cnt_idx + idx, cnt_idx + idx + 1)
422 elif idx in (6, 8):
423 self.annotate(name, self.to_power(value),
424 cnt_idx + idx, cnt_idx + idx + 1)
425 else:
426 self.annotate(name, str(value), cnt_idx + idx,
427 cnt_idx + idx + 1)
428 idx += 2
429
430 def gcs(self, data):
431 allbits = (data[0] << 8) | data[1]
432 out = []
433 for b in range(13):
434 if allbits & 0x8000:
435 out.append(GCS_BITS[b])
436 allbits <<= 1
437 self.annotate("General Control/Status", ', '.join(out))
438
439 def page_select(self, data):
440 self.cur_highmem_page = data[0]
441
442 def ext_module_id(self, data):
443 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
444 if data[0] & 0x20 == 0:
445 out.append("CDR")
446 if data[0] & 0x10 == 0:
447 out.append("TX ref clock input required")
448 if data[0] & 0x08 == 0:
449 self.have_clei = True
450 self.annotate("Extended id", ', '.join(out))
451
452 def connector(self, data):
453 if data[0] in CONNECTOR:
454 self.annotate("Connector", CONNECTOR[data[0]])
455
456 def transceiver(self, data):
457 out = []
458 for t in range(8):
459 if data[t] == 0:
460 continue
461 value = data[t]
462 for b in range(8):
463 if value & 0x80:
464 if len(TRANSCEIVER[t]) < b + 1:
465 out.append("(unknown)")
466 else:
467 out.append(TRANSCEIVER[t][b])
468 value <<= 1
469 self.annotate("Transceiver compliance", ', '.join(out))
470
471 def serial_encoding(self, data):
472 out = []
473 value = data[0]
474 for b in range(8):
475 if value & 0x80:
476 if len(SERIAL_ENCODING) < b + 1:
477 out.append("(unknown)")
478 else:
479 out.append(SERIAL_ENCODING[b])
480 value <<= 1
481 self.annotate("Serial encoding support", ', '.join(out))
482
483 def br_min(self, data):
484 # Increments represent 100Mb/s
485 rate = data[0] / 10.0
486 self.annotate("Minimum bit rate", "%.3f GB/s" % rate)
487
488 def br_max(self, data):
489 # Increments represent 100Mb/s
490 rate = data[0] / 10.0
491 self.annotate("Maximum bit rate", "%.3f GB/s" % rate)
492
493 def link_length_smf(self, data):
494 if data[0] == 0:
495 length = "(standard)"
496 elif data[0] == 255:
497 length = "> 254 km"
498 else:
499 length = "%d km" % data[0]
500 self.annotate("Link length (SMF)", length)
501
502 def link_length_e50(self, data):
503 if data[0] == 0:
504 length = "(standard)"
505 elif data[0] == 255:
506 length = "> 508 m"
507 else:
508 length = "%d m" % (data[0] * 2)
509 self.annotate("Link length (extended, 50μm MMF)", length)
510
511 def link_length_50um(self, data):
512 if data[0] == 0:
513 length = "(standard)"
514 elif data[0] == 255:
515 length = "> 254 m"
516 else:
517 length = "%d m" % data[0]
518 self.annotate("Link length (50μm MMF)", length)
519
520 def link_length_625um(self, data):
521 if data[0] == 0:
522 length = "(standard)"
523 elif data[0] == 255:
524 length = "> 254 m"
525 else:
526 length = "%d m" % (data[0])
527 self.annotate("Link length (62.5μm MMF)", length)
528
529 def link_length_copper(self, data):
530 if data[0] == 0:
531 length = "(unknown)"
532 elif data[0] == 255:
533 length = "> 254 m"
534 else:
535 length = "%d m" % (data[0] * 2)
536 self.annotate("Link length (copper)", length)
537
538 def device_tech(self, data):
539 out = []
540 xmit = data[0] >> 4
541 if xmit <= len(XMIT_TECH) - 1:
542 out.append("%s transmitter" % XMIT_TECH[xmit])
543 dev = data[0] & 0x0f
544 for b in range(4):
545 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
546 self.annotate("Device technology", ', '.join(out))
547
548 def vendor(self, data):
549 name = bytes(data).strip().decode('ascii').strip('\x00')
550 if name:
551 self.annotate("Vendor", name)
552
553 def cdr(self, data):
554 out = []
555 value = data[0]
556 for b in range(8):
557 if value & 0x80:
558 out.append(CDR[b])
559 value <<= 1
560 self.annotate("CDR support", ', '.join(out))
561
562 def vendor_oui(self, data):
563 if data != [0, 0, 0]:
564 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
565
566 def vendor_pn(self, data):
567 name = bytes(data).strip().decode('ascii').strip('\x00')
568 if name:
569 self.annotate("Vendor part number", name)
570
571 def vendor_rev(self, data):
572 name = bytes(data).strip().decode('ascii').strip('\x00')
573 if name:
574 self.annotate("Vendor revision", name)
575
576 def wavelength(self, data):
577 value = (data[0] << 8) | data[1]
578 self.annotate("Wavelength", self.to_wavelength(value))
579
580 def wavelength_tolerance(self, data):
581 value = (data[0] << 8) | data[1]
582 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
583
584 def max_case_temp(self, data):
585 self.annotate("Maximum case temperature", "%d C" % data[0])
586
587 def power_supply(self, data):
588 out = []
589 self.annotate("Max power dissipation",
590 "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3)
591 self.annotate("Max power dissipation (powered down)",
592 "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2)
593 value = (data[2] >> 4) * 0.050
594 self.annotate("Max current required (5V supply)",
595 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
596 value = (data[2] & 0x0f) * 0.100
597 self.annotate("Max current required (3.3V supply)",
598 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
599 value = (data[3] >> 4) * 0.100
600 self.annotate("Max current required (1.8V supply)",
601 "%.3f A" % value, self.cnt, self.cnt)
602 value = (data[3] & 0x0f) * 0.050
603 self.annotate("Max current required (-5.2V supply)",
604 "%.3f A" % value, self.cnt, self.cnt)
605
606 def vendor_sn(self, data):
607 name = bytes(data).strip().decode('ascii').strip('\x00')
608 if name:
609 self.annotate("Vendor serial number", name)
610
611 def manuf_date(self, data):
612 y = int(bytes(data[0:2])) + 2000
613 m = int(bytes(data[2:4]))
614 d = int(bytes(data[4:6]))
615 mnf = "%.4d-%.2d-%.2d" % (y, m, d)
616 lot = bytes(data[6:]).strip().decode('ascii').strip('\x00')
617 if lot:
618 mnf += " lot " + lot
619 self.annotate("Manufacturing date", mnf)
620
621 def diag_mon(self, data):
622 out = []
623 if data[0] & 0x10:
624 out.append("BER support")
625 else:
626 out.append("no BER support")
627 if data[0] & 0x08:
628 out.append("average power measurement")
629 else:
630 out.append("OMA power measurement")
631 self.annotate("Diagnostic monitoring", ', '.join(out))
632
633 def enhanced_opts(self, data):
634 out = []
635 value = data[0]
636 for b in range(8):
637 if value & 0x80:
638 out.append(ENHANCED_OPTS[b])
639 value <<= 1
640 self.annotate("Enhanced option support", ', '.join(out))
641
642 def aux_mon(self, data):
643 aux = AUX_TYPES[data[0] >> 4]
644 self.annotate("AUX1 monitoring", aux)
645 aux = AUX_TYPES[data[0] & 0x0f]
646 self.annotate("AUX2 monitoring", aux)