]>
Commit | Line | Data |
---|---|---|
1 | ## | |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz> | |
5 | ## Copyright (C) 2012-2013 Uwe Hermann <uwe@hermann-uwe.de> | |
6 | ## | |
7 | ## This program is free software; you can redistribute it and/or modify | |
8 | ## it under the terms of the GNU General Public License as published by | |
9 | ## the Free Software Foundation; either version 2 of the License, or | |
10 | ## (at your option) any later version. | |
11 | ## | |
12 | ## This program is distributed in the hope that it will be useful, | |
13 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | ## GNU General Public License for more details. | |
16 | ## | |
17 | ## You should have received a copy of the GNU General Public License | |
18 | ## along with this program; if not, write to the Free Software | |
19 | ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | ## | |
21 | ||
22 | # USB (low-speed and full-speed) packet protocol decoder | |
23 | ||
24 | import sigrokdecode as srd | |
25 | ||
26 | ''' | |
27 | Protocol output format: | |
28 | ||
29 | Packet: | |
30 | [<ptype>, <pdata>] | |
31 | ||
32 | <ptype>, <pdata>: | |
33 | - 'SYNC', <sync> | |
34 | - 'PID', <pid> | |
35 | - 'ADDR', <addr> | |
36 | - 'EP', <ep> | |
37 | - 'CRC5', <crc5> | |
38 | - 'CRC16', <crc16> | |
39 | - 'EOP', <eop> | |
40 | - 'FRAMENUM', <framenum> | |
41 | - 'DATABYTE', <databyte> | |
42 | - 'HUBADDR', <hubaddr> | |
43 | - 'SC', <sc> | |
44 | - 'PORT', <port> | |
45 | - 'S', <s> | |
46 | - 'E/U', <e/u> | |
47 | - 'ET', <et> | |
48 | - 'PACKET', [<pcategory>, <pname>, <pinfo>] | |
49 | ||
50 | <pcategory>, <pname>, <pinfo>: | |
51 | - 'TOKEN', 'OUT', [<sync>, <pid>, <addr>, <ep>, <crc5>, <eop>] | |
52 | - 'TOKEN', 'IN', [<sync>, <pid>, <addr>, <ep>, <crc5>, <eop>] | |
53 | - 'TOKEN', 'SOF', [<sync>, <pid>, <framenum>, <crc5>, <eop>] | |
54 | - 'TOKEN', 'SETUP', [<sync>, <pid>, <addr>, <ep>, <crc5>, <eop>] | |
55 | - 'DATA', 'DATA0', [<sync>, <pid>, <databytes>, <crc16>, <eop>] | |
56 | - 'DATA', 'DATA1', [<sync>, <pid>, <databytes>, <crc16>, <eop>] | |
57 | - 'DATA', 'DATA2', [<sync>, <pid>, <databytes>, <crc16>, <eop>] | |
58 | - 'DATA', 'MDATA', [<sync>, <pid>, <databytes>, <crc16>, <eop>] | |
59 | - 'HANDSHAKE', 'ACK', [<sync>, <pid>, <eop>] | |
60 | - 'HANDSHAKE', 'NACK', [<sync>, <pid>, <eop>] | |
61 | - 'HANDSHAKE', 'STALL', [<sync>, <pid>, <eop>] | |
62 | - 'HANDSHAKE', 'NYET', [<sync>, <pid>, <eop>] | |
63 | - 'SPECIAL', 'PRE', [<sync>, <pid>, <addr>, <ep>, <crc5>, <eop>] | |
64 | - 'SPECIAL', 'ERR', [<sync>, <pid>, <eop>] | |
65 | - 'SPECIAL', 'SPLIT', | |
66 | [<sync>, <pid>, <hubaddr>, <sc>, <port>, <s>, <e/u>, <et>, <crc5>, <eop>] | |
67 | - 'SPECIAL', 'PING', [<sync>, <pid>, <addr>, <ep>, <crc5>, <eop>] | |
68 | - 'SPECIAL', 'Reserved', None | |
69 | ||
70 | <sync>: SYNC field bitstring, normally '00000001' (8 chars). | |
71 | <pid>: Packet ID bitstring, e.g. '11000011' for DATA0 (8 chars). | |
72 | <addr>: Address field number, 0-127 (7 bits). | |
73 | <ep>: Endpoint number, 0-15 (4 bits). | |
74 | <crc5>: CRC-5 number (5 bits). | |
75 | <crc16>: CRC-16 number (16 bits). | |
76 | <eop>: End of packet marker. List of symbols, usually ['SE0', 'SE0', 'J']. | |
77 | <framenum>: USB (micro)frame number, 0-2047 (11 bits). | |
78 | <databyte>: A single data byte, e.g. 0x55. | |
79 | <databytes>: List of data bytes, e.g. [0x55, 0xaa, 0x99] (0 - 1024 bytes). | |
80 | <hubaddr>: TODO | |
81 | <sc>: TODO | |
82 | <port>: TODO | |
83 | <s>: TODO | |
84 | <e/u>: TODO | |
85 | <et>: TODO | |
86 | ''' | |
87 | ||
88 | # Packet IDs (PIDs). | |
89 | # The first 4 bits are the 'packet type' field, the last 4 bits are the | |
90 | # 'check field' (each bit in the check field must be the inverse of the resp. | |
91 | # bit in the 'packet type' field; if not, that's a 'PID error'). | |
92 | # For the 4-bit strings, the left-most '1' or '0' is the LSB, i.e. it's sent | |
93 | # to the bus first. | |
94 | pids = { | |
95 | # Tokens | |
96 | '10000111': ['OUT', 'Address & EP number in host-to-function transaction'], | |
97 | '10010110': ['IN', 'Address & EP number in function-to-host transaction'], | |
98 | '10100101': ['SOF', 'Start-Of-Frame marker & frame number'], | |
99 | '10110100': ['SETUP', 'Address & EP number in host-to-function transaction for SETUP to a control pipe'], | |
100 | ||
101 | # Data | |
102 | # Note: DATA2 and MDATA are HS-only. | |
103 | '11000011': ['DATA0', 'Data packet PID even'], | |
104 | '11010010': ['DATA1', 'Data packet PID odd'], | |
105 | '11100001': ['DATA2', 'Data packet PID HS, high bandwidth isosynchronous transaction in a microframe'], | |
106 | '11110000': ['MDATA', 'Data packet PID HS for split and high-bandwidth isosynchronous transactions'], | |
107 | ||
108 | # Handshake | |
109 | '01001011': ['ACK', 'Receiver accepts error-free packet'], | |
110 | '01011010': ['NAK', 'Receiver cannot accept or transmitter cannot send'], | |
111 | '01111000': ['STALL', 'EP halted or control pipe request unsupported'], | |
112 | '01101001': ['NYET', 'No response yet from receiver'], | |
113 | ||
114 | # Special | |
115 | '00111100': ['PRE', 'Host-issued preamble; enables downstream bus traffic to low-speed devices'], | |
116 | '00111100': ['ERR', 'Split transaction error handshake'], | |
117 | '00011110': ['SPLIT', 'HS split transaction token'], | |
118 | '00101101': ['PING', 'HS flow control probe for a bulk/control EP'], | |
119 | '00001111': ['Reserved', 'Reserved PID'], | |
120 | } | |
121 | ||
122 | def get_category(pidname): | |
123 | if pidname in ('OUT', 'IN', 'SOF', 'SETUP'): | |
124 | return 'TOKEN' | |
125 | elif pidname in ('DATA0', 'DATA1', 'DATA2', 'MDATA'): | |
126 | return 'DATA' | |
127 | elif pidname in ('ACK', 'NACK', 'STALL', 'NYET'): | |
128 | return 'HANDSHAKE' | |
129 | else: | |
130 | return 'SPECIAL' | |
131 | ||
132 | def bitstr_to_num(bitstr): | |
133 | if not bitstr: | |
134 | return 0 | |
135 | l = list(bitstr) | |
136 | l.reverse() | |
137 | return int(''.join(l), 2) | |
138 | ||
139 | class Decoder(srd.Decoder): | |
140 | api_version = 1 | |
141 | id = 'usb_packet' | |
142 | name = 'USB packet' | |
143 | longname = 'Universal Serial Bus (LS/FS) packet' | |
144 | desc = 'USB (low-speed and full-speed) packet protocol.' | |
145 | license = 'gplv2+' | |
146 | inputs = ['usb_signalling'] | |
147 | outputs = ['usb_packet'] | |
148 | probes = [] | |
149 | optional_probes = [] | |
150 | options = { | |
151 | 'signalling': ['Signalling', 'full-speed'], | |
152 | } | |
153 | annotations = [ | |
154 | ['Text', 'Human-readable text'] | |
155 | ] | |
156 | ||
157 | def __init__(self): | |
158 | self.samplenum = 0 | |
159 | self.bits = [] | |
160 | self.packet = [] | |
161 | self.packet_summary = '' | |
162 | self.ss = self.es = None | |
163 | self.ss_packet = self.es_packet = None | |
164 | self.state = 'WAIT FOR SOP' | |
165 | ||
166 | def putpb(self, data): | |
167 | self.put(self.ss, self.es, self.out_proto, data) | |
168 | ||
169 | def putb(self, data): | |
170 | self.put(self.ss, self.es, self.out_ann, data) | |
171 | ||
172 | def putpp(self, data): | |
173 | self.put(self.ss_packet, self.es_packet, self.out_proto, data) | |
174 | ||
175 | def putp(self, data): | |
176 | self.put(self.ss_packet, self.es_packet, self.out_ann, data) | |
177 | ||
178 | def start(self): | |
179 | self.out_proto = self.register(srd.OUTPUT_PYTHON) | |
180 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
181 | ||
182 | def handle_packet(self): | |
183 | packet = '' | |
184 | for (bit, ss, es) in self.bits: | |
185 | packet += bit | |
186 | ||
187 | # Bits[0:7]: SYNC | |
188 | sync = packet[:7 + 1] | |
189 | self.ss, self.es = self.bits[0][1], self.bits[7][2] | |
190 | # The SYNC pattern for low-speed/full-speed is KJKJKJKK (00000001). | |
191 | if sync != '00000001': | |
192 | self.putpb(['SYNC ERROR', sync]) | |
193 | self.putb([0, ['SYNC ERROR: %s' % sync]]) | |
194 | else: | |
195 | self.putpb(['SYNC', sync]) | |
196 | self.putb([0, ['SYNC: %s' % sync]]) | |
197 | self.packet.append(sync) | |
198 | ||
199 | # Bits[8:15]: PID | |
200 | pid = packet[8:15 + 1] | |
201 | pidname = pids.get(pid, (pid, ''))[0] | |
202 | self.ss, self.es = self.bits[8][1], self.bits[15][2] | |
203 | self.putpb(['PID', pidname]) | |
204 | self.putb([0, ['PID: %s' % pidname]]) | |
205 | self.packet.append(pid) | |
206 | self.packet_summary += pidname | |
207 | ||
208 | if pidname in ('OUT', 'IN', 'SOF', 'SETUP', 'PRE', 'PING'): | |
209 | if pidname == 'SOF': | |
210 | # Bits[16:26]: Framenum | |
211 | framenum = bitstr_to_num(packet[16:26 + 1]) | |
212 | self.ss, self.es = self.bits[16][1], self.bits[26][2] | |
213 | self.putpb(['FRAMENUM', framenum]) | |
214 | self.putb([0, ['Frame: %d' % framenum]]) | |
215 | self.packet.append(framenum) | |
216 | self.packet_summary += ' %d' % framenum | |
217 | else: | |
218 | # Bits[16:22]: Addr | |
219 | addr = bitstr_to_num(packet[16:22 + 1]) | |
220 | self.ss, self.es = self.bits[16][1], self.bits[22][2] | |
221 | self.putpb(['ADDR', addr]) | |
222 | self.putb([0, ['Addr: %d' % addr]]) | |
223 | self.packet.append(addr) | |
224 | self.packet_summary += ' ADDR %d' % addr | |
225 | ||
226 | # Bits[23:26]: EP | |
227 | ep = bitstr_to_num(packet[23:26 + 1]) | |
228 | self.ss, self.es = self.bits[23][1], self.bits[26][2] | |
229 | self.putpb(['EP', ep]) | |
230 | self.putb([0, ['EP: %d' % ep]]) | |
231 | self.packet.append(ep) | |
232 | self.packet_summary += ' EP %d' % ep | |
233 | ||
234 | # Bits[27:31]: CRC5 | |
235 | crc5 = bitstr_to_num(packet[27:31 + 1]) | |
236 | self.ss, self.es = self.bits[27][1], self.bits[31][2] | |
237 | self.putpb(['CRC5', crc5]) | |
238 | self.putb([0, ['CRC5: 0x%02x' % crc5]]) | |
239 | self.packet.append(crc5) | |
240 | elif pidname in ('DATA0', 'DATA1', 'DATA2', 'MDATA'): | |
241 | # Bits[16:packetlen-16]: Data | |
242 | data = packet[16:-16] | |
243 | # TODO: len(data) must be a multiple of 8. | |
244 | databytes = [] | |
245 | self.packet_summary += ' [' | |
246 | for i in range(0, len(data), 8): | |
247 | db = bitstr_to_num(data[i:i + 8]) | |
248 | self.ss, self.es = self.bits[16 + i][1], self.bits[23 + i][2] | |
249 | self.putpb(['DATABYTE', db]) | |
250 | self.putb([0, ['Databyte: %02x' % db]]) | |
251 | databytes.append(db) | |
252 | self.packet_summary += ' %02x' % db | |
253 | data = data[8:] | |
254 | self.packet_summary += ' ]' | |
255 | ||
256 | # Convenience proto output (no annotation) for all bytes together. | |
257 | self.ss, self.es = self.bits[16][1], self.bits[-16][2] | |
258 | self.putpb(['DATABYTES', databytes]) | |
259 | self.packet.append(databytes) | |
260 | ||
261 | # Bits[packetlen-16:packetlen]: CRC16 | |
262 | crc16 = bitstr_to_num(packet[-16:]) | |
263 | self.ss, self.es = self.bits[-16][1], self.bits[-1][2] | |
264 | self.putpb(['CRC16', crc16]) | |
265 | self.putb([0, ['CRC16: 0x%04x' % crc16]]) | |
266 | self.packet.append(crc16) | |
267 | elif pidname in ('ACK', 'NAK', 'STALL', 'NYET', 'ERR'): | |
268 | pass # Nothing to do, these only have SYNC+PID+EOP fields. | |
269 | else: | |
270 | pass # TODO: Handle 'SPLIT' and possibly 'Reserved' packets. | |
271 | ||
272 | # Output a (summary of) the whole packet. | |
273 | pcategory, pname, pinfo = get_category(pidname), pidname, self.packet | |
274 | self.putpp(['PACKET', [pcategory, pname, pinfo]]) | |
275 | self.putp([0, ['PACKET: %s' % self.packet_summary]]) | |
276 | ||
277 | self.packet, self.packet_summary = [], '' | |
278 | ||
279 | def decode(self, ss, es, data): | |
280 | (ptype, pdata) = data | |
281 | ||
282 | # We only care about certain packet types for now. | |
283 | if ptype not in ('SOP', 'BIT', 'EOP'): | |
284 | return | |
285 | ||
286 | # State machine. | |
287 | if self.state == 'WAIT FOR SOP': | |
288 | if ptype != 'SOP': | |
289 | return | |
290 | self.ss_packet = ss | |
291 | self.state = 'GET BIT' | |
292 | elif self.state == 'GET BIT': | |
293 | if ptype == 'BIT': | |
294 | self.bits.append([pdata, ss, es]) | |
295 | elif ptype == 'EOP': | |
296 | self.es_packet = es | |
297 | self.handle_packet() | |
298 | self.bits, self.state = [], 'WAIT FOR SOP' | |
299 | else: | |
300 | pass # TODO: Error | |
301 | else: | |
302 | raise Exception('Invalid state: %s' % self.state) | |
303 |