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