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.'
59 tags = ['Display', 'PC']
61 {'id': 'cec', 'name': 'CEC', 'desc': 'CEC bus data'},
65 ('eom-0', 'End of message'),
66 ('eom-1', 'Message continued'),
67 ('nack', 'ACK not set'),
72 ('section', 'Section'),
73 ('warning', 'Warning')
76 ('bits', 'Bits', (0, 1, 2, 3, 4, 5)),
77 ('bytes', 'Bytes', (6,)),
78 ('frames', 'Frames', (7,)),
79 ('sections', 'Sections', (8,)),
80 ('warnings', 'Warnings', (9,))
86 def precalculate(self):
87 # Restrict max length of ACK/NACK labels to 2 BIT pulses.
88 bit_time = timing[Pulse.ZERO]['total']['min'] * 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 handle_frame(self, is_nack):
116 if self.fall_start is None or self.fall_end is None:
121 while i < len(self.cmd_bytes):
122 string += '{:02x}'.format(self.cmd_bytes[i]['val'])
123 if i != (len(self.cmd_bytes) - 1):
127 self.put(self.frame_start, self.frame_end, self.out_ann, [7, [string]])
132 while i < len(self.cmd_bytes):
133 if i == 0: # Parse header
134 (src, dst) = decode_header(self.cmd_bytes[i]['val'])
135 string = 'HDR: ' + src + ', ' + dst
136 elif i == 1: # Parse opcode
137 string += ' | OPC: ' + opcodes.get(self.cmd_bytes[i]['val'], 'Invalid')
138 else: # Parse operands
142 string += '0x{:02x}'.format(self.cmd_bytes[i]['val'])
143 if i != len(self.cmd_bytes) - 1:
147 # Header only commands are PINGS
149 string += ' | OPC: PING' if self.eom else ' | OPC: NONE. Aborted cmd'
151 # Add extra information (ack of the command from the destination)
152 string += ' | R: NACK' if is_nack else ' | R: ACK'
154 self.put(self.frame_start, self.frame_end, self.out_ann, [8, [string]])
157 zero_time = ((self.rise - self.fall_start) / self.samplerate) * 1000.0
158 total_time = ((self.fall_end - self.fall_start) / self.samplerate) * 1000.0
159 pulse = Pulse.INVALID
161 # VALIDATION: Identify pulse based on length of the low period
163 if zero_time >= timing[key]['low']['min'] and zero_time <= timing[key]['low']['max']:
167 # VALIDATION: Invalid pulse
168 if pulse == Pulse.INVALID:
169 self.stat = Stat.WAIT_START
170 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Invalid pulse: Wrong timing']])
173 # VALIDATION: If waiting for start, discard everything else
174 if self.stat == Stat.WAIT_START and pulse != Pulse.START:
175 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected START: BIT found']])
178 # VALIDATION: If waiting for ACK or EOM, only BIT pulses (0/1) are expected
179 if (self.stat == Stat.WAIT_ACK or self.stat == Stat.WAIT_EOM) and pulse == Pulse.START:
180 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Expected BIT: START received)']])
181 self.stat = Stat.WAIT_START
183 # VALIDATION: ACK bit pulse remains high till the next frame (if any): Validate only min time of the low period
184 if self.stat == Stat.WAIT_ACK and pulse != Pulse.START:
185 if total_time < timing[pulse]['total']['min']:
186 pulse = Pulse.INVALID
187 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['ACK pulse below minimun time']])
188 self.stat = Stat.WAIT_START
191 # VALIDATION / PING FRAME DETECTION: Initiator doesn't sets the EOM = 1 but stops sending when ack doesn't arrive
192 if self.stat == Stat.GET_BITS and pulse == Pulse.START:
193 # Make sure we received a complete byte to consider it a valid ping
194 if self.bit_count == 0:
195 self.handle_frame(self.is_nack)
197 self.put(self.frame_start, self.samplenum, self.out_ann, [9, ['ERROR: Incomplete byte received']])
199 # Set wait start so we receive next frame
200 self.stat = Stat.WAIT_START
202 # VALIDATION: Check timing of the BIT (0/1) pulse in any other case (not waiting for ACK)
203 if self.stat != Stat.WAIT_ACK and pulse != Pulse.START:
204 if total_time < timing[pulse]['total']['min'] or total_time > timing[pulse]['total']['max']:
205 self.put(self.fall_start, self.fall_end, self.out_ann, [9, ['Bit pulse exceeds total pulse timespan']])
206 pulse = Pulse.INVALID
207 self.stat = Stat.WAIT_START
210 if pulse == Pulse.ZERO:
212 elif pulse == Pulse.ONE:
216 if self.stat == Stat.WAIT_START:
217 self.stat = Stat.GET_BITS
218 self.reset_frame_vars()
219 self.put(self.fall_start, self.fall_end, self.out_ann, [0, ['ST']])
222 elif self.stat == Stat.GET_BITS:
223 # Reset stats on first bit
224 if self.bit_count == 0:
225 self.byte_start = self.fall_start
228 # If 1st byte of the datagram save its sample num
229 if len(self.cmd_bytes) == 0:
230 self.frame_start = self.fall_start
232 self.byte += (bit << (7 - self.bit_count))
234 self.put(self.fall_start, self.fall_end, self.out_ann, [5, [str(bit)]])
236 if self.bit_count == 8:
239 self.stat = Stat.WAIT_EOM
240 self.put(self.byte_start, self.samplenum, self.out_ann, [6, ['0x{:02x}'.format(self.byte)]])
241 self.cmd_bytes.append({'st': self.byte_start, 'ed': self.samplenum, 'val': self.byte})
244 elif self.stat == Stat.WAIT_EOM:
246 self.frame_end = self.fall_end
248 a = [2, ['EOM=Y']] if self.eom else [1, ['EOM=N']]
249 self.put(self.fall_start, self.fall_end, self.out_ann, a)
251 self.stat = Stat.WAIT_ACK
254 elif self.stat == Stat.WAIT_ACK:
255 # If a frame with broadcast destination is being sent, the ACK is
256 # inverted: a 0 is considered a NACK, therefore we invert the value
257 # of the bit here, so we match the real meaning of it.
258 if (self.cmd_bytes[0]['val'] & 0x0F) == 0x0F:
261 if (self.fall_end - self.fall_start) > self.max_ack_len_samples:
262 ann_end = self.fall_start + self.max_ack_len_samples
264 ann_end = self.fall_end
267 # Any NACK detected in the frame is enough to consider the
268 # whole frame NACK'd.
270 self.put(self.fall_start, ann_end, self.out_ann, [3, ['NACK']])
272 self.put(self.fall_start, ann_end, self.out_ann, [4, ['ACK']])
274 # After ACK bit, wait for new datagram or continue reading current
275 # one based on EOM value.
276 if self.eom or self.is_nack:
277 self.stat = Stat.WAIT_START
278 self.handle_frame(self.is_nack)
280 self.stat = Stat.GET_BITS
283 self.out_ann = self.register(srd.OUTPUT_ANN)
286 if not self.samplerate:
287 raise SamplerateError('Cannot decode without samplerate.')
289 # Wait for first falling edge.
291 self.fall_end = self.samplenum
295 self.rise = self.samplenum
297 if self.stat == Stat.WAIT_ACK:
298 self.wait([{0: 'f'}, {'skip': self.max_ack_len_samples}])
300 self.wait([{0: 'f'}])
302 self.fall_start = self.fall_end
303 self.fall_end = self.samplenum
306 # If there was a timeout while waiting for ACK: RESYNC.
307 # Note: This is an expected situation as no new falling edge will
308 # happen until next frame is transmitted.
309 if self.matched == (False, True):
311 self.fall_end = self.samplenum