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'] * 2
88 self.max_ack_len_samples = round((bit_time / 1000) * self.samplerate)
91 self.stat = Stat.WAIT_START
92 self.samplerate = None
93 self.fall_start = None
96 self.reset_frame_vars()
98 def reset_frame_vars(self):
103 self.byte_start = None
104 self.frame_start = None
105 self.frame_end = None
109 def metadata(self, key, value):
110 if key == srd.SRD_CONF_SAMPLERATE:
111 self.samplerate = value
114 def set_stat(self, stat):
117 def handle_frame(self, is_nack):
118 if self.fall_start is None or self.fall_end is None:
123 while i < len(self.cmd_bytes):
124 str += '{:02x}'.format(self.cmd_bytes[i]['val'])
125 if i != (len(self.cmd_bytes) - 1):
129 self.put(self.frame_start, self.frame_end, self.out_ann, [7, [str]])
134 while i < len(self.cmd_bytes):
135 if i == 0: # Parse header
136 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
137 str = 'HDR: ' + src + ', ' + dst
138 elif i == 1: # Parse opcode
139 str += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
140 else: # Parse operands
144 str += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
145 if i != len(self.cmd_bytes) - 1:
149 # Header only commands are PINGS
151 str += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
153 # Add extra information (ack of the command from the destination)
154 str += ' | R: NACK' if is_nack else ' | R: ACK'
156 self.put(self.frame_start, self.frame_end, self.out_ann, [8, [str]])
159 zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
160 total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0
161 pulse = Pulse.INVALID
163 # VALIDATION: Identify pulse based on length of the low period
165 if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']:
169 # VALIDATION: Invalid pulse
170 if pulse == Pulse.INVALID:
171 self.set_stat(Stat.WAIT_START)
172 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']])
175 # VALIDATION: If waiting for start, discard everything else
176 if self.stat == Stat.WAIT_START and pulse != Pulse.START:
177 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']])
180 # VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected
181 if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START:
182 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']])
183 self.set_stat(Stat.WAIT_START)
185 # VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period
186 if self.stat == Stat.WAIT_ACK and pulse != Pulse.START:
187 if total_time < timing[pulse]['total']['min']:
188 pulse = Pulse.INVALID
189 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']])
190 self.set_stat(Stat.WAIT_START)
193 # VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive
194 if self.stat == Stat.GET_BITS and pulse == Pulse.START:
195 # Make sure we received a complete byte to consider it a valid ping
196 if self.bit_count == 0:
197 self.handle_frame(self.is_nack)
199 self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']])
201 # Set wait start so we receive next frame
202 self.set_stat(Stat.WAIT_START)
204 # VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK)
205 if self.stat != Stat.WAIT_ACK and pulse != Pulse.START:
206 if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']:
207 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']])
208 pulse = Pulse.INVALID
209 self.set_stat(Stat.WAIT_START)
212 if pulse == Pulse.ZERO:
214 elif pulse == Pulse.ONE:
218 if self.stat == Stat.WAIT_START:
219 self.set_stat(Stat.GET_BITS)
220 self.reset_frame_vars()
221 self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']])
224 elif self.stat == Stat.GET_BITS:
225 # Reset stats on first bit
226 if self.bit_count == 0:
227 self.byte_start = self.fall_start
230 # If 1st byte of the datagram save its sample num
231 if len(self.cmd_bytes) == 0:
232 self.frame_start = self.fall_start
234 self.byte += (bit << (7 - self.bit_count))
236 self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]])
238 if self.bit_count == 8:
241 self.set_stat(Stat.WAIT_EOM)
242 self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]])
243 self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte})
246 elif self.stat == Stat.WAIT_EOM:
248 self.frame_end = self.fall_end
250 a = [2, ['EOM=Y']] if self.eom else [1, ['EOM=N']]
251 self.put(self.fall_start, self.fall_end, self.out_ann, a)
253 self.set_stat(Stat.WAIT_ACK)
256 elif self.stat == Stat.WAIT_ACK:
257 # If a frame with broadcast destination is being sent, the ACK is
258 # inverted: a 0 is considered a NACK, therefore we invert the value
259 # of the bit here, so we match the real meaning of it.
260 if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F:
263 if (self.fall_end - self.fall_start) > self.max_ack_len_samples:
264 ann_end = self.fall_start + self.max_ack_len_samples
266 ann_end = self.fall_end
269 # Any NACK detected in the frame is enough to consider the
270 # whole frame NACK'd.
272 self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']])
274 self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']])
276 # After ACK bit, wait for new datagram or continue reading current
277 # one based on EOM value.
278 if self.eom or self.is_nack:
279 self.set_stat(Stat.WAIT_START)
280 self.handle_frame(self.is_nack)
282 self.set_stat(Stat.GET_BITS)
285 self.out_ann = self.register(srd.OUTPUT_ANN)
288 if not self.samplerate:
289 raise SamplerateError('Cannot decode without samplerate.')
291 # Wait for first falling edge.
293 self.fall_end = self.samplenum
297 self.rise = self.samplenum
299 if self.stat == Stat.WAIT_ACK:
300 self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}])
302 self.wait([{0: 'f'}])
304 self.fall_start = self.fall_end
305 self.fall_end = self.samplenum
308 # If there was a timeout while waiting for ACK: RESYNC.
309 # Note: This is an expected situation as no new falling edge will
310 # happen until next frame is transmitted.
311 if self.matched == (False, True):
313 self.fall_end = self.samplenum