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