]> sigrok.org Git - libsigrokdecode.git/blob - decoders/sdcard_spi/pd.py
decoders: Add/update tags for each PD.
[libsigrokdecode.git] / decoders / sdcard_spi / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2012-2014 Uwe Hermann <uwe@hermann-uwe.de>
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, see <http://www.gnu.org/licenses/>.
18 ##
19
20 import sigrokdecode as srd
21 from common.sdcard import (cmd_names, acmd_names)
22
23 class Decoder(srd.Decoder):
24     api_version = 3
25     id = 'sdcard_spi'
26     name = 'SD card (SPI mode)'
27     longname = 'Secure Digital card (SPI mode)'
28     desc = 'Secure Digital card (SPI mode) low-level protocol.'
29     license = 'gplv2+'
30     inputs = ['spi']
31     outputs = ['sdcard_spi']
32     tags = ['Memory']
33     annotations = \
34         tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \
35         tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \
36         ('r1', 'R1 reply'),
37         ('r1b', 'R1B reply'),
38         ('r2', 'R2 reply'),
39         ('r3', 'R3 reply'),
40         ('r7', 'R7 reply'),
41         ('bits', 'Bits'),
42         ('bit-warnings', 'Bit warnings'),
43     )
44     annotation_rows = (
45         ('bits', 'Bits', (134, 135)),
46         ('cmd-reply', 'Commands/replies', tuple(range(134))),
47     )
48
49     def __init__(self):
50         self.reset()
51
52     def reset(self):
53         self.state = 'IDLE'
54         self.ss, self.es = 0, 0
55         self.ss_bit, self.es_bit = 0, 0
56         self.ss_cmd, self.es_cmd = 0, 0
57         self.cmd_token = []
58         self.cmd_token_bits = []
59         self.is_acmd = False # Indicates CMD vs. ACMD
60         self.blocklen = 0
61         self.read_buf = []
62         self.cmd_str = ''
63
64     def start(self):
65         self.out_ann = self.register(srd.OUTPUT_ANN)
66
67     def putx(self, data):
68         self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
69
70     def putc(self, cmd, desc):
71         self.putx([cmd, ['%s: %s' % (self.cmd_str, desc)]])
72
73     def putb(self, data):
74         self.put(self.ss_bit, self.es_bit, self.out_ann, data)
75
76     def cmd_name(self, cmd):
77         c = acmd_names if self.is_acmd else cmd_names
78         s = c.get(cmd, 'Unknown')
79         # SD mode names for CMD32/33: ERASE_WR_BLK_{START,END}.
80         # SPI mode names for CMD32/33: ERASE_WR_BLK_{START,END}_ADDR.
81         if cmd in (32, 33):
82             s += '_ADDR'
83         return s
84
85     def handle_command_token(self, mosi, miso):
86         # Command tokens (6 bytes) are sent (MSB-first) by the host.
87         #
88         # Format:
89         #  - CMD[47:47]: Start bit (always 0)
90         #  - CMD[46:46]: Transmitter bit (1 == host)
91         #  - CMD[45:40]: Command index (BCD; valid: 0-63)
92         #  - CMD[39:08]: Argument
93         #  - CMD[07:01]: CRC7
94         #  - CMD[00:00]: End bit (always 1)
95
96         if len(self.cmd_token) == 0:
97             self.ss_cmd = self.ss
98
99         self.cmd_token.append(mosi)
100         self.cmd_token_bits.append(self.mosi_bits)
101
102         # All command tokens are 6 bytes long.
103         if len(self.cmd_token) < 6:
104             return
105
106         self.es_cmd = self.es
107
108         t = self.cmd_token
109
110         # CMD or ACMD?
111         s = 'ACMD' if self.is_acmd else 'CMD'
112
113         def tb(byte, bit):
114             return self.cmd_token_bits[5 - byte][bit]
115
116         # Bits[47:47]: Start bit (always 0)
117         bit, self.ss_bit, self.es_bit = tb(5, 7)[0], tb(5, 7)[1], tb(5, 7)[2]
118         if bit == 0:
119             self.putb([134, ['Start bit: %d' % bit]])
120         else:
121             self.putb([135, ['Start bit: %s (Warning: Must be 0!)' % bit]])
122
123         # Bits[46:46]: Transmitter bit (1 == host)
124         bit, self.ss_bit, self.es_bit = tb(5, 6)[0], tb(5, 6)[1], tb(5, 6)[2]
125         if bit == 1:
126             self.putb([134, ['Transmitter bit: %d' % bit]])
127         else:
128             self.putb([135, ['Transmitter bit: %d (Warning: Must be 1!)' % bit]])
129
130         # Bits[45:40]: Command index (BCD; valid: 0-63)
131         cmd = self.cmd_index = t[0] & 0x3f
132         self.ss_bit, self.es_bit = tb(5, 5)[1], tb(5, 0)[2]
133         self.putb([134, ['Command: %s%d (%s)' % (s, cmd, self.cmd_name(cmd))]])
134
135         # Bits[39:8]: Argument
136         self.arg = (t[1] << 24) | (t[2] << 16) | (t[3] << 8) | t[4]
137         self.ss_bit, self.es_bit = tb(4, 7)[1], tb(1, 0)[2]
138         self.putb([134, ['Argument: 0x%04x' % self.arg]])
139
140         # Bits[7:1]: CRC7
141         # TODO: Check CRC7.
142         crc = t[5] >> 1
143         self.ss_bit, self.es_bit = tb(0, 7)[1], tb(0, 1)[2]
144         self.putb([134, ['CRC7: 0x%01x' % crc]])
145
146         # Bits[0:0]: End bit (always 1)
147         bit, self.ss_bit, self.es_bit = tb(0, 0)[0], tb(0, 0)[1], tb(0, 0)[2]
148         self.putb([134, ['End bit: %d' % bit]])
149         if bit == 1:
150             self.putb([134, ['End bit: %d' % bit]])
151         else:
152             self.putb([135, ['End bit: %d (Warning: Must be 1!)' % bit]])
153
154         # Handle command.
155         if cmd in (0, 1, 9, 16, 17, 41, 49, 55, 59):
156             self.state = 'HANDLE CMD%d' % cmd
157             self.cmd_str = '%s%d (%s)' % (s, cmd, self.cmd_name(cmd))
158         else:
159             self.state = 'HANDLE CMD999'
160             a = '%s%d: %02x %02x %02x %02x %02x %02x' % ((s, cmd) + tuple(t))
161             self.putx([cmd, [a]])
162
163     def handle_cmd0(self):
164         # CMD0: GO_IDLE_STATE
165         self.putc(0, 'Reset the SD card')
166         self.state = 'GET RESPONSE R1'
167
168     def handle_cmd1(self):
169         # CMD1: SEND_OP_COND
170         self.putc(1, 'Send HCS info and activate the card init process')
171         hcs = (self.arg & (1 << 30)) >> 30
172         self.ss_bit = self.cmd_token_bits[5 - 4][6][1]
173         self.es_bit = self.cmd_token_bits[5 - 4][6][2]
174         self.putb([134, ['HCS: %d' % hcs]])
175         self.state = 'GET RESPONSE R1'
176
177     def handle_cmd9(self):
178         # CMD9: SEND_CSD (128 bits / 16 bytes)
179         self.putc(9, 'Ask card to send its card specific data (CSD)')
180         if len(self.read_buf) == 0:
181             self.ss_cmd = self.ss
182         self.read_buf.append(self.miso)
183         # FIXME
184         ### if len(self.read_buf) < 16:
185         if len(self.read_buf) < 16 + 4:
186             return
187         self.es_cmd = self.es
188         self.read_buf = self.read_buf[4:] # TODO: Document or redo.
189         self.putx([9, ['CSD: %s' % self.read_buf]])
190         # TODO: Decode all bits.
191         self.read_buf = []
192         ### self.state = 'GET RESPONSE R1'
193         self.state = 'IDLE'
194
195     def handle_cmd10(self):
196         # CMD10: SEND_CID (128 bits / 16 bytes)
197         self.putc(10, 'Ask card to send its card identification (CID)')
198         self.read_buf.append(self.miso)
199         if len(self.read_buf) < 16:
200             return
201         self.putx([10, ['CID: %s' % self.read_buf]])
202         # TODO: Decode all bits.
203         self.read_buf = []
204         self.state = 'GET RESPONSE R1'
205
206     def handle_cmd16(self):
207         # CMD16: SET_BLOCKLEN
208         self.blocklen = self.arg
209         # TODO: Sanity check on block length.
210         self.putc(16, 'Set the block length to %d bytes' % self.blocklen)
211         self.state = 'GET RESPONSE R1'
212
213     def handle_cmd17(self):
214         # CMD17: READ_SINGLE_BLOCK
215         self.putc(17, 'Read a block from address 0x%04x' % self.arg)
216         if len(self.read_buf) == 0:
217             self.ss_cmd = self.ss
218         self.read_buf.append(self.miso)
219         if len(self.read_buf) < self.blocklen + 2: # FIXME
220             return
221         self.es_cmd = self.es
222         self.read_buf = self.read_buf[2:] # FIXME
223         self.putx([17, ['Block data: %s' % self.read_buf]])
224         self.read_buf = []
225         self.state = 'GET RESPONSE R1'
226
227     def handle_cmd49(self):
228         self.state = 'GET RESPONSE R1'
229
230     def handle_cmd55(self):
231         # CMD55: APP_CMD
232         self.putc(55, 'Next command is an application-specific command')
233         self.is_acmd = True
234         self.state = 'GET RESPONSE R1'
235
236     def handle_cmd59(self):
237         # CMD59: CRC_ON_OFF
238         crc_on_off = self.arg & (1 << 0)
239         s = 'on' if crc_on_off == 1 else 'off'
240         self.putc(59, 'Turn the SD card CRC option %s' % s)
241         self.state = 'GET RESPONSE R1'
242
243     def handle_acmd41(self):
244         # ACMD41: SD_SEND_OP_COND
245         self.putc(64 + 41, 'Send HCS info and activate the card init process')
246         self.state = 'GET RESPONSE R1'
247
248     def handle_cmd999(self):
249         self.state = 'GET RESPONSE R1'
250
251     def handle_cid_register(self):
252         # Card Identification (CID) register, 128bits
253
254         cid = self.cid
255
256         # Manufacturer ID: CID[127:120] (8 bits)
257         mid = cid[15]
258
259         # OEM/Application ID: CID[119:104] (16 bits)
260         oid = (cid[14] << 8) | cid[13]
261
262         # Product name: CID[103:64] (40 bits)
263         pnm = 0
264         for i in range(12, 8 - 1, -1):
265             pnm <<= 8
266             pnm |= cid[i]
267
268         # Product revision: CID[63:56] (8 bits)
269         prv = cid[7]
270
271         # Product serial number: CID[55:24] (32 bits)
272         psn = 0
273         for i in range(6, 3 - 1, -1):
274             psn <<= 8
275             psn |= cid[i]
276
277         # RESERVED: CID[23:20] (4 bits)
278
279         # Manufacturing date: CID[19:8] (12 bits)
280         # TODO
281
282         # CRC7 checksum: CID[7:1] (7 bits)
283         # TODO
284
285         # Not used, always 1: CID[0:0] (1 bit)
286         # TODO
287
288     def handle_response_r1(self, res):
289         # The R1 response token format (1 byte).
290         # Sent by the card after every command except for SEND_STATUS.
291
292         self.ss_cmd, self.es_cmd = self.miso_bits[7][1], self.miso_bits[0][2]
293         self.putx([65, ['R1: 0x%02x' % res]])
294
295         def putbit(bit, data):
296             b = self.miso_bits[bit]
297             self.ss_bit, self.es_bit = b[1], b[2]
298             self.putb([134, data])
299
300         # Bit 0: 'In idle state' bit
301         s = '' if (res & (1 << 0)) else 'not '
302         putbit(0, ['Card is %sin idle state' % s])
303
304         # Bit 1: 'Erase reset' bit
305         s = '' if (res & (1 << 1)) else 'not '
306         putbit(1, ['Erase sequence %scleared' % s])
307
308         # Bit 2: 'Illegal command' bit
309         s = 'I' if (res & (1 << 2)) else 'No i'
310         putbit(2, ['%sllegal command detected' % s])
311
312         # Bit 3: 'Communication CRC error' bit
313         s = 'failed' if (res & (1 << 3)) else 'was successful'
314         putbit(3, ['CRC check of last command %s' % s])
315
316         # Bit 4: 'Erase sequence error' bit
317         s = 'E' if (res & (1 << 4)) else 'No e'
318         putbit(4, ['%srror in the sequence of erase commands' % s])
319
320         # Bit 5: 'Address error' bit
321         s = 'M' if (res & (1 << 4)) else 'No m'
322         putbit(5, ['%sisaligned address used in command' % s])
323
324         # Bit 6: 'Parameter error' bit
325         s = '' if (res & (1 << 4)) else 'not '
326         putbit(6, ['Command argument %soutside allowed range' % s])
327
328         # Bit 7: Always set to 0
329         putbit(7, ['Bit 7 (always 0)'])
330
331         self.state = 'IDLE'
332
333     def handle_response_r1b(self, res):
334         # TODO
335         pass
336
337     def handle_response_r2(self, res):
338         # TODO
339         pass
340
341     def handle_response_r3(self, res):
342         # TODO
343         pass
344
345     # Note: Response token formats R4 and R5 are reserved for SDIO.
346
347     # TODO: R6?
348
349     def handle_response_r7(self, res):
350         # TODO
351         pass
352
353     def decode(self, ss, es, data):
354         ptype, mosi, miso = data
355
356         # For now, only use DATA and BITS packets.
357         if ptype not in ('DATA', 'BITS'):
358             return
359
360         # Store the individual bit values and ss/es numbers. The next packet
361         # is guaranteed to be a 'DATA' packet belonging to this 'BITS' one.
362         if ptype == 'BITS':
363             self.miso_bits, self.mosi_bits = miso, mosi
364             return
365
366         self.ss, self.es = ss, es
367
368         # State machine.
369         if self.state == 'IDLE':
370             # Ignore stray 0xff bytes, some devices seem to send those!?
371             if mosi == 0xff: # TODO?
372                 return
373             self.state = 'GET COMMAND TOKEN'
374             self.handle_command_token(mosi, miso)
375         elif self.state == 'GET COMMAND TOKEN':
376             self.handle_command_token(mosi, miso)
377         elif self.state.startswith('HANDLE CMD'):
378             self.miso, self.mosi = miso, mosi
379             # Call the respective handler method for the command.
380             a, cmdstr = 'a' if self.is_acmd else '', self.state[10:].lower()
381             handle_cmd = getattr(self, 'handle_%scmd%s' % (a, cmdstr))
382             handle_cmd()
383             self.cmd_token = []
384             self.cmd_token_bits = []
385             # Leave ACMD mode again after the first command after CMD55.
386             if self.is_acmd and cmdstr != '55':
387                 self.is_acmd = False
388         elif self.state.startswith('GET RESPONSE'):
389             # Ignore stray 0xff bytes, some devices seem to send those!?
390             if miso == 0xff: # TODO?
391                 return
392
393             # Call the respective handler method for the response.
394             s = 'handle_response_%s' % self.state[13:].lower()
395             handle_response = getattr(self, s)
396             handle_response(miso)
397
398             self.state = 'IDLE'