]> sigrok.org Git - libsigrokdecode.git/blob - decoders/xfp/pd.py
Move common/ directory into decoders/.
[libsigrokdecode.git] / decoders / xfp / pd.py
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 import sigrokdecode as srd
21 from common.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)
24
25 class Decoder(srd.Decoder):
26     api_version = 2
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']
34     annotations = (
35         ('fieldnames-and-values', 'XFP structure field names and values'),
36         ('fields', 'XFP structure fields'),
37     )
38
39     def __init__(self):
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
105     def start(self):
106         self.out_ann = self.register(srd.OUTPUT_ANN)
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
153     # representing 1/256C, to degrees Celsius.
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)