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