From: Uwe Hermann Date: Thu, 6 Aug 2015 17:27:04 +0000 (+0200) Subject: Add initial SD card (SD mode) decoder. X-Git-Tag: libsigrokdecode-0.4.0~101 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=08c41387ca016a59c041c8bc76efab037da5894a;p=libsigrokdecode.git Add initial SD card (SD mode) decoder. In the current state the decoder can decode the following commands: CMD0, CMD2, CMD3, CMD6, CMD7, CMD8, CMD9, CMD10, CMD13, CMD16, CMD55, ACMD6, ACMD13, ACMD41, ACMD51. Other commands (and more details for existing commands) will be added later. --- diff --git a/decoders/sdcard_sd/__init__.py b/decoders/sdcard_sd/__init__.py new file mode 100644 index 0000000..a05f21a --- /dev/null +++ b/decoders/sdcard_sd/__init__.py @@ -0,0 +1,25 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2015 Uwe Hermann +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +''' +SD card (SD mode) low-level protocol decoder. +''' + +from .pd import Decoder diff --git a/decoders/sdcard_sd/lists.py b/decoders/sdcard_sd/lists.py new file mode 100644 index 0000000..479c9f3 --- /dev/null +++ b/decoders/sdcard_sd/lists.py @@ -0,0 +1,185 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2015 Uwe Hermann +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +# Normal commands (CMD) +# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't +# seem to be mentioned in the spec, but aren't marked as reserved either. +cmd_names = { + 0: 'GO_IDLE_STATE', + # 1: Reserved + 2: 'ALL_SEND_CID', + 3: 'SEND_RELATIVE_ADDR', + 4: 'SET_DSR', + 5: 'IO_SEND_OP_COND', # SDIO-only + 6: 'SWITCH_FUNC', # New since spec 1.10 + 7: 'SELECT/DESELECT_CARD', + 8: 'SEND_IF_COND', + 9: 'SEND_CSD', + 10: 'SEND_CID', + 11: 'VOLTAGE_SWITCH', + 12: 'STOP_TRANSMISSION', + 13: 'SEND_STATUS', + # 14: Reserved + 15: 'GO_INACTIVE_STATE', + 16: 'SET_BLOCKLEN', + 17: 'READ_SINGLE_BLOCK', + 18: 'READ_MULTIPLE_BLOCK', + 19: 'SEND_TUNING_BLOCK', + 20: 'SPEED_CLASS_CONTROL', + # 21-22: Reserved + 23: 'SET_BLOCK_COUNT', + 24: 'WRITE_BLOCK', + 25: 'WRITE_MULTIPLE_BLOCK', + 26: 'Reserved for manufacturer', + 27: 'PROGRAM_CSD', + 28: 'SET_WRITE_PROT', + 29: 'CLR_WRITE_PROT', + 30: 'SEND_WRITE_PROT', + # 31: Reserved + 32: 'ERASE_WR_BLK_START', + 33: 'ERASE_WR_BLK_END', + 34: 'Reserved for CMD6', # New since spec 1.10 + 35: 'Reserved for CMD6', # New since spec 1.10 + 36: 'Reserved for CMD6', # New since spec 1.10 + 37: 'Reserved for CMD6', # New since spec 1.10 + 38: 'ERASE', + # 39: Reserved + 40: 'Reserved for security specification', + # 41: Reserved + 42: 'LOCK_UNLOCK', + # 43-49: Reserved + 50: 'Reserved for CMD6', # New since spec 1.10 + # 51: Reserved + 52: 'IO_RW_DIRECT', # SDIO-only + 53: 'IO_RW_EXTENDED', # SDIO-only + 54: 'Unknown', + 55: 'APP_CMD', + 56: 'GEN_CMD', + 57: 'Reserved for CMD6', # New since spec 1.10 + # 58-59: Reserved + 60: 'Reserved for manufacturer', + 61: 'Reserved for manufacturer', + 62: 'Reserved for manufacturer', + 63: 'Reserved for manufacturer', +} + +# Application-specific commands (ACMD) +# Unlisted items are 'Reserved' as per SD spec. The 'Unknown' items don't +# seem to be mentioned in the spec, but aren't marked as reserved either. +acmd_names = { + # 1-5: Reserved + 6: 'SET_BUS_WIDTH', + # 7-12: Reserved + 13: 'SD_STATUS', + 14: 'Reserved for Security Application', + 15: 'Reserved for Security Application', + 16: 'Reserved for Security Application', + # 17: Reserved + 18: 'Reserved for SD security applications', + # 19-21: Reserved + 22: 'SEND_NUM_WR_BLOCKS', + 23: 'SET_WR_BLK_ERASE_COUNT', + # 24: Reserved + 25: 'Reserved for SD security applications', + 26: 'Reserved for SD security applications', + 27: 'Reserved for security specification', + 28: 'Reserved for security specification', + # 29: Reserved + 30: 'Reserved for security specification', + 31: 'Reserved for security specification', + 32: 'Reserved for security specification', + 33: 'Reserved for security specification', + 34: 'Reserved for security specification', + 35: 'Reserved for security specification', + # 36-37: Reserved + 38: 'Reserved for SD security applications', + # 39-40: Reserved + 41: 'SD_SEND_OP_COND', + 42: 'SET_CLR_CARD_DETECT', + 43: 'Reserved for SD security applications', + 44: 'Reserved for SD security applications', + 45: 'Reserved for SD security applications', + 46: 'Reserved for SD security applications', + 47: 'Reserved for SD security applications', + 48: 'Reserved for SD security applications', + 49: 'Reserved for SD security applications', + 50: 'Unknown', + 51: 'SEND_SCR', + 52: 'Reserved for security specification', + 53: 'Reserved for security specification', + 54: 'Reserved for security specification', + 55: 'Non-existant', # Doesn't exist (equivalent to CMD55) + 56: 'Reserved for security specification', + 57: 'Reserved for security specification', + 58: 'Reserved for security specification', + 59: 'Reserved for security specification', + 60: 'Unknown', + 61: 'Unknown', + 62: 'Unknown', + 63: 'Unknown', +} + +accepted_voltages = { + 0b0001: '2.7-3.6V', + 0b0010: 'reserved for low voltage range', + 0b0100: 'reserved', + 0b1000: 'reserved', + # All other values: "not defined". +} + +card_status = { + 0: 'Reserved for manufacturer test mode', + 1: 'Reserved for manufacturer test mode', + 2: 'Reserved for application specific commands', + 3: 'AKE_SEQ_ERROR', + 4: 'Reserved for SDIO card', + 5: 'APP_CMD', + 6: 'Unknown', + 7: 'Unknown', + 8: 'READY_FOR_DATA', + 9: 'CURRENT_STATE', # CURRENT_STATE is a 4-bit value (decimal: 0..15). + 10: 'CURRENT_STATE', + 11: 'CURRENT_STATE', + 12: 'CURRENT_STATE', + 13: 'ERASE_RESET', + 14: 'CARD_ECC_DISABLED', + 15: 'WP_ERASE_SKIP', + 16: 'CSD_OVERWRITE', + 17: 'Reserved for DEFERRED_RESPONSE', # See eSD addendum + 18: 'Reserved', + 19: 'ERROR', + 20: 'CC_ERROR', + 21: 'CARD_ECC_FAILED', + 22: 'ILLEGAL_COMMAND', + 23: 'COM_CRC_ERROR', + 24: 'LOCK_UNLOCK_FAILED', + 25: 'CARD_IS_LOCKED', + 26: 'WP_VIOLATION', + 27: 'ERASE_PARAM', + 28: 'ERASE_SEQ_ERROR', + 29: 'BLOCK_LEN_ERROR', + 30: 'ADDRESS_ERROR', + 31: 'OUT_OF_RANGE', +} + +sd_status = { + # 311:0: Reserved for manufacturer + # 391:312: Reserved +} diff --git a/decoders/sdcard_sd/pd.py b/decoders/sdcard_sd/pd.py new file mode 100644 index 0000000..a1eac2b --- /dev/null +++ b/decoders/sdcard_sd/pd.py @@ -0,0 +1,444 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2015 Uwe Hermann +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +import sigrokdecode as srd +from .lists import * + +class Decoder(srd.Decoder): + api_version = 2 + id = 'sdcard_sd' + name = 'SD card (SD mode)' + longname = 'Secure Digital card (SD mode)' + desc = 'Secure Digital card (SD mode) low-level protocol.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['sdcard_sd'] + channels = ( + {'id': 'cmd', 'name': 'CMD', 'desc': 'Command'}, + {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'}, + ) + optional_channels = ( + {'id': 'dat0', 'name': 'DAT0', 'desc': 'Data pin 0'}, + {'id': 'dat1', 'name': 'DAT1', 'desc': 'Data pin 1'}, + {'id': 'dat2', 'name': 'DAT2', 'desc': 'Data pin 2'}, + {'id': 'dat3', 'name': 'DAT3', 'desc': 'Data pin 3'}, + ) + annotations = \ + tuple(('cmd%d' % i, 'CMD%d' % i) for i in range(64)) + \ + tuple(('acmd%d' % i, 'ACMD%d' % i) for i in range(64)) + ( \ + ('bits', 'Bits'), + ('field-start', 'Start bit'), + ('field-transmission', 'Transmission bit'), + ('field-cmd', 'Command'), + ('field-arg', 'Argument'), + ('field-crc', 'CRC'), + ('field-end', 'End bit'), + ('decoded-bits', 'Decoded bits'), + ('decoded-fields', 'Decoded fields'), + ) + annotation_rows = ( + ('raw-bits', 'Raw bits', (128,)), + ('decoded-bits', 'Decoded bits', (135,)), + ('decoded-fields', 'Decoded fields', (136,)), + ('fields', 'Fields', tuple(range(129, 135))), + ('cmd', 'Commands', tuple(range(128))), + ) + + def __init__(self, **kwargs): + self.state = 'GET COMMAND TOKEN' + self.token = [] + self.oldpins = None + self.oldclk = 0 + self.is_acmd = False # Indicates CMD vs. ACMD + self.cmd = None + self.arg = None + + def start(self): + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putbit(self, b, data): + self.put(self.token[b][0], self.token[b][1], self.out_ann, [135, data]) + + def putt(self, data): + self.put(self.token[0][0], self.token[47][1], self.out_ann, data) + + def putt2(self, data): + self.put(self.token[47][0], self.token[0][1], self.out_ann, data) + + def putf(self, s, e, data): + self.put(self.token[s][0], self.token[e][1], self.out_ann, data) + + def puta(self, s, e, data): + self.put(self.token[47 - 8 - e][0], self.token[47 - 8 - s][1], + self.out_ann, data) + + def putc(self, cmd, desc): + self.putt([cmd, ['%s: %s' % (self.cmd_str, desc), self.cmd_str, + self.cmd_str.split(' ')[0]]]) + + def putr(self, cmd, desc): + self.putt([cmd, ['Reply: %s' % desc]]) + + def putr2(self, cmd, desc): + self.putt2([cmd, ['Reply: %s' % desc]]) + + def reset(self): + self.cmd, self.arg = None, None + self.token, self.state = [], 'GET COMMAND TOKEN' + + def cmd_name(self, cmd): + c = acmd_names if self.is_acmd else cmd_names + return c.get(cmd, 'Unknown') + + def get_token_bits(self, cmd, n): + # Get a bit, return True if we already got 'n' bits, False otherwise. + self.token.append([self.samplenum, self.samplenum, cmd]) + if len(self.token) > 0: + self.token[len(self.token) - 2][1] = self.samplenum + if len(self.token) < n: + return False + self.token[n - 1][1] += self.token[n - 1][0] - self.token[n - 2][0] + return True + + def handle_common_token_fields(self): + s = self.token + + # Annotations for each individual bit. + for bit in range(len(self.token)): + self.putf(bit, bit, [128, ['%d' % s[bit][2]]]) + + # CMD[47:47]: Start bit (always 0) + self.putf(0, 0, [129, ['Start bit', 'Start', 'S']]) + + # CMD[46:46]: Transmission bit (1 == host) + t = 'host' if s[1][2] == 1 else 'card' + self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']]) + + # CMD[45:40]: Command index (BCD; valid: 0-63) + self.cmd = int('0b' + ''.join([str(s[i][2]) for i in range(2, 8)]), 2) + c = '%s (%d)' % (self.cmd_name(self.cmd), self.cmd) + self.putf(2, 7, [131, ['Command: ' + c, 'Cmd: ' + c, + 'CMD%d' % self.cmd, 'Cmd', 'C']]) + + # CMD[39:08]: Argument + self.putf(8, 39, [132, ['Argument', 'Arg', 'A']]) + + # CMD[07:01]: CRC7 + self.crc = int('0b' + ''.join([str(s[i][2]) for i in range(40, 47)]), 2) + self.putf(40, 46, [133, ['CRC: 0x%x' % self.crc, 'CRC', 'C']]) + + # CMD[00:00]: End bit (always 1) + self.putf(47, 47, [134, ['End bit', 'End', 'E']]) + + def get_command_token(self, cmd): + # Command tokens (48 bits) are sent serially (MSB-first) by the host + # (over the CMD line), either to one SD card or to multiple ones. + # + # Format: + # - Bits[47:47]: Start bit (always 0) + # - Bits[46:46]: Transmission bit (1 == host) + # - Bits[45:40]: Command index (BCD; valid: 0-63) + # - Bits[39:08]: Argument + # - Bits[07:01]: CRC7 + # - Bits[00:00]: End bit (always 1) + + if not self.get_token_bits(cmd, 48): + return + + self.handle_common_token_fields() + + # Handle command. + s = 'ACMD' if self.is_acmd else 'CMD' + self.cmd_str = '%s%d (%s)' % (s, self.cmd, self.cmd_name(self.cmd)) + if self.cmd in (0, 2, 3, 4, 6, 7, 8, 9, 10, 13, 41, 51, 55): + self.state = 'HANDLE CMD%d' % self.cmd + else: + self.state = 'HANDLE CMD999' + self.putc(self.cmd, '%s%d' % (s, self.cmd)) + + def handle_cmd0(self): + # CMD0 (GO_IDLE_STATE) -> no response + self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(0, 'Reset all SD cards') + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_cmd2(self): + # CMD2 (ALL_SEND_CID) -> R2 + self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(2, 'Ask card for CID number') + self.token, self.state = [], 'GET RESPONSE R2' + + def handle_cmd3(self): + # CMD3 (SEND_RELATIVE_ADDR) -> R6 + self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(3, 'Ask card for new relative card address (RCA)') + self.token, self.state = [], 'GET RESPONSE R6' + + def handle_cmd6(self): + # CMD6 (SWITCH_FUNC) -> R1 + self.putc(6, 'Switch/check card function') + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_cmd7(self): + # CMD7 (SELECT/DESELECT_CARD) -> R1b + self.putc(7, 'Select / deselect card') + self.token, self.state = [], 'GET RESPONSE R6' + + def handle_cmd8(self): + # CMD8 (SEND_IF_COND) -> R7 + self.puta(12, 31, [136, ['Reserved', 'Res', 'R']]) + self.puta(8, 11, [136, ['Supply voltage', 'Voltage', 'VHS', 'V']]) + self.puta(0, 7, [136, ['Check pattern', 'Check pat', 'Check', 'C']]) + self.putc(0, 'Send interface condition to card') + self.token, self.state = [], 'GET RESPONSE R7' + # TODO: Handle case when card doesn't reply with R7 (no reply at all). + + def handle_cmd9(self): + # CMD9 (SEND_CSD) -> R2 + self.puta(16, 31, [136, ['RCA', 'R']]) + self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(9, 'Send card-specific data (CSD)') + self.token, self.state = [], 'GET RESPONSE R2' + + def handle_cmd10(self): + # CMD10 (SEND_CID) -> R2 + self.puta(16, 31, [136, ['RCA', 'R']]) + self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(9, 'Send card identification data (CID)') + self.token, self.state = [], 'GET RESPONSE R2' + + def handle_cmd13(self): + # CMD13 (SEND_STATUS) -> R1 + self.puta(16, 31, [136, ['RCA', 'R']]) + self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(13, 'Send card status register') + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_cmd16(self): + # CMD16 (SET_BLOCKLEN) -> R1 + self.blocklen = self.arg + self.puta(0, 31, [136, ['Block length', 'Blocklen', 'BL', 'B']]) + self.putc(16, 'Set the block length to %d bytes' % self.blocklen) + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_cmd55(self): + # CMD55 (APP_CMD) -> R1 + self.puta(16, 31, [136, ['RCA', 'R']]) + self.puta(0, 15, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(55, 'Next command is an application-specific command') + self.is_acmd = True + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_acmd6(self): + # ACMD6 (SET_BUS_WIDTH) -> R1 + self.putc(64 + 6, 'Read SD config register (SCR)') + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_acmd13(self): + # ACMD13 (SD_STATUS) -> R1 + self.puta(0, 31, [136, ['Stuff bits', 'Stuff', 'SB', 'S']]) + self.putc(64 + 13, 'Set SD status') + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_acmd41(self): + # ACMD41 (SD_SEND_OP_COND) -> R3 + self.puta(0, 23, [136, ['VDD voltage window', 'VDD volt', 'VDD', 'V']]) + self.puta(24, 24, [136, ['S18R']]) + self.puta(25, 27, [136, ['Reserved', 'Res', 'R']]) + self.puta(28, 28, [136, ['XPC']]) + self.puta(29, 29, [136, ['Reserved for eSD', 'Reserved', 'Res', 'R']]) + self.puta(30, 30, [136, ['Host capacity support info', 'Host capacity', + 'HCS', 'H']]) + self.puta(31, 31, [136, ['Reserved', 'Res', 'R']]) + self.putc(64 + 41, 'Send HCS info and activate the card init process') + self.token, self.state = [], 'GET RESPONSE R3' + + def handle_acmd51(self): + # ACMD51 (SEND_SCR) -> R1 + self.putc(64 + 51, 'Read SD config register (SCR)') + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_cmd999(self): + self.token, self.state = [], 'GET RESPONSE R1' + + def handle_acmd999(self): + self.token, self.state = [], 'GET RESPONSE R1' + + # Response tokens can have one of four formats (depends on content). + # They can have a total length of 48 or 136 bits. + # They're sent serially (MSB-first) by the card that the host + # addressed previously, or (synchronously) by all connected cards. + + def handle_response_r1(self, cmd): + # R1: Normal response command + # - Bits[47:47]: Start bit (always 0) + # - Bits[46:46]: Transmission bit (0 == card) + # - Bits[45:40]: Command index (BCD; valid: 0-63) + # - Bits[39:08]: Card status + # - Bits[07:01]: CRC7 + # - Bits[00:00]: End bit (always 1) + if not self.get_token_bits(cmd, 48): + return + self.handle_common_token_fields() + self.putr(55, 'R1') + self.puta(0, 31, [136, ['Card status', 'Status', 'S']]) + for i in range(32): + self.putbit(8 + i, [card_status[31 - i]]) + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_response_r1b(self, cmd): + # R1b: Same as R1 with an optional busy signal (on the data line) + if not self.get_token_bits(cmd, 48): + return + self.handle_common_token_fields() + self.puta(0, 31, [136, ['Card status', 'Status', 'S']]) + self.putr(55, 'R1b') + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_response_r2(self, cmd): + # R2: CID/CSD register + # - Bits[135:135]: Start bit (always 0) + # - Bits[134:134]: Transmission bit (0 == card) + # - Bits[133:128]: Reserved (always 0b111111) + # - Bits[127:001]: CID or CSD register including internal CRC7 + # - Bits[000:000]: End bit (always 1) + if not self.get_token_bits(cmd, 136): + return + # Annotations for each individual bit. + for bit in range(len(self.token)): + self.putf(bit, bit, [128, ['%d' % self.token[bit][2]]]) + self.putf(0, 0, [129, ['Start bit', 'Start', 'S']]) + t = 'host' if self.token[1][2] == 1 else 'card' + self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']]) + self.putf(2, 7, [131, ['Reserved', 'Res', 'R']]) + self.putf(8, 134, [132, ['Argument', 'Arg', 'A']]) + self.putf(135, 135, [134, ['End bit', 'End', 'E']]) + self.putf(8, 134, [136, ['CID/CSD register', 'CID/CSD', 'C']]) + self.putf(0, 135, [55, ['R2']]) + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_response_r3(self, cmd): + # R3: OCR register + # - Bits[47:47]: Start bit (always 0) + # - Bits[46:46]: Transmission bit (0 == card) + # - Bits[45:40]: Reserved (always 0b111111) + # - Bits[39:08]: OCR register + # - Bits[07:01]: Reserved (always 0b111111) + # - Bits[00:00]: End bit (always 1) + if not self.get_token_bits(cmd, 48): + return + self.putr(55, 'R3') + # Annotations for each individual bit. + for bit in range(len(self.token)): + self.putf(bit, bit, [128, ['%d' % self.token[bit][2]]]) + self.putf(0, 0, [129, ['Start bit', 'Start', 'S']]) + t = 'host' if self.token[1][2] == 1 else 'card' + self.putf(1, 1, [130, ['Transmission: ' + t, 'T: ' + t, 'T']]) + self.putf(2, 7, [131, ['Reserved', 'Res', 'R']]) + self.putf(8, 39, [132, ['Argument', 'Arg', 'A']]) + self.putf(40, 46, [133, ['Reserved', 'Res', 'R']]) + self.putf(47, 47, [134, ['End bit', 'End', 'E']]) + self.puta(0, 31, [136, ['OCR register', 'OCR reg', 'OCR', 'O']]) + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_response_r6(self, cmd): + # R6: Published RCA response + # - Bits[47:47]: Start bit (always 0) + # - Bits[46:46]: Transmission bit (0 == card) + # - Bits[45:40]: Command index (always 0b000011) + # - Bits[39:24]: Argument[31:16]: New published RCA of the card + # - Bits[23:08]: Argument[15:0]: Card status bits + # - Bits[07:01]: CRC7 + # - Bits[00:00]: End bit (always 1) + if not self.get_token_bits(cmd, 48): + return + self.handle_common_token_fields() + self.puta(0, 15, [136, ['Card status bits', 'Status', 'S']]) + self.puta(16, 31, [136, ['Relative card address', 'RCA', 'R']]) + self.putr(55, 'R6') + self.token, self.state = [], 'GET COMMAND TOKEN' + + def handle_response_r7(self, cmd): + # R7: Card interface condition + # - Bits[47:47]: Start bit (always 0) + # - Bits[46:46]: Transmission bit (0 == card) + # - Bits[45:40]: Command index (always 0b001000) + # - Bits[39:20]: Reserved bits (all-zero) + # - Bits[19:16]: Voltage accepted + # - Bits[15:08]: Echo-back of check pattern + # - Bits[07:01]: CRC7 + # - Bits[00:00]: End bit (always 1) + if not self.get_token_bits(cmd, 48): + return + self.handle_common_token_fields() + + self.putr(55, 'R7') + + # Arg[31:12]: Reserved bits (all-zero) + self.puta(12, 31, [136, ['Reserved', 'Res', 'R']]) + + # Arg[11:08]: Voltage accepted + v = ''.join(str(i[2]) for i in self.token[28:32]) + av = accepted_voltages.get(int('0b' + v, 2), 'Unknown') + self.puta(8, 11, [136, ['Voltage accepted: ' + av, 'Voltage', 'Volt', 'V']]) + + # Arg[07:00]: Echo-back of check pattern + self.puta(0, 7, [136, ['Echo-back of check pattern', 'Echo', 'E']]) + + self.token, self.state = [], 'GET COMMAND TOKEN' + + def decode(self, ss, es, data): + for (self.samplenum, pins) in data: + + # Ignore identical samples early on (for performance reasons). + if self.oldpins == pins: + continue + self.oldpins, (cmd, clk, dat0, dat1, dat2, dat3) = pins, pins + + # Wait for a rising CLK edge. + if not (self.oldclk == 0 and clk == 1): + self.oldclk = clk + continue + self.oldclk = clk + + # State machine. + if self.state == 'GET COMMAND TOKEN': + if len(self.token) == 0: + # Wait for start bit (CMD = 0). + if cmd != 0: + continue + self.get_command_token(cmd) + elif self.state.startswith('HANDLE CMD'): + # Call the respective handler method for the command. + a, cmdstr = 'a' if self.is_acmd else '', self.state[10:].lower() + handle_cmd = getattr(self, 'handle_%scmd%s' % (a, cmdstr)) + handle_cmd() + # Leave ACMD mode again after the first command after CMD55. + if self.is_acmd and cmdstr not in ('55', '63'): + self.is_acmd = False + elif self.state.startswith('GET RESPONSE'): + if len(self.token) == 0: + # Wait for start bit (CMD = 0). + if cmd != 0: + continue + # Call the respective handler method for the response. + s = 'handle_response_%s' % self.state[13:].lower() + handle_response = getattr(self, s) + handle_response(cmd)