]>
Commit | Line | Data |
---|---|---|
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 | ||
20 | # XFP protocol decoder | |
21 | ||
22 | import sigrokdecode as srd | |
23 | import os | |
24 | ||
25 | MODULE_ID = { | |
26 | 0x01: 'GBIC', | |
27 | 0x02: 'Integrated module/connector', | |
28 | 0x03: 'SFP', | |
29 | 0x04: '300-pin XBI', | |
30 | 0x05: 'XENPAK', | |
31 | 0x06: 'XFP', | |
32 | 0x07: 'XFF', | |
33 | 0x08: 'XFP-E', | |
34 | 0x09: 'XPAK', | |
35 | 0x0a: 'X2', | |
36 | } | |
37 | ||
38 | ALARM_THRESHOLDS = { | |
39 | 0: "Temp high alarm", | |
40 | 2: "Temp low alarm", | |
41 | 4: "Temp high warning", | |
42 | 6: "Temp low warning", | |
43 | 16: "Bias high alarm", | |
44 | 18: "Bias low alarm", | |
45 | 20: "Bias high warning", | |
46 | 22: "Bias low warning", | |
47 | 24: "TX power high alarm", | |
48 | 26: "TX power low alarm", | |
49 | 28: "TX power high warning", | |
50 | 30: "TX power low warning", | |
51 | 32: "RX power high alarm", | |
52 | 34: "RX power low alarm", | |
53 | 36: "RX power high warning", | |
54 | 38: "RX power low warning", | |
55 | 40: "AUX 1 high alarm", | |
56 | 42: "AUX 1 low alarm", | |
57 | 44: "AUX 1 high warning", | |
58 | 46: "AUX 1 low warning", | |
59 | 48: "AUX 2 high alarm", | |
60 | 50: "AUX 2 low alarm", | |
61 | 52: "AUX 2 high warning", | |
62 | 54: "AUX 2 low warning", | |
63 | } | |
64 | ||
65 | AD_READOUTS = { | |
66 | 0: "Module temperature", | |
67 | 4: "TX bias current", | |
68 | 6: "Measured TX output power", | |
69 | 8: "Measured RX input power", | |
70 | 10: "AUX 1 measurement", | |
71 | 12: "AUX 2 measurement", | |
72 | } | |
73 | ||
74 | GCS_BITS = [ | |
75 | "TX disable", | |
76 | "Soft TX disable", | |
77 | "MOD_NR", | |
78 | "P_Down", | |
79 | "Soft P_Down", | |
80 | "Interrupt", | |
81 | "RX_LOS", | |
82 | "Data_Not_Ready", | |
83 | "TX_NR", | |
84 | "TX_Fault", | |
85 | "TX_CDR not locked", | |
86 | "RX_NR", | |
87 | "RX_CDR not locked", | |
88 | ] | |
89 | ||
90 | CONNECTOR = { | |
91 | 0x01: "SC", | |
92 | 0x02: "Fibre Channel style 1 copper", | |
93 | 0x03: "Fibre Channel style 2 copper", | |
94 | 0x04: "BNC/TNC", | |
95 | 0x05: "Fibre Channel coax", | |
96 | 0x06: "FiberJack", | |
97 | 0x07: "LC", | |
98 | 0x08: "MT-RJ", | |
99 | 0x09: "MU", | |
100 | 0x0a: "SG", | |
101 | 0x0b: "Optical pigtail", | |
102 | 0x20: "HSSDC II", | |
103 | 0x21: "Copper pigtail", | |
104 | } | |
105 | ||
106 | TRANSCEIVER = [ | |
107 | # 10GB Ethernet | |
108 | ["10GBASE-SR", "10GBASE-LR", "10GBASE-ER", "10GBASE-LRM", "10GBASE-SW", | |
109 | "10GBASE-LW", "10GBASE-EW"], | |
110 | # 10GB Fibre Channel | |
111 | ["1200-MX-SN-I", "1200-SM-LL-L", "Extended Reach 1550 nm", | |
112 | "Intermediate reach 1300 nm FP"], | |
113 | # 10GB Copper | |
114 | [], | |
115 | # 10GB low speed | |
116 | ["1000BASE-SX / 1xFC MMF", "1000BASE-LX / 1xFC SMF", "2xFC MMF", | |
117 | "2xFC SMF", "OC48-SR", "OC48-IR", "OC48-LR"], | |
118 | # 10GB SONET/SDH interconnect | |
119 | ["I-64.1r", "I-64.1", "I-64.2r", "I-64.2", "I-64.3", "I-64.5"], | |
120 | # 10GB SONET/SDH short haul | |
121 | ["S-64.1", "S-64.2a", "S-64.2b", "S-64.3a", "S-64.3b", "S-64.5a", "S-64.5b"], | |
122 | # 10GB SONET/SDH long haul | |
123 | ["L-64.1", "L-64.2a", "L-64.2b", "L-64.2c", "L-64.3", "G.959.1 P1L1-2D2"], | |
124 | # 10GB SONET/SDH very long haul | |
125 | ["V-64.2a", "V-64.2b", "V-64.3"], | |
126 | ] | |
127 | ||
128 | SERIAL_ENCODING = [ | |
129 | "64B/66B", | |
130 | "8B/10B", | |
131 | "SONET scrambled", | |
132 | "NRZ", | |
133 | "RZ", | |
134 | ] | |
135 | ||
136 | XMIT_TECH = [ | |
137 | "850 nm VCSEL", | |
138 | "1310 nm VCSEL", | |
139 | "1550 nm VCSEL", | |
140 | "1310 nm FP", | |
141 | "1310 nm DFB", | |
142 | "1550 nm DFB", | |
143 | "1310 nm EML" | |
144 | "1550 nm EML" | |
145 | "copper", | |
146 | ] | |
147 | ||
148 | CDR = [ | |
149 | "9.95Gb/s", | |
150 | "10.3Gb/s", | |
151 | "10.5Gb/s", | |
152 | "10.7Gb/s", | |
153 | "11.1Gb/s", | |
154 | "(unknown)", | |
155 | "lineside loopback mode", | |
156 | "XFI loopback mode", | |
157 | ] | |
158 | ||
159 | DEVICE_TECH = [ | |
160 | ["no wavelength control", "sctive wavelength control"], | |
161 | ["uncooled transmitter device", "cooled transmitter"], | |
162 | ["PIN detector", "APD detector"], | |
163 | ["transmitter not tunable", "transmitter tunable"], | |
164 | ] | |
165 | ||
166 | ENHANCED_OPTS = [ | |
167 | "VPS", | |
168 | "soft TX_DISABLE", | |
169 | "soft P_Down", | |
170 | "VPS LV regulator mode", | |
171 | "VPS bypassed regulator mode", | |
172 | "active FEC control", | |
173 | "wavelength tunability", | |
174 | "CMU", | |
175 | ] | |
176 | ||
177 | AUX_TYPES = [ | |
178 | "not implemented", | |
179 | "APD bias voltage", | |
180 | "(unknown)", | |
181 | "TEC current", | |
182 | "laser temperature", | |
183 | "laser wavelength", | |
184 | "5V supply voltage", | |
185 | "3.3V supply voltage", | |
186 | "1.8V supply voltage", | |
187 | "-5.2V supply voltage", | |
188 | "5V supply current", | |
189 | "(unknown)", | |
190 | "(unknown)", | |
191 | "3.3V supply current", | |
192 | "1.8V supply current", | |
193 | "-5.2V supply current", | |
194 | ] | |
195 | ||
196 | class Decoder(srd.Decoder): | |
197 | api_version = 1 | |
198 | id = 'xfp' | |
199 | name = 'XFP' | |
200 | longname = '10 Gigabit Small Form Factor Pluggable Module (XFP)' | |
201 | desc = 'Data structure describing display device capabilities.' | |
202 | license = 'gplv3+' | |
203 | inputs = ['i2c'] | |
204 | outputs = ['xfp'] | |
205 | probes = [] | |
206 | optional_probes = [] | |
207 | options = {} | |
208 | annotations = [ | |
209 | ['XFP field names and values', 'XFP structure field names and values'], | |
210 | ['XFP fields', 'XFP structure fields'], | |
211 | ] | |
212 | ||
213 | def __init__(self, **kwargs): | |
214 | # Received data items, used as an index into samplenum/data | |
215 | self.cnt = -1 | |
216 | # Start/end sample numbers per data item | |
217 | self.sn = [] | |
218 | # Multi-byte structure buffer | |
219 | self.buf = [] | |
220 | # Filled in by address 0x7f in low memory | |
221 | self.cur_highmem_page = 0 | |
222 | # Filled in by extended ID value in table 2 | |
223 | self.have_clei = False | |
224 | # Handlers for each field in the structure, keyed by the end | |
225 | # index of that field. Each handler is fed all unhandled bytes | |
226 | # up until that point, so mark unused space with the dummy | |
227 | # handler self.ignore(). | |
228 | self.MAP_LOWER_MEMORY = { | |
229 | 0: self.module_id, | |
230 | 1: self.signal_cc, | |
231 | 57: self.alarm_warnings, | |
232 | 59: self.vps, | |
233 | 69: self.ignore, | |
234 | 71: self.ber, | |
235 | 75: self.wavelength_cr, | |
236 | 79: self.fec_cr, | |
237 | 95: self.int_ctrl, | |
238 | 109: self.ad_readout, | |
239 | 111: self.gcs, | |
240 | 117: self.ignore, | |
241 | 118: self.ignore, | |
242 | 122: self.ignore, | |
243 | 126: self.ignore, | |
244 | 127: self.page_select, | |
245 | } | |
246 | self.MAP_HIGH_TABLE_1 = { | |
247 | 128: self.module_id, | |
248 | 129: self.ext_module_id, | |
249 | 130: self.connector, | |
250 | 138: self.transceiver, | |
251 | 139: self.serial_encoding, | |
252 | 140: self.br_min, | |
253 | 141: self.br_max, | |
254 | 142: self.link_length_smf, | |
255 | 143: self.link_length_e50, | |
256 | 144: self.link_length_50um, | |
257 | 145: self.link_length_625um, | |
258 | 146: self.link_length_copper, | |
259 | 147: self.device_tech, | |
260 | 163: self.vendor, | |
261 | 164: self.cdr, | |
262 | 167: self.vendor_oui, | |
263 | 183: self.vendor_pn, | |
264 | 185: self.vendor_rev, | |
265 | 187: self.wavelength, | |
266 | 189: self.wavelength_tolerance, | |
267 | 190: self.max_case_temp, | |
268 | 191: self.ignore, | |
269 | 195: self.power_supply, | |
270 | 211: self.vendor_sn, | |
271 | 219: self.manuf_date, | |
272 | 220: self.diag_mon, | |
273 | 221: self.enhanced_opts, | |
274 | 222: self.aux_mon, | |
275 | 223: self.ignore, | |
276 | 255: self.maybe_ascii, | |
277 | } | |
278 | ||
279 | def start(self, metadata): | |
280 | self.out_ann = self.add(srd.OUTPUT_ANN, 'xfp') | |
281 | ||
282 | def decode(self, ss, es, data): | |
283 | cmd, data = data | |
284 | ||
285 | # We only care about actual data bytes that are read (for now). | |
286 | if cmd != 'DATA READ': | |
287 | return | |
288 | ||
289 | self.cnt += 1 | |
290 | self.sn.append([ss, es]) | |
291 | ||
292 | self.buf.append(data) | |
293 | if self.cnt < 0x80: | |
294 | if self.cnt in self.MAP_LOWER_MEMORY: | |
295 | self.MAP_LOWER_MEMORY[self.cnt](self.buf) | |
296 | self.buf.clear() | |
297 | elif self.cnt < 0x0100 and self.cur_highmem_page == 0x01: | |
298 | # Serial ID memory map | |
299 | if self.cnt in self.MAP_HIGH_TABLE_1: | |
300 | self.MAP_HIGH_TABLE_1[self.cnt](self.buf) | |
301 | self.buf.clear() | |
302 | ||
303 | # Annotation helper | |
304 | def annotate(self, key, value, start_cnt=None, end_cnt=None): | |
305 | if start_cnt is None: | |
306 | start_cnt = self.cnt - len(self.buf) + 1 | |
307 | if end_cnt is None: | |
308 | end_cnt = self.cnt | |
309 | self.put(self.sn[start_cnt][0], self.sn[end_cnt][1], | |
310 | self.out_ann, [0, [key + ": " + value]]) | |
311 | self.put(self.sn[start_cnt][0], self.sn[end_cnt][1], | |
312 | self.out_ann, [1, [value]]) | |
313 | ||
314 | # Placeholder handler, needed to advance the buffer past unused or | |
315 | # reserved space in the structures. | |
316 | def ignore(self, data): | |
317 | pass | |
318 | ||
319 | # Show as ASCII if possible | |
320 | def maybe_ascii(self, data): | |
321 | for i in range(len(data)): | |
322 | if data[i] >= 0x20 and data[i] < 0x7f: | |
323 | cnt = self.cnt - len(data) + 1 | |
324 | self.annotate("Vendor ID", chr(data[i]), cnt, cnt) | |
325 | ||
326 | # Convert 16-bit two's complement values, with each increment | |
327 | # representing 1/256C, to degrees Celcius. | |
328 | def to_temp(self, value): | |
329 | if value & 0x8000: | |
330 | value = -((value ^ 0xffff) + 1) | |
331 | temp = value / 256.0 | |
332 | return "%.1f C" % temp | |
333 | ||
334 | # TX bias current in uA. Each increment represents 0.2uA | |
335 | def to_current(self, value): | |
336 | current = value / 500000.0 | |
337 | return "%.1f mA" % current | |
338 | ||
339 | # Power in mW, with each increment representing 0.1uW | |
340 | def to_power(self, value): | |
341 | power = value / 10000.0 | |
342 | return "%.2f mW" % power | |
343 | ||
344 | # Wavelength in increments of 0.05nm | |
345 | def to_wavelength(self, value): | |
346 | wl = value / 20 | |
347 | return "%d nm" % wl | |
348 | ||
349 | # Wavelength in increments of 0.005nm | |
350 | def to_wavelength_tolerance(self, value): | |
351 | wl = value / 200.0 | |
352 | return "%.1f nm" % wl | |
353 | ||
354 | def module_id(self, data): | |
355 | self.annotate("Module identifier", MODULE_ID.get(data[0], "Unknown")) | |
356 | ||
357 | def signal_cc(self, data): | |
358 | # No good data available. | |
359 | if (data[0] != 0x00): | |
360 | self.annotate("Signal Conditioner Control", "%.2x" % data[0]) | |
361 | ||
362 | def alarm_warnings(self, data): | |
363 | cnt_idx = self.cnt - len(data) | |
364 | idx = 0 | |
365 | while idx < 56: | |
366 | if idx == 8: | |
367 | # Skip over reserved A/D flag thresholds | |
368 | idx += 8 | |
369 | value = (data[idx] << 8) | data[idx + 1] | |
370 | if value != 0: | |
371 | name = ALARM_THRESHOLDS.get(idx, "...") | |
372 | if idx in (0, 2, 4, 6): | |
373 | self.annotate(name, self.to_temp(value), | |
374 | cnt_idx + idx, cnt_idx + idx + 1) | |
375 | elif idx in (16, 18, 20, 22): | |
376 | self.annotate(name, self.to_current(value), | |
377 | cnt_idx + idx, cnt_idx + idx + 1) | |
378 | elif idx in (24, 26, 28, 30, 32, 34, 36, 38): | |
379 | self.annotate(name, self.to_power(value), | |
380 | cnt_idx + idx, cnt_idx + idx + 1) | |
381 | else: | |
382 | self.annotate(name, "%d" % name, value, cnt_idx + idx, | |
383 | cnt_idx + idx + 1) | |
384 | idx += 2 | |
385 | ||
386 | def vps(self, data): | |
387 | # No good data available. | |
388 | if (data != [0, 0]): | |
389 | self.annotate("VPS", "%.2x%.2x" % (data[0], data[1])) | |
390 | ||
391 | def ber(self, data): | |
392 | # No good data available. | |
393 | if (data != [0, 0]): | |
394 | self.annotate("BER", str(data)) | |
395 | ||
396 | def wavelength_cr(self, data): | |
397 | # No good data available. | |
398 | if (data != [0, 0, 0, 0]): | |
399 | self.annotate("WCR", str(data)) | |
400 | ||
401 | def fec_cr(self, data): | |
402 | if (data != [0, 0, 0, 0]): | |
403 | self.annotate("FEC", str(data)) | |
404 | ||
405 | def int_ctrl(self, data): | |
406 | # No good data available. Also boring. | |
407 | out = [] | |
408 | for d in data: | |
409 | out.append("%.2x" % d) | |
410 | self.annotate("Interrupt bits", ' '.join(out)) | |
411 | ||
412 | def ad_readout(self, data): | |
413 | cnt_idx = self.cnt - len(data) + 1 | |
414 | idx = 0 | |
415 | while idx < 14: | |
416 | if idx == 2: | |
417 | # Skip over reserved field | |
418 | idx += 2 | |
419 | value = (data[idx] << 8) | data[idx + 1] | |
420 | name = AD_READOUTS.get(idx, "...") | |
421 | if value != 0: | |
422 | if idx == 0: | |
423 | self.annotate(name, self.to_temp(value), | |
424 | cnt_idx + idx, cnt_idx + idx + 1) | |
425 | elif idx == 4: | |
426 | self.annotate(name, self.to_current(value), | |
427 | cnt_idx + idx, cnt_idx + idx + 1) | |
428 | elif idx in (6, 8): | |
429 | self.annotate(name, self.to_power(value), | |
430 | cnt_idx + idx, cnt_idx + idx + 1) | |
431 | else: | |
432 | self.annotate(name, str(value), cnt_idx + idx, | |
433 | cnt_idx + idx + 1) | |
434 | idx += 2 | |
435 | ||
436 | def gcs(self, data): | |
437 | allbits = (data[0] << 8) | data[1] | |
438 | out = [] | |
439 | for b in range(13): | |
440 | if allbits & 0x8000: | |
441 | out.append(GCS_BITS[b]) | |
442 | allbits <<= 1 | |
443 | self.annotate("General Control/Status", ', '.join(out)) | |
444 | ||
445 | def page_select(self, data): | |
446 | self.cur_highmem_page = data[0] | |
447 | ||
448 | def ext_module_id(self, data): | |
449 | out = ["Power level %d module" % ((data[0] >> 6) + 1)] | |
450 | if data[0] & 0x20 == 0: | |
451 | out.append("CDR") | |
452 | if data[0] & 0x10 == 0: | |
453 | out.append("TX ref clock input required") | |
454 | if data[0] & 0x08 == 0: | |
455 | self.have_clei = True | |
456 | self.annotate("Extended id", ', '.join(out)) | |
457 | ||
458 | def connector(self, data): | |
459 | if data[0] in CONNECTOR: | |
460 | self.annotate("Connector", CONNECTOR[data[0]]) | |
461 | ||
462 | def transceiver(self, data): | |
463 | out = [] | |
464 | for t in range(8): | |
465 | if data[t] == 0: | |
466 | continue | |
467 | value = data[t] | |
468 | for b in range(8): | |
469 | if value & 0x80: | |
470 | if len(TRANSCEIVER[t]) < b + 1: | |
471 | out.append("(unknown)") | |
472 | else: | |
473 | out.append(TRANSCEIVER[t][b]) | |
474 | value <<= 1 | |
475 | self.annotate("Transceiver compliance", ', '.join(out)) | |
476 | ||
477 | def serial_encoding(self, data): | |
478 | out = [] | |
479 | value = data[0] | |
480 | for b in range(8): | |
481 | if value & 0x80: | |
482 | if len(SERIAL_ENCODING) < b + 1: | |
483 | out.append("(unknown)") | |
484 | else: | |
485 | out.append(SERIAL_ENCODING[b]) | |
486 | value <<= 1 | |
487 | self.annotate("Serial encoding support", ', '.join(out)) | |
488 | ||
489 | def br_min(self, data): | |
490 | # Increments represent 100Mb/s | |
491 | rate = data[0] / 10.0 | |
492 | self.annotate("Minimum bit rate", "%.3f GB/s" % rate) | |
493 | ||
494 | def br_max(self, data): | |
495 | # Increments represent 100Mb/s | |
496 | rate = data[0] / 10.0 | |
497 | self.annotate("Maximum bit rate", "%.3f GB/s" % rate) | |
498 | ||
499 | def link_length_smf(self, data): | |
500 | if data[0] == 0: | |
501 | length = "(standard)" | |
502 | elif data[0] == 255: | |
503 | length = "> 254 km" | |
504 | else: | |
505 | length = "%d km" % data[0] | |
506 | self.annotate("Link length (SMF)", length) | |
507 | ||
508 | def link_length_e50(self, data): | |
509 | if data[0] == 0: | |
510 | length = "(standard)" | |
511 | elif data[0] == 255: | |
512 | length = "> 508 m" | |
513 | else: | |
514 | length = "%d m" % (data[0] * 2) | |
515 | self.annotate("Link length (extended, 50μm MMF)", length) | |
516 | ||
517 | def link_length_50um(self, data): | |
518 | if data[0] == 0: | |
519 | length = "(standard)" | |
520 | elif data[0] == 255: | |
521 | length = "> 254 m" | |
522 | else: | |
523 | length = "%d m" % data[0] | |
524 | self.annotate("Link length (50μm MMF)", length) | |
525 | ||
526 | def link_length_625um(self, data): | |
527 | if data[0] == 0: | |
528 | length = "(standard)" | |
529 | elif data[0] == 255: | |
530 | length = "> 254 m" | |
531 | else: | |
532 | length = "%d m" % (data[0]) | |
533 | self.annotate("Link length (62.5μm MMF)", length) | |
534 | ||
535 | def link_length_copper(self, data): | |
536 | if data[0] == 0: | |
537 | length = "(unknown)" | |
538 | elif data[0] == 255: | |
539 | length = "> 254 m" | |
540 | else: | |
541 | length = "%d m" % (data[0] * 2) | |
542 | self.annotate("Link length (copper)", length) | |
543 | ||
544 | def device_tech(self, data): | |
545 | out = [] | |
546 | xmit = data[0] >> 4 | |
547 | if xmit <= len(XMIT_TECH) - 1: | |
548 | out.append("%s transmitter" % XMIT_TECH[xmit]) | |
549 | dev = data[0] & 0x0f | |
550 | for b in range(4): | |
551 | out.append(DEVICE_TECH[b][(dev >> (3 - b)) & 0x01]) | |
552 | self.annotate("Device technology", ', '.join(out)) | |
553 | ||
554 | def vendor(self, data): | |
555 | name = bytes(data).strip().decode('ascii').strip('\x00') | |
556 | if name: | |
557 | self.annotate("Vendor", name) | |
558 | ||
559 | def cdr(self, data): | |
560 | out = [] | |
561 | value = data[0] | |
562 | for b in range(8): | |
563 | if value & 0x80: | |
564 | out.append(CDR[b]) | |
565 | value <<= 1 | |
566 | self.annotate("CDR support", ', '.join(out)) | |
567 | ||
568 | def vendor_oui(self, data): | |
569 | if data != [0, 0, 0]: | |
570 | self.annotate("Vendor OUI", "%.2X-%.2X-%.2X" % tuple(data)) | |
571 | ||
572 | def vendor_pn(self, data): | |
573 | name = bytes(data).strip().decode('ascii').strip('\x00') | |
574 | if name: | |
575 | self.annotate("Vendor part number", name) | |
576 | ||
577 | def vendor_rev(self, data): | |
578 | name = bytes(data).strip().decode('ascii').strip('\x00') | |
579 | if name: | |
580 | self.annotate("Vendor revision", name) | |
581 | ||
582 | def wavelength(self, data): | |
583 | value = (data[0] << 8) | data[1] | |
584 | self.annotate("Wavelength", self.to_wavelength(value)) | |
585 | ||
586 | def wavelength_tolerance(self, data): | |
587 | value = (data[0] << 8) | data[1] | |
588 | self.annotate("Wavelength tolerance", self.to_wavelength_tolerance(value)) | |
589 | ||
590 | def max_case_temp(self, data): | |
591 | self.annotate("Maximum case temperature", "%d C" % data[0]) | |
592 | ||
593 | def power_supply(self, data): | |
594 | out = [] | |
595 | self.annotate("Max power dissipation", | |
596 | "%.3f W" % (data[0] * 0.02), self.cnt - 3, self.cnt - 3) | |
597 | self.annotate("Max power dissipation (powered down)", | |
598 | "%.3f W" % (data[1] * 0.01), self.cnt - 2, self.cnt - 2) | |
599 | value = (data[2] >> 4) * 0.050 | |
600 | self.annotate("Max current required (5V supply)", | |
601 | "%.3f A" % value, self.cnt - 1, self.cnt - 1) | |
602 | value = (data[2] & 0x0f) * 0.100 | |
603 | self.annotate("Max current required (3.3V supply)", | |
604 | "%.3f A" % value, self.cnt - 1, self.cnt - 1) | |
605 | value = (data[3] >> 4) * 0.100 | |
606 | self.annotate("Max current required (1.8V supply)", | |
607 | "%.3f A" % value, self.cnt, self.cnt) | |
608 | value = (data[3] & 0x0f) * 0.050 | |
609 | self.annotate("Max current required (-5.2V supply)", | |
610 | "%.3f A" % value, self.cnt, self.cnt) | |
611 | ||
612 | def vendor_sn(self, data): | |
613 | name = bytes(data).strip().decode('ascii').strip('\x00') | |
614 | if name: | |
615 | self.annotate("Vendor serial number", name) | |
616 | ||
617 | def manuf_date(self, data): | |
618 | y = int(bytes(data[0:2])) + 2000 | |
619 | m = int(bytes(data[2:4])) | |
620 | d = int(bytes(data[4:6])) | |
621 | mnf = "%.4d-%.2d-%.2d" % (y, m, d) | |
622 | lot = bytes(data[6:]).strip().decode('ascii').strip('\x00') | |
623 | if lot: | |
624 | mnf += " lot " + lot | |
625 | self.annotate("Manufacturing date", mnf) | |
626 | ||
627 | def diag_mon(self, data): | |
628 | out = [] | |
629 | if data[0] & 0x10: | |
630 | out.append("BER support") | |
631 | else: | |
632 | out.append("no BER support") | |
633 | if data[0] & 0x08: | |
634 | out.append("average power measurement") | |
635 | else: | |
636 | out.append("OMA power measurement") | |
637 | self.annotate("Diagnostic monitoring", ', '.join(out)) | |
638 | ||
639 | def enhanced_opts(self, data): | |
640 | out = [] | |
641 | value = data[0] | |
642 | for b in range(8): | |
643 | if value & 0x80: | |
644 | out.append(ENHANCED_OPTS[b]) | |
645 | value <<= 1 | |
646 | self.annotate("Enhanced option support", ', '.join(out)) | |
647 | ||
648 | def aux_mon(self, data): | |
649 | aux = AUX_TYPES[data[0] >> 4] | |
650 | self.annotate("AUX1 monitoring", aux) | |
651 | aux = AUX_TYPES[data[0] & 0x0f] | |
652 | self.annotate("AUX2 monitoring", aux) | |
653 |