Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decoders/sdcard_spi: CMD17/CMD24 bugfix, added support for CMD12,CMD18 #87

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 127 additions & 4 deletions decoders/sdcard_spi/pd.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,47 @@
['R' + r.upper() for r in responses] + ['BIT', 'BIT_WARNING']
Ann = SrdIntEnum.from_list('Ann', a)

# CRC16 (CCITT) with an initial XOR set to 0x0
def crc16(data: bytes):
table = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
]
crc = 0
for byte in data:
crc = ( (crc << 8) ^ table[(crc >> 8) ^ byte] ) & 0xffff
return crc

class Decoder(srd.Decoder):
api_version = 3
id = 'sdcard_spi'
Expand Down Expand Up @@ -68,6 +109,8 @@ def reset(self):
self.cmd24_start_token_found = False
self.is_cmd17 = False
self.cmd17_start_token_found = False
self.is_cmd18 = False
self.cmd18_start_token_found = False
self.busy_first_byte = False

def start(self):
Expand Down Expand Up @@ -160,7 +203,7 @@ def tb(byte, bit):
self.putb([Ann.BIT_WARNING, ['End bit: %d (Warning: Must be 1!)' % bit]])

# Handle command.
if cmd in (0, 1, 9, 16, 17, 24, 41, 49, 55, 59):
if cmd in (0, 1, 9, 12, 16, 17, 18, 24, 41, 49, 55, 59):
self.state = 'HANDLE CMD%d' % cmd
self.cmd_str = '%s%d (%s)' % (s, cmd, self.cmd_name(cmd))
else:
Expand Down Expand Up @@ -211,6 +254,14 @@ def handle_cmd10(self):
self.read_buf = []
self.state = 'GET RESPONSE R1'

def handle_cmd12(self):
# CMD12: STOP_TRANSMISSION
self.putc(Ann.CMD12, 'End current multi block transfer')
self.read_buf = []
self.cmd18_start_token_found = False
self.is_cmd18 = False
self.state = 'GET RESPONSE R1'

def handle_cmd16(self):
# CMD16: SET_BLOCKLEN
self.blocklen = self.arg
Expand All @@ -224,6 +275,12 @@ def handle_cmd17(self):
self.is_cmd17 = True
self.state = 'GET RESPONSE R1'

def handle_cmd18(self):
# CMD18: READ_MULTIPLE_BLOCK
self.putc(Ann.CMD18, 'Read multiple blocks from address 0x%04x' % self.arg)
self.is_cmd18 = True
self.state = 'GET RESPONSE R1'

def handle_cmd24(self):
# CMD24: WRITE_BLOCK
self.putc(Ann.CMD24, 'Write a block to address 0x%04x' % self.arg)
Expand Down Expand Up @@ -336,7 +393,9 @@ def putbit(bit, data):

if self.is_cmd17:
self.state = 'HANDLE DATA BLOCK CMD17'
if self.is_cmd24:
elif self.is_cmd18:
self.state = 'HANDLE DATA BLOCK CMD18'
elif self.is_cmd24:
self.state = 'HANDLE DATA BLOCK CMD24'

def handle_response_r1b(self, res):
Expand Down Expand Up @@ -382,13 +441,73 @@ def handle_data_cmd17(self, miso):
self.ss_crc = self.ss
elif len(self.read_buf) == (self.blocklen + 2):
self.es_crc = self.es
# TODO: Check CRC.
self.put(self.ss_crc, self.es_crc, self.out_ann, [Ann.CMD17, ['CRC']])
block_crc = (self.read_buf[self.blocklen]<<8) | self.read_buf[self.blocklen+1]
comp_crc = crc16(self.read_buf[:self.blocklen])
if block_crc == comp_crc:
msg = "0x%04x (valid)" % block_crc
else:
msg = "0x%04x (Warning: invalid -- expected: 0x%04x)" % (block_crc, comp_crc)
self.put(self.ss_crc, self.es_crc, self.out_ann, [Ann.CMD17, ['CRC16: %s' % (msg)]])
self.state = 'IDLE'
self.read_buf = []
self.cmd17_start_token_found = False
self.is_cmd17 = False
elif miso == 0xfe:
self.put(self.ss, self.es, self.out_ann, [Ann.CMD17, ['Start Block']])
self.cmd17_start_token_found = True

