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