]>
Commit | Line | Data |
---|---|---|
686f0c36 PA |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2015 Petteri Aimonen <jpa@sigrok.mail.kapsi.fi> | |
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 2 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, write to the Free Software | |
18 | ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | ## | |
20 | ||
21 | import sigrokdecode as srd | |
22 | import subprocess | |
23 | import re | |
24 | ||
1c023fab PA |
25 | # See ETMv3 Signal Protocol table 7-11: 'Encoding of Exception[8:0]'. |
26 | exc_names = [ | |
27 | 'No exception', 'IRQ1', 'IRQ2', 'IRQ3', 'IRQ4', 'IRQ5', 'IRQ6', 'IRQ7', | |
28 | 'IRQ0', 'UsageFault', 'NMI', 'SVC', 'DebugMon', 'MemManage', 'PendSV', | |
29 | 'SysTick', 'Reserved', 'Reset', 'BusFault', 'Reserved', 'Reserved' | |
30 | ] | |
31 | ||
32 | for i in range(8, 496): | |
33 | exc_names.append('IRQ%d' % i) | |
34 | ||
686f0c36 PA |
35 | def parse_varint(bytes): |
36 | '''Parse an integer where the top bit is the continuation bit. | |
37 | Returns value and number of parsed bytes.''' | |
38 | v = 0 | |
39 | for i, b in enumerate(bytes): | |
40 | v |= (b & 0x7F) << (i * 7) | |
41 | if b & 0x80 == 0: | |
42 | return v, i+1 | |
43 | return v, len(bytes) | |
44 | ||
45 | def parse_uint(bytes): | |
46 | '''Parse little-endian integer.''' | |
47 | v = 0 | |
48 | for i, b in enumerate(bytes): | |
49 | v |= b << (i * 8) | |
50 | return v | |
51 | ||
52 | def parse_exc_info(bytes): | |
53 | '''Parse exception information bytes from a branch packet.''' | |
54 | if len(bytes) < 1: | |
55 | return None | |
56 | ||
57 | excv, exclen = parse_varint(bytes) | |
58 | if bytes[exclen - 1] & 0x80 != 0x00: | |
59 | return None # Exception info not complete. | |
60 | ||
61 | if exclen == 2 and excv & (1 << 13): | |
62 | # Exception byte 1 was skipped, fix up the decoding. | |
63 | excv = (excv & 0x7F) | ((excv & 0x3F80) << 7) | |
64 | ||
65 | ns = excv & 1 | |
66 | exc = ((excv >> 1) & 0x0F) | ((excv >> 7) & 0x1F0) | |
67 | cancel = (excv >> 5) & 1 | |
68 | altisa = (excv >> 6) & 1 | |
69 | hyp = (excv >> 12) & 1 | |
70 | resume = (excv >> 14) & 0x0F | |
71 | return (ns, exc, cancel, altisa, hyp, resume) | |
72 | ||
73 | def parse_branch_addr(bytes, ref_addr, cpu_state, branch_enc): | |
74 | '''Parse encoded branch address. | |
75 | Returns addr, addrlen, cpu_state, exc_info. | |
76 | Returns None if packet is not yet complete''' | |
77 | ||
78 | addr, addrlen = parse_varint(bytes) | |
79 | ||
80 | if bytes[addrlen-1] & 0x80 != 0x00: | |
81 | return None # Branch address not complete. | |
82 | ||
1c023fab PA |
83 | addr_bits = 7 * addrlen |
84 | ||
686f0c36 PA |
85 | have_exc_info = False |
86 | if branch_enc == 'original': | |
87 | if addrlen == 5 and bytes[4] & 0x40: | |
88 | have_exc_info = True | |
89 | elif branch_enc == 'alternative': | |
1c023fab PA |
90 | addr_bits -= 1 # Top bit of address indicates exc_info. |
91 | if addrlen >= 2 and addr & (1 << addr_bits): | |
686f0c36 | 92 | have_exc_info = True |
1c023fab | 93 | addr &= ~(1 << addr_bits) |
686f0c36 PA |
94 | |
95 | exc_info = None | |
96 | if have_exc_info: | |
97 | exc_info = parse_exc_info(bytes[addrlen:]) | |
98 | if exc_info is None: | |
99 | return None # Exception info not complete. | |
100 | ||
101 | if addrlen == 5: | |
102 | # Possible change in CPU state. | |
103 | if bytes[4] & 0xB8 == 0x08: | |
104 | cpu_state = 'arm' | |
105 | elif bytes[4] & 0xB0 == 0x10: | |
106 | cpu_state = 'thumb' | |
107 | elif bytes[4] & 0xA0 == 0x20: | |
108 | cpu_state = 'jazelle' | |
109 | else: | |
110 | raise NotImplementedError('Unhandled branch byte 4: 0x%02x' % bytes[4]) | |
111 | ||
112 | # Shift the address according to current CPU state. | |
113 | if cpu_state == 'arm': | |
114 | addr = (addr & 0xFFFFFFFE) << 1 | |
1c023fab | 115 | addr_bits += 1 |
686f0c36 PA |
116 | elif cpu_state == 'thumb': |
117 | addr = addr & 0xFFFFFFFE | |
118 | elif cpu_state == 'jazelle': | |
119 | addr = (addr & 0xFFFFFFFFE) >> 1 | |
1c023fab | 120 | addr_bits -= 1 |
686f0c36 PA |
121 | else: |
122 | raise NotImplementedError('Unhandled state: ' + cpu_state) | |
123 | ||
124 | # If the address wasn't full, fill in with the previous address. | |
125 | if addrlen < 5: | |
1c023fab | 126 | addr |= ref_addr & (0xFFFFFFFF << addr_bits) |
686f0c36 PA |
127 | |
128 | return addr, addrlen, cpu_state, exc_info | |
129 | ||
130 | class Decoder(srd.Decoder): | |
131 | api_version = 2 | |
132 | id = 'arm_etmv3' | |
133 | name = 'ARM ETMv3' | |
134 | longname = 'ARM Embedded Trace Macroblock' | |
135 | desc = 'Decode ETM instruction trace packets.' | |
136 | license = 'gplv2+' | |
137 | inputs = ['uart'] | |
138 | outputs = ['arm_etmv3'] | |
139 | annotations = ( | |
140 | ('trace', 'Trace info'), | |
141 | ('branch', 'Branches'), | |
142 | ('exception', 'Exceptions'), | |
143 | ('execution', 'Instruction execution'), | |
144 | ('data', 'Data access'), | |
145 | ('pc', 'Program counter'), | |
146 | ('instr_e', 'Executed instructions'), | |
147 | ('instr_n', 'Not executed instructions'), | |
148 | ('source', 'Source code'), | |
149 | ('location', 'Current location'), | |
150 | ('function', 'Current function'), | |
151 | ) | |
152 | annotation_rows = ( | |
153 | ('trace', 'Trace info', (0,)), | |
154 | ('flow', 'Code flow', (1, 2, 3,)), | |
155 | ('data', 'Data access', (4,)), | |
156 | ('pc', 'Program counter', (5,)), | |
157 | ('instruction', 'Instructions', (6, 7,)), | |
158 | ('source', 'Source code', (8,)), | |
159 | ('location', 'Current location', (9,)), | |
160 | ('function', 'Current function', (10,)), | |
161 | ) | |
162 | options = ( | |
163 | {'id': 'objdump', 'desc': 'objdump path', | |
164 | 'default': 'arm-none-eabi-objdump'}, | |
165 | {'id': 'objdump_opts', 'desc': 'objdump options', | |
1c023fab | 166 | 'default': '-lSC'}, |
686f0c36 PA |
167 | {'id': 'elffile', 'desc': '.elf path', |
168 | 'default': ''}, | |
169 | {'id': 'branch_enc', 'desc': 'Branch encoding', | |
170 | 'default': 'alternative', 'values': ('alternative', 'original')}, | |
171 | ) | |
172 | ||
173 | def __init__(self, **kwargs): | |
174 | self.buf = [] | |
175 | self.syncbuf = [] | |
176 | self.prevsample = 0 | |
177 | self.last_branch = 0 | |
178 | self.cpu_state = 'arm' | |
179 | self.current_pc = 0 | |
180 | self.current_loc = None | |
181 | self.current_func = None | |
182 | self.next_instr_lookup = {} | |
183 | self.file_lookup = {} | |
184 | self.func_lookup = {} | |
185 | self.disasm_lookup = {} | |
186 | self.source_lookup = {} | |
187 | ||
188 | def start(self): | |
189 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
190 | self.load_objdump() | |
191 | ||
192 | def load_objdump(self): | |
193 | '''Parse disassembly obtained from objdump into two tables: | |
194 | next_instr_lookup: Find the next PC addr from current PC. | |
195 | disasm_lookup: Find the instruction text from current PC. | |
196 | source_lookup: Find the source code line from current PC. | |
197 | ''' | |
198 | if not (self.options['objdump'] and self.options['elffile']): | |
199 | return | |
200 | ||
201 | opts = [self.options['objdump']] | |
202 | opts += self.options['objdump_opts'].split() | |
203 | opts += [self.options['elffile']] | |
204 | ||
205 | try: | |
206 | disasm = subprocess.check_output(opts) | |
207 | except subprocess.CalledProcessError: | |
208 | return | |
209 | ||
210 | disasm = disasm.decode('utf-8', 'replace') | |
211 | ||
212 | instpat = re.compile('\s*([0-9a-fA-F]+):\t+([0-9a-fA-F ]+)\t+([a-zA-Z][^;]+)\s*;?.*') | |
213 | branchpat = re.compile('(b|bl|b..|bl..|cbnz|cbz)(?:\.[wn])?\s+(?:r[0-9]+,\s*)?([0-9a-fA-F]+)') | |
214 | filepat = re.compile('[^\s]+[/\\\\]([a-zA-Z0-9._-]+:[0-9]+)(?:\s.*)?') | |
1c023fab | 215 | funcpat = re.compile('[0-9a-fA-F]+\s*<([^>]+)>:.*') |
686f0c36 PA |
216 | |
217 | prev_src = '' | |
218 | prev_file = '' | |
219 | prev_func = '' | |
220 | ||
221 | for line in disasm.split('\n'): | |
222 | m = instpat.match(line) | |
223 | if m: | |
224 | addr = int(m.group(1), 16) | |
225 | raw = m.group(2) | |
226 | disas = m.group(3).strip().replace('\t', ' ') | |
227 | self.disasm_lookup[addr] = disas | |
228 | self.source_lookup[addr] = prev_src | |
229 | self.file_lookup[addr] = prev_file | |
230 | self.func_lookup[addr] = prev_func | |
231 | ||
232 | # Next address in direct sequence. | |
233 | ilen = len(raw.replace(' ', '')) // 2 | |
234 | next_n = addr + ilen | |
235 | ||
236 | # Next address if branch is taken. | |
237 | bm = branchpat.match(disas) | |
238 | if bm: | |
239 | next_e = int(bm.group(2), 16) | |
240 | else: | |
241 | next_e = next_n | |
242 | ||
243 | self.next_instr_lookup[addr] = (next_n, next_e) | |
244 | else: | |
245 | m = funcpat.match(line) | |
246 | if m: | |
247 | prev_func = m.group(1) | |
1c023fab | 248 | prev_src = None |
686f0c36 PA |
249 | else: |
250 | m = filepat.match(line) | |
251 | if m: | |
252 | prev_file = m.group(1) | |
1c023fab | 253 | prev_src = None |
686f0c36 PA |
254 | else: |
255 | prev_src = line.strip() | |
256 | ||
257 | def flush_current_loc(self): | |
258 | if self.current_loc is not None: | |
259 | ss, es, loc, src = self.current_loc | |
260 | if loc: | |
261 | self.put(ss, es, self.out_ann, [9, [loc]]) | |
262 | if src: | |
263 | self.put(ss, es, self.out_ann, [8, [src]]) | |
264 | self.current_loc = None | |
265 | ||
266 | def flush_current_func(self): | |
267 | if self.current_func is not None: | |
268 | ss, es, func = self.current_func | |
269 | if func: | |
270 | self.put(ss, es, self.out_ann, [10, [func]]) | |
271 | self.current_func = None | |
272 | ||
273 | def instructions_executed(self, exec_status): | |
274 | '''Advance program counter based on executed instructions. | |
275 | Argument is a list of False for not executed and True for executed | |
276 | instructions. | |
277 | ''' | |
278 | ||
1c023fab PA |
279 | if len(exec_status) == 0: |
280 | return | |
281 | ||
686f0c36 PA |
282 | tdelta = max(1, (self.prevsample - self.startsample) / len(exec_status)) |
283 | ||
284 | for i, exec_status in enumerate(exec_status): | |
285 | pc = self.current_pc | |
286 | default_next = pc + 2 if self.cpu_state == 'thumb' else pc + 4 | |
287 | target_n, target_e = self.next_instr_lookup.get(pc, (default_next, default_next)) | |
288 | ss = self.startsample + round(tdelta * i) | |
289 | es = self.startsample + round(tdelta * (i+1)) | |
290 | ||
291 | self.put(ss, es, self.out_ann, | |
292 | [5, ['PC 0x%08x' % pc, '0x%08x' % pc, '%08x' % pc]]) | |
293 | ||
294 | new_loc = self.file_lookup.get(pc) | |
295 | new_src = self.source_lookup.get(pc) | |
296 | new_dis = self.disasm_lookup.get(pc) | |
297 | new_func = self.func_lookup.get(pc) | |
298 | ||
299 | # Report source line only when it changes. | |
300 | if self.current_loc is not None: | |
301 | if new_loc != self.current_loc[2] or new_src != self.current_loc[3]: | |
302 | self.flush_current_loc() | |
303 | ||
304 | if self.current_loc is None: | |
305 | self.current_loc = [ss, es, new_loc, new_src] | |
306 | else: | |
307 | self.current_loc[1] = es | |
308 | ||
309 | # Report function name only when it changes. | |
310 | if self.current_func is not None: | |
311 | if new_func != self.current_func[2]: | |
312 | self.flush_current_func() | |
313 | ||
314 | if self.current_func is None: | |
315 | self.current_func = [ss, es, new_func] | |
316 | else: | |
317 | self.current_func[1] = es | |
318 | ||
319 | # Report instruction every time. | |
320 | if new_dis: | |
321 | if exec_status: | |
322 | a = [6, ['Executed: ' + new_dis, new_dis, new_dis.split()[0]]] | |
323 | else: | |
324 | a = [7, ['Not executed: ' + new_dis, new_dis, new_dis.split()[0]]] | |
325 | self.put(ss, es, self.out_ann, a) | |
326 | ||
327 | if exec_status: | |
328 | self.current_pc = target_e | |
329 | else: | |
330 | self.current_pc = target_n | |
331 | ||
332 | def get_packet_type(self, byte): | |
333 | '''Identify packet type based on its first byte. | |
334 | See ARM IHI0014Q section "ETMv3 Signal Protocol" "Packet Types" | |
335 | ''' | |
336 | if byte & 0x01 == 0x01: | |
337 | return 'branch' | |
338 | elif byte == 0x00: | |
339 | return 'a_sync' | |
340 | elif byte == 0x04: | |
341 | return 'cyclecount' | |
342 | elif byte == 0x08: | |
343 | return 'i_sync' | |
344 | elif byte == 0x0C: | |
345 | return 'trigger' | |
346 | elif byte & 0xF3 in (0x20, 0x40, 0x60): | |
347 | return 'ooo_data' | |
348 | elif byte == 0x50: | |
349 | return 'store_failed' | |
350 | elif byte == 0x70: | |
351 | return 'i_sync' | |
352 | elif byte & 0xDF in (0x54, 0x58, 0x5C): | |
353 | return 'ooo_place' | |
354 | elif byte == 0x3C: | |
355 | return 'vmid' | |
356 | elif byte & 0xD3 == 0x02: | |
357 | return 'data' | |
358 | elif byte & 0xFB == 0x42: | |
359 | return 'timestamp' | |
360 | elif byte == 0x62: | |
361 | return 'data_suppressed' | |
362 | elif byte == 0x66: | |
363 | return 'ignore' | |
364 | elif byte & 0xEF == 0x6A: | |
365 | return 'value_not_traced' | |
366 | elif byte == 0x6E: | |
367 | return 'context_id' | |
368 | elif byte == 0x76: | |
369 | return 'exception_exit' | |
370 | elif byte == 0x7E: | |
371 | return 'exception_entry' | |
372 | elif byte & 0x81 == 0x80: | |
373 | return 'p_header' | |
374 | else: | |
375 | return 'unknown' | |
376 | ||
377 | def fallback(self, buf): | |
378 | ptype = self.get_packet_type(buf[0]) | |
379 | return [0, ['Unhandled ' + ptype + ': ' + ' '.join(['%02x' % b for b in buf])]] | |
380 | ||
381 | def handle_a_sync(self, buf): | |
382 | if buf[-1] == 0x80: | |
383 | return [0, ['Synchronization']] | |
384 | ||
385 | def handle_exception_exit(self, buf): | |
1c023fab | 386 | return [2, ['Exception exit']] |
686f0c36 PA |
387 | |
388 | def handle_exception_entry(self, buf): | |
1c023fab | 389 | return [2, ['Exception entry']] |
686f0c36 PA |
390 | |
391 | def handle_i_sync(self, buf): | |
392 | contextid_bytes = 0 # This is the default ETM config. | |
393 | ||
394 | if len(buf) < 6: | |
395 | return None # Packet definitely not full yet. | |
396 | ||
397 | if buf[0] == 0x08: # No cycle count. | |
398 | cyclecount = None | |
399 | idx = 1 + contextid_bytes # Index to info byte. | |
400 | elif buf[0] == 0x70: # With cycle count. | |
401 | cyclecount, cyclen = parse_varint(buf[1:6]) | |
402 | idx = 1 + cyclen + contextid_bytes | |
403 | ||
404 | if len(buf) <= idx + 4: | |
405 | return None | |
406 | infobyte = buf[idx] | |
407 | addr = parse_uint(buf[idx+1:idx+5]) | |
408 | ||
409 | reasoncode = (infobyte >> 5) & 3 | |
410 | reason = ('Periodic', 'Tracing enabled', 'After overflow', 'Exit from debug')[reasoncode] | |
411 | jazelle = (infobyte >> 4) & 1 | |
412 | nonsec = (infobyte >> 3) & 1 | |
413 | altisa = (infobyte >> 2) & 1 | |
414 | hypervisor = (infobyte >> 1) & 1 | |
415 | thumb = addr & 1 | |
416 | addr &= 0xFFFFFFFE | |
417 | ||
418 | if reasoncode == 0 and self.current_pc != addr: | |
419 | self.put(self.startsample, self.prevsample, self.out_ann, | |
420 | [0, ['WARN: Unexpected PC change 0x%08x -> 0x%08x' % \ | |
421 | (self.current_pc, addr)]]) | |
422 | elif reasoncode != 0: | |
423 | # Reset location when the trace has been interrupted. | |
424 | self.flush_current_loc() | |
425 | self.flush_current_func() | |
426 | ||
427 | self.last_branch = addr | |
428 | self.current_pc = addr | |
429 | ||
430 | if jazelle: | |
431 | self.cpu_state = 'jazelle' | |
432 | elif thumb: | |
433 | self.cpu_state = 'thumb' | |
434 | else: | |
435 | self.cpu_state = 'arm' | |
436 | ||
437 | cycstr = '' | |
438 | if cyclecount is not None: | |
439 | cycstr = ', cyclecount %d' % cyclecount | |
440 | ||
441 | if infobyte & 0x80: # LSIP packet | |
442 | self.put(self.startsample, self.prevsample, self.out_ann, | |
443 | [0, ['WARN: LSIP I-Sync packet not implemented']]) | |
444 | ||
445 | return [0, ['I-Sync: %s, PC 0x%08x, %s state%s' % \ | |
446 | (reason, addr, self.cpu_state, cycstr), \ | |
447 | 'I-Sync: %s 0x%08x' % (reason, addr)]] | |
448 | ||
449 | def handle_trigger(self, buf): | |
450 | return [0, ['Trigger event', 'Trigger']] | |
451 | ||
452 | def handle_p_header(self, buf): | |
453 | # Only non cycle-accurate mode supported. | |
454 | if buf[0] & 0x83 == 0x80: | |
455 | n = (buf[0] >> 6) & 1 | |
456 | e = (buf[0] >> 2) & 15 | |
457 | ||
458 | self.instructions_executed([1] * e + [0] * n) | |
459 | ||
460 | if n: | |
461 | return [3, ['%d instructions executed, %d skipped due to ' \ | |
462 | 'condition codes' % (e, n), | |
463 | '%d ins exec, %d skipped' % (e, n), | |
464 | '%dE,%dN' % (e, n)]] | |
465 | else: | |
466 | return [3, ['%d instructions executed' % e, | |
467 | '%d ins exec' % e, '%dE' % e]] | |
468 | elif buf[0] & 0xF3 == 0x82: | |
469 | i1 = (buf[0] >> 3) & 1 | |
470 | i2 = (buf[0] >> 2) & 1 | |
471 | self.instructions_executed([not i1, not i2]) | |
472 | txt1 = ('executed', 'skipped') | |
473 | txt2 = ('E', 'S') | |
474 | return [3, ['Instruction 1 %s, instruction 2 %s' % (txt1[i1], txt1[i2]), | |
475 | 'I1 %s, I2 %s' % (txt2[i1], txt2[i2]), | |
476 | '%s,%s' % (txt2[i1], txt2[i2])]] | |
477 | else: | |
478 | return self.fallback(buf) | |
479 | ||
480 | def handle_branch(self, buf): | |
481 | if buf[-1] & 0x80 != 0x00: | |
482 | return None # Not complete yet. | |
483 | ||
484 | brinfo = parse_branch_addr(buf, self.last_branch, self.cpu_state, | |
485 | self.options['branch_enc']) | |
486 | ||
487 | if brinfo is None: | |
488 | return None # Not complete yet. | |
489 | ||
490 | addr, addrlen, cpu_state, exc_info = brinfo | |
491 | self.last_branch = addr | |
492 | self.current_pc = addr | |
493 | ||
494 | txt = '' | |
495 | ||
496 | if cpu_state != self.cpu_state: | |
497 | txt += ', to %s state' % cpu_state | |
498 | self.cpu_state = cpu_state | |
499 | ||
1c023fab PA |
500 | annidx = 1 |
501 | ||
686f0c36 | 502 | if exc_info: |
1c023fab | 503 | annidx = 2 |
686f0c36 PA |
504 | ns, exc, cancel, altisa, hyp, resume = exc_info |
505 | if ns: | |
506 | txt += ', to non-secure state' | |
507 | if exc: | |
1c023fab PA |
508 | if exc < len(exc_names): |
509 | txt += ', exception %s' % exc_names[exc] | |
510 | else: | |
511 | txt += ', exception 0x%02x' % exc | |
686f0c36 PA |
512 | if cancel: |
513 | txt += ', instr cancelled' | |
514 | if altisa: | |
515 | txt += ', to AltISA' | |
516 | if hyp: | |
517 | txt += ', to hypervisor' | |
518 | if resume: | |
519 | txt += ', instr resume 0x%02x' % resume | |
520 | ||
1c023fab PA |
521 | return [annidx, ['Branch to 0x%08x%s' % (addr, txt), |
522 | 'B 0x%08x%s' % (addr, txt)]] | |
686f0c36 PA |
523 | |
524 | def decode(self, ss, es, data): | |
525 | ptype, rxtx, pdata = data | |
526 | ||
527 | if ptype != 'DATA': | |
528 | return | |
529 | ||
530 | # Reset packet if there is a long pause between bytes. | |
531 | # This helps getting the initial synchronization. | |
532 | self.byte_len = es - ss | |
533 | if ss - self.prevsample > 16 * self.byte_len: | |
534 | self.flush_current_loc() | |
535 | self.flush_current_func() | |
536 | self.buf = [] | |
537 | self.prevsample = es | |
538 | ||
539 | self.buf.append(pdata[0]) | |
540 | ||
541 | # Store the start time of the packet. | |
542 | if len(self.buf) == 1: | |
543 | self.startsample = ss | |
544 | ||
545 | # Keep separate buffer for detection of sync packets. | |
546 | # Sync packets override everything else, so that we can regain sync | |
547 | # even if some packets are corrupted. | |
548 | self.syncbuf = self.syncbuf[-4:] + [pdata[0]] | |
549 | if self.syncbuf == [0x00, 0x00, 0x00, 0x00, 0x80]: | |
550 | self.buf = self.syncbuf | |
551 | self.syncbuf = [] | |
552 | ||
553 | # See if it is ready to be decoded. | |
554 | ptype = self.get_packet_type(self.buf[0]) | |
555 | if hasattr(self, 'handle_' + ptype): | |
556 | func = getattr(self, 'handle_' + ptype) | |
557 | data = func(self.buf) | |
558 | else: | |
559 | data = self.fallback(self.buf) | |
560 | ||
561 | if data is not None: | |
562 | if data: | |
563 | self.put(self.startsample, es, self.out_ann, data) | |
564 | self.buf = [] |