def handle_data_cmd18(self, mosi, miso):
# CMD18 returns one byte R1, then some bytes 0xff, then a Start Block
# (single byte 0xfe), then [ self.blocklen bytes of data, then always
# 2 bytes of CRC. ] multiple times until a CMD12 is sent by the master.
if self.cmd18_start_token_found:
if len(self.read_buf) == 0:
self.ss_data = self.ss
if not self.blocklen:
# Assume a fixed block size when inspection of the previous
# traffic did not provide the respective parameter value.
# TODO: Make the default block size a PD option?
self.blocklen = 512
self.read_buf.append(miso)
# Wait until block transfer completed.
if len(self.read_buf) < self.blocklen:
pass
elif len(self.read_buf) == self.blocklen:
self.es_data = self.es
self.put(self.ss_data, self.es_data, self.out_ann, [Ann.CMD18, ['Block data: %s' % self.read_buf]])
elif len(self.read_buf) == (self.blocklen + 1):
self.ss_crc = self.ss
elif len(self.read_buf) == (self.blocklen + 2):
self.es_crc = self.es
block_crc = (self.read_buf[self.blocklen]<<8) | self.read_buf[self.blocklen+1]
comp_crc = crc16(self.read_buf[:self.blocklen])
if block_crc == comp_crc:
msg = "0x%04x (valid)" % block_crc
else:
msg = "0x%04x (Warning: invalid -- expected: 0x%04x)" % (block_crc, comp_crc)
self.put(self.ss_crc, self.es_crc, self.out_ann, [Ann.CMD18, ['CRC16: %s' % (msg)]])
self.read_buf = []
self.cmd18_start_token_found = False
elif miso == 0xfe:
self.put(self.ss, self.es, self.out_ann, [Ann.CMD18, ['Start Block']])
self.cmd18_start_token_found = True
elif miso != 0xff:
self.put(self.ss, self.es, self.out_ann, [Ann.CMD18, ['Warning: unexpected byte on MISO line: 0x%x (should be either 0xff or 0xfe).' % miso]])
self.state = 'IDLE'
self.read_buf = []
self.cmd18_start_token_found = False
self.is_cmd18 = False

if mosi != 0xff:
self.handle_command_token(mosi, miso)
if self.state.startswith('HANDLE CMD'):
# master has sent a new command
if self.state != 'HANDLE CMD12':
self.put(self.ss, self.es, self.out_ann, [Ann.CMD18, ['Warning: unexpected command on MISO line in the middle of a transfer (state: %s).' % self.state]])
self.read_buf = []
self.cmd18_start_token_found = False
self.is_cmd18 = False

def handle_data_cmd24(self, mosi):
if self.cmd24_start_token_found:
if len(self.read_buf) == 0:
Expand All @@ -406,6 +525,8 @@ def handle_data_cmd24(self, mosi):
self.es_data = self.es
self.put(self.ss_data, self.es_data, self.out_ann, [Ann.CMD24, ['Block data: %s' % self.read_buf]])
self.read_buf = []
self.cmd24_start_token_found = False
self.is_cmd24 = False
self.state = 'DATA RESPONSE'
elif mosi == 0xfe:
self.put(self.ss, self.es, self.out_ann, [Ann.CMD24, ['Start Block']])
Expand Down Expand Up @@ -510,6 +631,8 @@ def decode(self, ss, es, data):
handle_response(miso)
elif self.state == 'HANDLE DATA BLOCK CMD17':
self.handle_data_cmd17(miso)
elif self.state == 'HANDLE DATA BLOCK CMD18':
self.handle_data_cmd18(mosi, miso)
elif self.state == 'HANDLE DATA BLOCK CMD24':
self.handle_data_cmd24(mosi)
elif self.state == 'DATA RESPONSE':
Expand Down