2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2018 Jorge Solla Rubiales <jorgesolla@gmail.com>
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.
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.
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/>.
20 import sigrokdecode as srd
21 from .protocoldata import *
25 INVALID, START, ZERO, ONE = range(4)
29 WAIT_START, GET_BITS, WAIT_EOM, WAIT_ACK = range(4)
31 # Pulse times in milliseconds
34 'low': { 'min': 3.5, 'max': 3.9 },
35 'total': { 'min': 4.3, 'max': 4.7 }
38 'low': { 'min': 1.3, 'max': 1.7 },
39 'total': { 'min': 2.05, 'max': 2.75 }
42 'low': { 'min': 0.4, 'max': 0.8 },
43 'total': { 'min': 2.05, 'max': 2.75 }
47 class ChannelError(Exception):
50 class Decoder(srd.Decoder):
55 desc = 'HDMI Consumer Electronics Control (CEC) protocol.'
60 {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
64 ('eom-0', 'End of message'),
65 ('eom-1', 'Message continued'),
66 ('nack', 'ACK not set'),
71 ('sections', 'Sections'),
72 ('warnings', 'Warnings')
75 ('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
76 ('bytes', 'Bytes', (6,)),
77 ('frames', 'Frames', (7,)),
78 ('sections', 'Sections', (8,)),
79 ('warnings', 'Warnings', (9,))
85 def precalculate(self):
86 # Restrict max length of ACK/NACK labels to 2 BIT pulses.
87 bit_time = timing[Pulse.ZERO]['total']['min']
88 bit_time = bit_time * 2
89 self.max_ack_len_samples = round((bit_time / 1000) * self.samplerate)
92 self.stat = Stat.WAIT_START
93 self.samplerate = None
94 self.fall_start = None
97 self.reset_frame_vars()
99 def reset_frame_vars(self):
104 self.byte_start = None
105 self.frame_start = None
106 self.frame_end = None
110 def metadata(self, key, value):
111 if key == srd.SRD_CONF_SAMPLERATE:
112 self.samplerate = value
115 def set_stat(self, stat):
118 def handle_frame(self, is_nack):
119 if self.fall_start is None or self.fall_end is None:
124 while i < len(self.cmd_bytes):
125 str += '{:02x}'.format(self.cmd_bytes[i]['val'])
126 if i != (len(self.cmd_bytes) - 1):
130 self.put(self.frame_start, self.frame_end, self.out_ann, [7, [str]])
135 while i < len(self.cmd_bytes):
136 if i == 0: # Parse header
137 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
138 str = 'HDR: ' + src + ', ' + dst
139 elif i == 1: # Parse opcode
140 str += ' | OPC: ' + decode_opcode(self.cmd_bytes[i]['val'])
141 else: # Parse operands
145 str += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
146 if i != len(self.cmd_bytes) - 1:
150 # Header only commands are PINGS
153 str += ' | OPC: PING'
155 str += ' | OPC: NONE. Aborted cmd'
157 # Add extra information (ack of the command from the destination)
163 self.put(self.frame_start, self.frame_end, self.out_ann, [8, [str]])
166 zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
167 total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0
168 pulse = Pulse.INVALID
170 # VALIDATION: Identify pulse based on length of the low period
172 if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']:
176 # VALIDATION: Invalid pulse
177 if pulse == Pulse.INVALID:
178 self.set_stat(Stat.WAIT_START)
179 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']])
182 # VALIDATION: If waiting for start, discard everything else
183 if self.stat == Stat.WAIT_START and pulse != Pulse.START:
184 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']])
187 # VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected
188 if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START:
189 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']])
190 self.set_stat(Stat.WAIT_START)
192 # VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period
193 if self.stat == Stat.WAIT_ACK and pulse != Pulse.START:
194 if total_time < timing[pulse]['total']['min']:
195 pulse = Pulse.INVALID
196 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']])
197 self.set_stat(Stat.WAIT_START)
200 # VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive
201 if self.stat == Stat.GET_BITS and pulse == Pulse.START:
202 # Make sure we received a complete byte to consider it a valid ping
203 if self.bit_count == 0:
204 self.handle_frame(self.is_nack)
206 self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']])
208 # Set wait start so we receive next frame
209 self.set_stat(Stat.WAIT_START)
211 # VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK)
212 if self.stat != Stat.WAIT_ACK and pulse != Pulse.START:
213 if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']:
214 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']])
215 pulse = Pulse.INVALID
216 self.set_stat(Stat.WAIT_START)
219 if pulse == Pulse.ZERO:
221 elif pulse == Pulse.ONE:
225 if self.stat == Stat.WAIT_START:
226 self.set_stat(Stat.GET_BITS)
227 self.reset_frame_vars()
228 self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']])
231 elif self.stat == Stat.GET_BITS:
232 # Reset stats on first bit
233 if self.bit_count == 0:
234 self.byte_start = self.fall_start
237 # If 1st byte of the datagram save its sample num
238 if len(self.cmd_bytes) == 0:
239 self.frame_start = self.fall_start
241 self.byte += (bit << (7 - self.bit_count))
243 self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]])
245 if self.bit_count == 8:
248 self.set_stat(Stat.WAIT_EOM)
249 self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]])
250 self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte})
253 elif self.stat == Stat.WAIT_EOM:
255 self.frame_end = self.fall_end
258 self.put(self.fall_start, self.fall_end, self.out_ann, [2, ['EOM=Y']])
260 self.put(self.fall_start, self.fall_end, self.out_ann, [1, ['EOM=N']])
262 self.set_stat(Stat.WAIT_ACK)
265 elif self.stat == Stat.WAIT_ACK:
266 # If a frame with broadcast destination is being sent, the ACK is
267 # inverted: a 0 is considered a NACK, therefore we invert the value
268 # of the bit here, so we match the real meaning of it.
269 if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F:
272 if (self.fall_end - self.fall_start) > self.max_ack_len_samples:
273 ann_end = self.fall_start + self.max_ack_len_samples
275 ann_end = self.fall_end
278 # Any NACK detected in the frame is enough to consider the
279 # whole frame NACK'd.
281 self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']])
283 self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']])
285 # After ACK bit, wait for new datagram or continue reading current
286 # one based on EOM value.
287 if self.eom or self.is_nack:
288 self.set_stat(Stat.WAIT_START)
289 self.handle_frame(self.is_nack)
291 self.set_stat(Stat.GET_BITS)
294 self.out_ann = self.register(srd.OUTPUT_ANN)
297 if not self.samplerate:
298 raise SamplerateError('Cannot decode without samplerate.')
300 # Wait for first falling edge.
302 self.fall_end = self.samplenum
306 self.rise = self.samplenum
308 if self.stat == Stat.WAIT_ACK:
309 self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}])
311 self.wait([{0: 'f'}])
313 self.fall_start = self.fall_end
314 self.fall_end = self.samplenum
317 # If there was a timeout while waiting for ACK: RESYNC.
318 # Note: This is an expected situation as no new falling edge will
319 # happen until next frame is transmitted.
320 if self.matched == (False, True):
322 self.fall_end = self.samplenum