]> sigrok.org Git - libsigrokdecode.git/blame - decoders/xfp/pd.py
Use consistent __init__() format across all PDs.
[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
1049c90f
BV
21from plugtrx import (MODULE_ID, ALARM_THRESHOLDS, AD_READOUTS, GCS_BITS,
22 CONNECTOR, TRANSCEIVER, SERIAL_ENCODING, XMIT_TECH, CDR, DEVICE_TECH,
23 ENHANCED_OPTS, AUX_TYPES)
845f0d40
BV
24
25class Decoder(srd.Decoder):
12851357 26 api_version = 2
845f0d40
BV
27 id = 'xfp'
28 name = 'XFP'
29 longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)'
30 desc = 'Data structure describing display device capabilities.'
31 license = 'gplv3+'
32 inputs = ['i2c']
33 outputs = ['xfp']
da9bcbd9
BV
34 annotations = (
35 ('fieldnames-and-values', 'XFP structure field names and values'),
36 ('fields', 'XFP structure fields'),
37 )
845f0d40 38
92b7b49f 39 def __init__(self):
845f0d40
BV
40 # Received data items, used as an index into samplenum/data
41 self.cnt = -1
42 # Start/end sample numbers per data item
43 self.sn = []
44 # Multi-byte structure buffer
45 self.buf = []
46 # Filled in by address 0x7f in low memory
47 self.cur_highmem_page = 0
48 # Filled in by extended ID value in table 2
49 self.have_clei = False
50 # Handlers for each field in the structure, keyed by the end
51 # index of that field. Each handler is fed all unhandled bytes
52 # up until that point, so mark unused space with the dummy
53 # handler self.ignore().
54 self.MAP_LOWER_MEMORY = {
55 0: self.module_id,
56 1: self.signal_cc,
57 57: self.alarm_warnings,
58 59: self.vps,
59 69: self.ignore,
60 71: self.ber,
61 75: self.wavelength_cr,
62 79: self.fec_cr,
63 95: self.int_ctrl,
64 109: self.ad_readout,
65 111: self.gcs,
66 117: self.ignore,
67 118: self.ignore,
68 122: self.ignore,
69 126: self.ignore,
70 127: self.page_select,
71 }
72 self.MAP_HIGH_TABLE_1 = {
73 128: self.module_id,
74 129: self.ext_module_id,
75 130: self.connector,
76 138: self.transceiver,
77 139: self.serial_encoding,
78 140: self.br_min,
79 141: self.br_max,
80 142: self.link_length_smf,
81 143: self.link_length_e50,
82 144: self.link_length_50um,
83 145: self.link_length_625um,
84 146: self.link_length_copper,
85 147: self.device_tech,
86 163: self.vendor,
87 164: self.cdr,
88 167: self.vendor_oui,
89 183: self.vendor_pn,
90 185: self.vendor_rev,
91 187: self.wavelength,
92 189: self.wavelength_tolerance,
93 190: self.max_case_temp,
94 191: self.ignore,
95 195: self.power_supply,
96 211: self.vendor_sn,
97 219: self.manuf_date,
98 220: self.diag_mon,
99 221: self.enhanced_opts,
100 222: self.aux_mon,
101 223: self.ignore,
102 255: self.maybe_ascii,
103 }
104
8915b346 105 def start(self):
be465111 106 self.out_ann = self.register(srd.OUTPUT_ANN)
845f0d40
BV
107
108 def decode(self, ss, es, data):
109 cmd, data = data
110
111 # We only care about actual data bytes that are read (for now).
112 if cmd != 'DATA READ':
113 return
114
115 self.cnt += 1
116 self.sn.append([ss, es])
117
118 self.buf.append(data)
119 if self.cnt < 0x80:
120 if self.cnt in self.MAP_LOWER_MEMORY:
121 self.MAP_LOWER_MEMORY[self.cnt](self.buf)
122 self.buf.clear()
123 elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01:
124 # Serial ID memory map
125 if self.cnt in self.MAP_HIGH_TABLE_1:
126 self.MAP_HIGH_TABLE_1[self.cnt](self.buf)
127 self.buf.clear()
128
129 # Annotation helper
130 def annotate(self, key, value, start_cnt=None, end_cnt=None):
131 if start_cnt is None:
132 start_cnt = self.cnt - len(self.buf) + 1
133 if end_cnt is None:
134 end_cnt = self.cnt
135 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
136 self.out_ann, [0, [key + ": " + value]])
137 self.put(self.sn[start_cnt][0], self.sn[end_cnt][1],
138 self.out_ann, [1, [value]])
139
140 # Placeholder handler, needed to advance the buffer past unused or
141 # reserved space in the structures.
142 def ignore(self, data):
143 pass
144
145 # Show as ASCII if possible
146 def maybe_ascii(self, data):
147 for i in range(len(data)):
148 if data[i] >= 0x20 and data[i] < 0x7f:
149 cnt = self.cnt - len(data) + 1
150 self.annotate("Vendor ID", chr(data[i]), cnt, cnt)
151
152 # Convert 16-bit two's complement values, with each increment
868fd207 153 # representing 1/256C, to degrees Celsius.
845f0d40
BV
154 def to_temp(self, value):
155 if value & 0x8000:
156 value = -((value ^ 0xffff) + 1)
157 temp = value / 256.0
158 return "%.1f C" % temp
159
160 # TX bias current in uA. Each increment represents 0.2uA
161 def to_current(self, value):
162 current = value / 500000.0
163 return "%.1f mA" % current
164
165 # Power in mW, with each increment representing 0.1uW
166 def to_power(self, value):
167 power = value / 10000.0
168 return "%.2f mW" % power
169
170 # Wavelength in increments of 0.05nm
171 def to_wavelength(self, value):
172 wl = value / 20
173 return "%d nm" % wl
174
175 # Wavelength in increments of 0.005nm
176 def to_wavelength_tolerance(self, value):
177 wl = value / 200.0
178 return "%.1f nm" % wl
179
180 def module_id(self, data):
181 self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown"))
182
183 def signal_cc(self, data):
184 # No good data available.
185 if (data[0] != 0x00):
186 self.annotate("Signal Conditioner Control", "%.2x" % data[0])
187
188 def alarm_warnings(self, data):
189 cnt_idx = self.cnt - len(data)
190 idx = 0
191 while idx < 56:
192 if idx == 8:
193 # Skip over reserved A/D flag thresholds
194 idx += 8
195 value = (data[idx] << 8) | data[idx + 1]
196 if value != 0:
197 name = ALARM_THRESHOLDS.get(idx, "...")
198 if idx in (0, 2, 4, 6):
199 self.annotate(name, self.to_temp(value),
200 cnt_idx + idx, cnt_idx + idx + 1)
201 elif idx in (16, 18, 20, 22):
202 self.annotate(name, self.to_current(value),
203 cnt_idx + idx, cnt_idx + idx + 1)
204 elif idx in (24, 26, 28, 30, 32, 34, 36, 38):
205 self.annotate(name, self.to_power(value),
206 cnt_idx + idx, cnt_idx + idx + 1)
207 else:
208 self.annotate(name, "%d" % name, value, cnt_idx + idx,
209 cnt_idx + idx + 1)
210 idx += 2
211
212 def vps(self, data):
213 # No good data available.
214 if (data != [0, 0]):
215 self.annotate("VPS", "%.2x%.2x" % (data[0], data[1]))
216
217 def ber(self, data):
218 # No good data available.
219 if (data != [0, 0]):
220 self.annotate("BER", str(data))
221
222 def wavelength_cr(self, data):
223 # No good data available.
224 if (data != [0, 0, 0, 0]):
225 self.annotate("WCR", str(data))
226
227 def fec_cr(self, data):
228 if (data != [0, 0, 0, 0]):
229 self.annotate("FEC", str(data))
230
231 def int_ctrl(self, data):
232 # No good data available. Also boring.
233 out = []
234 for d in data:
235 out.append("%.2x" % d)
236 self.annotate("Interrupt bits", ' '.join(out))
237
238 def ad_readout(self, data):
239 cnt_idx = self.cnt - len(data) + 1
240 idx = 0
241 while idx < 14:
242 if idx == 2:
243 # Skip over reserved field
244 idx += 2
245 value = (data[idx] << 8) | data[idx + 1]
246 name = AD_READOUTS.get(idx, "...")
247 if value != 0:
248 if idx == 0:
249 self.annotate(name, self.to_temp(value),
250 cnt_idx + idx, cnt_idx + idx + 1)
251 elif idx == 4:
252 self.annotate(name, self.to_current(value),
253 cnt_idx + idx, cnt_idx + idx + 1)
254 elif idx in (6, 8):
255 self.annotate(name, self.to_power(value),
256 cnt_idx + idx, cnt_idx + idx + 1)
257 else:
258 self.annotate(name, str(value), cnt_idx + idx,
259 cnt_idx + idx + 1)
260 idx += 2
261
262 def gcs(self, data):
263 allbits = (data[0] << 8) | data[1]
264 out = []
265 for b in range(13):
266 if allbits & 0x8000:
267 out.append(GCS_BITS[b])
268 allbits <<= 1
269 self.annotate("General Control/Status", ', '.join(out))
270
271 def page_select(self, data):
272 self.cur_highmem_page = data[0]
273
274 def ext_module_id(self, data):
275 out = ["Power level %d module" % ((data[0] >> 6) + 1)]
276 if data[0] & 0x20 == 0:
277 out.append("CDR")
278 if data[0] & 0x10 == 0:
279 out.append("TX ref clock input required")
280 if data[0] & 0x08 == 0:
281 self.have_clei = True
282 self.annotate("Extended id", ', '.join(out))
283
284 def connector(self, data):
285 if data[0] in CONNECTOR:
286 self.annotate("Connector", CONNECTOR[data[0]])
287
288 def transceiver(self, data):
289 out = []
290 for t in range(8):
291 if data[t] == 0:
292 continue
293 value = data[t]
294 for b in range(8):
295 if value & 0x80:
296 if len(TRANSCEIVER[t]) < b + 1:
297 out.append("(unknown)")
298 else:
299 out.append(TRANSCEIVER[t][b])
300 value <<= 1
301 self.annotate("Transceiver compliance", ', '.join(out))
302
303 def serial_encoding(self, data):
304 out = []
305 value = data[0]
306 for b in range(8):
307 if value & 0x80:
308 if len(SERIAL_ENCODING) < b + 1:
309 out.append("(unknown)")
310 else:
311 out.append(SERIAL_ENCODING[b])
312 value <<= 1
313 self.annotate("Serial encoding support", ', '.join(out))
314
315 def br_min(self, data):
316 # Increments represent 100Mb/s
317 rate = data[0] / 10.0
318 self.annotate("Minimum bit rate", "%.3f GB/s" % rate)
319
320 def br_max(self, data):
321 # Increments represent 100Mb/s
322 rate = data[0] / 10.0
323 self.annotate("Maximum bit rate", "%.3f GB/s" % rate)
324
325 def link_length_smf(self, data):
326 if data[0] == 0:
327 length = "(standard)"
328 elif data[0] == 255:
329 length = "> 254 km"
330 else:
331 length = "%d km" % data[0]
332 self.annotate("Link length (SMF)", length)
333
334 def link_length_e50(self, data):
335 if data[0] == 0:
336 length = "(standard)"
337 elif data[0] == 255:
338 length = "> 508 m"
339 else:
340 length = "%d m" % (data[0] * 2)
341 self.annotate("Link length (extended, 50μm MMF)", length)
342
343 def link_length_50um(self, data):
344 if data[0] == 0:
345 length = "(standard)"
346 elif data[0] == 255:
347 length = "> 254 m"
348 else:
349 length = "%d m" % data[0]
350 self.annotate("Link length (50μm MMF)", length)
351
352 def link_length_625um(self, data):
353 if data[0] == 0:
354 length = "(standard)"
355 elif data[0] == 255:
356 length = "> 254 m"
357 else:
358 length = "%d m" % (data[0])
359 self.annotate("Link length (62.5μm MMF)", length)
360
361 def link_length_copper(self, data):
362 if data[0] == 0:
363 length = "(unknown)"
364 elif data[0] == 255:
365 length = "> 254 m"
366 else:
367 length = "%d m" % (data[0] * 2)
368 self.annotate("Link length (copper)", length)
369
370 def device_tech(self, data):
371 out = []
372 xmit = data[0] >> 4
373 if xmit <= len(XMIT_TECH) - 1:
374 out.append("%s transmitter" % XMIT_TECH[xmit])
375 dev = data[0] & 0x0f
376 for b in range(4):
377 out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01])
378 self.annotate("Device technology", ', '.join(out))
379
380 def vendor(self, data):
381 name = bytes(data).strip().decode('ascii').strip('\x00')
382 if name:
383 self.annotate("Vendor", name)
384
385 def cdr(self, data):
386 out = []
387 value = data[0]
388 for b in range(8):
389 if value & 0x80:
390 out.append(CDR[b])
391 value <<= 1
392 self.annotate("CDR support", ', '.join(out))
393
394 def vendor_oui(self, data):
395 if data != [0, 0, 0]:
396 self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data))
397
398 def vendor_pn(self, data):
399 name = bytes(data).strip().decode('ascii').strip('\x00')
400 if name:
401 self.annotate("Vendor part number", name)
402
403 def vendor_rev(self, data):
404 name = bytes(data).strip().decode('ascii').strip('\x00')
405 if name:
406 self.annotate("Vendor revision", name)
407
408 def wavelength(self, data):
409 value = (data[0] << 8) | data[1]
410 self.annotate("Wavelength", self.to_wavelength(value))
411
412 def wavelength_tolerance(self, data):
413 value = (data[0] << 8) | data[1]
414 self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value))
415
416 def max_case_temp(self, data):
417 self.annotate("Maximum case temperature", "%d C" % data[0])
418
419 def power_supply(self, data):
420 out = []
421 self.annotate("Max power dissipation",
422 "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3)
423 self.annotate("Max power dissipation (powered down)",
424 "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2)
425 value = (data[2] >> 4) * 0.050
426 self.annotate("Max current required (5V supply)",
427 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
428 value = (data[2] & 0x0f) * 0.100
429 self.annotate("Max current required (3.3V supply)",
430 "%.3f A" % value, self.cnt - 1, self.cnt - 1)
431 value = (data[3] >> 4) * 0.100
432 self.annotate("Max current required (1.8V supply)",
433 "%.3f A" % value, self.cnt, self.cnt)
434 value = (data[3] & 0x0f) * 0.050
435 self.annotate("Max current required (-5.2V supply)",
436 "%.3f A" % value, self.cnt, self.cnt)
437
438 def vendor_sn(self, data):
439 name = bytes(data).strip().decode('ascii').strip('\x00')
440 if name:
441 self.annotate("Vendor serial number", name)
442
443 def manuf_date(self, data):
444 y = int(bytes(data[0:2])) + 2000
445 m = int(bytes(data[2:4]))
446 d = int(bytes(data[4:6]))
447 mnf = "%.4d-%.2d-%.2d" % (y, m, d)
448 lot = bytes(data[6:]).strip().decode('ascii').strip('\x00')
449 if lot:
450 mnf += " lot " + lot
451 self.annotate("Manufacturing date", mnf)
452
453 def diag_mon(self, data):
454 out = []
455 if data[0] & 0x10:
456 out.append("BER support")
457 else:
458 out.append("no BER support")
459 if data[0] & 0x08:
460 out.append("average power measurement")
461 else:
462 out.append("OMA power measurement")
463 self.annotate("Diagnostic monitoring", ', '.join(out))
464
465 def enhanced_opts(self, data):
466 out = []
467 value = data[0]
468 for b in range(8):
469 if value & 0x80:
470 out.append(ENHANCED_OPTS[b])
471 value <<= 1
472 self.annotate("Enhanced option support", ', '.join(out))
473
474 def aux_mon(self, data):
475 aux = AUX_TYPES[data[0] >> 4]
476 self.annotate("AUX1 monitoring", aux)
477 aux = AUX_TYPES[data[0] & 0x0f]
478 self.annotate("AUX2 monitoring", aux)