|
|
|
@ -18,6 +18,8 @@ |
|
|
|
|
|
|
|
|
|
Refrences: |
|
|
|
|
https://www.sdcard.org/downloads/pls/ (Physical Layer Simplified Specification)
|
|
|
|
|
REF: tags are refering to the spec form above. 'Physical Layer Simplified Specification v8.00' |
|
|
|
|
|
|
|
|
|
http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
|
|
|
|
|
https://embdev.net/attachment/39390/TOSHIBA_SD_Card_Specification.pdf
|
|
|
|
|
http://elm-chan.org/docs/mmc/mmc_e.html
|
|
|
|
@ -46,9 +48,12 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type |
|
|
|
|
device_t(mconfig, type, tag, owner, clock), |
|
|
|
|
write_miso(*this), |
|
|
|
|
m_image(*this, "image"), |
|
|
|
|
m_state(SD_STATE_IDLE), |
|
|
|
|
m_harddisk(nullptr), |
|
|
|
|
m_in_latch(0), m_out_latch(0), m_cmd_ptr(0), m_state(0), m_out_ptr(0), m_out_count(0), m_ss(0), m_in_bit(0), |
|
|
|
|
m_cur_bit(0), m_write_ptr(0), m_blksize(512), m_bACMD(false) |
|
|
|
|
m_ss(0), m_in_bit(0), |
|
|
|
|
m_in_latch(0), m_out_latch(0xff), m_cur_bit(0), |
|
|
|
|
m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_blksize(512), m_blknext(0), |
|
|
|
|
m_bACMD(false) |
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -64,13 +69,15 @@ spi_sdcard_sdhc_device::spi_sdcard_sdhc_device(const machine_config &mconfig, co |
|
|
|
|
m_type = SD_TYPE_HC; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ALLOW_SAVE_TYPE(spi_sdcard_device::sd_state); |
|
|
|
|
ALLOW_SAVE_TYPE(spi_sdcard_device::sd_type); |
|
|
|
|
|
|
|
|
|
void spi_sdcard_device::device_start() |
|
|
|
|
{ |
|
|
|
|
write_miso.resolve_safe(); |
|
|
|
|
save_item(NAME(m_state)); |
|
|
|
|
save_item(NAME(m_in_latch)); |
|
|
|
|
save_item(NAME(m_out_latch)); |
|
|
|
|
save_item(NAME(m_cmd_ptr)); |
|
|
|
|
save_item(NAME(m_state)); |
|
|
|
|
save_item(NAME(m_out_ptr)); |
|
|
|
|
save_item(NAME(m_out_count)); |
|
|
|
|
save_item(NAME(m_ss)); |
|
|
|
@ -78,6 +85,7 @@ void spi_sdcard_device::device_start() |
|
|
|
|
save_item(NAME(m_cur_bit)); |
|
|
|
|
save_item(NAME(m_write_ptr)); |
|
|
|
|
save_item(NAME(m_blksize)); |
|
|
|
|
save_item(NAME(m_blknext)); |
|
|
|
|
save_item(NAME(m_type)); |
|
|
|
|
save_item(NAME(m_cmd)); |
|
|
|
|
save_item(NAME(m_data)); |
|
|
|
@ -94,10 +102,11 @@ void spi_sdcard_device::device_add_mconfig(machine_config &config) |
|
|
|
|
HARDDISK(config, m_image).set_interface("spi_sdcard"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void spi_sdcard_device::send_data(int count) |
|
|
|
|
void spi_sdcard_device::send_data(u16 count, sd_state new_state) |
|
|
|
|
{ |
|
|
|
|
m_out_ptr = 0; |
|
|
|
|
m_out_count = count; |
|
|
|
|
change_state(new_state); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void spi_sdcard_device::spi_clock_w(int state) |
|
|
|
@ -121,56 +130,73 @@ void spi_sdcard_device::spi_clock_w(int state) |
|
|
|
|
if (m_cur_bit == 8) |
|
|
|
|
{ |
|
|
|
|
LOGMASKED(LOG_SPI, "SDCARD: got %02x\n", m_in_latch); |
|
|
|
|
for (u8 i = 0; i < 5; i++) |
|
|
|
|
{ |
|
|
|
|
m_cmd[i] = m_cmd[i + 1]; |
|
|
|
|
} |
|
|
|
|
m_cmd[5] = m_in_latch; |
|
|
|
|
|
|
|
|
|
switch (m_state) |
|
|
|
|
{ |
|
|
|
|
case SD_STATE_IDLE: |
|
|
|
|
for (int i = 0; i < 5; i++) |
|
|
|
|
case SD_STATE_IDLE: |
|
|
|
|
do_command(); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SD_STATE_WRITE_WAITFE: |
|
|
|
|
if (m_in_latch == 0xfe) |
|
|
|
|
{ |
|
|
|
|
m_state = SD_STATE_WRITE_DATA; |
|
|
|
|
m_out_latch = 0xff; |
|
|
|
|
m_write_ptr = 0; |
|
|
|
|
change_state(SD_STATE_RCV); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SD_STATE_WRITE_DATA: |
|
|
|
|
m_data[m_write_ptr++] = m_in_latch; |
|
|
|
|
if (m_write_ptr == (m_blksize + 2)) |
|
|
|
|
{ |
|
|
|
|
u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); |
|
|
|
|
if (m_type == SD_TYPE_V2) |
|
|
|
|
{ |
|
|
|
|
m_cmd[i] = m_cmd[i + 1]; |
|
|
|
|
blk /= m_blksize; |
|
|
|
|
} |
|
|
|
|
m_cmd[5] = m_in_latch; |
|
|
|
|
|
|
|
|
|
if ((((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1)) && (m_out_count == 0)) |
|
|
|
|
LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]); |
|
|
|
|
if (hard_disk_write(m_harddisk, blk, &m_data[0])) |
|
|
|
|
{ |
|
|
|
|
do_command(); |
|
|
|
|
m_data[0] = DATA_RESPONSE_OK; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SD_STATE_WRITE_WAITFE: |
|
|
|
|
if (m_in_latch == 0xfe) |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_state = SD_STATE_WRITE_DATA; |
|
|
|
|
m_out_latch = 0xff; |
|
|
|
|
m_write_ptr = 0; |
|
|
|
|
m_data[0] = DATA_RESPONSE_IO_ERROR; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SD_STATE_WRITE_DATA: |
|
|
|
|
m_data[m_write_ptr++] = m_in_latch; |
|
|
|
|
if (m_write_ptr == (m_blksize + 2)) |
|
|
|
|
{ |
|
|
|
|
u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4]; |
|
|
|
|
if (m_type == SD_TYPE_V2) |
|
|
|
|
{ |
|
|
|
|
blk /= m_blksize; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]); |
|
|
|
|
if (hard_disk_write(m_harddisk, blk, &m_data[0])) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = DATA_RESPONSE_OK; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = DATA_RESPONSE_IO_ERROR; |
|
|
|
|
} |
|
|
|
|
m_data[1] = 0x01; |
|
|
|
|
|
|
|
|
|
m_state = SD_STATE_IDLE; |
|
|
|
|
send_data(2); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
m_data[1] = 0x01; |
|
|
|
|
|
|
|
|
|
send_data(2, SD_STATE_IDLE); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case SD_STATE_DATA_MULTI: |
|
|
|
|
do_command(); |
|
|
|
|
if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xfe; // data token
|
|
|
|
|
hard_disk_read(m_harddisk, m_blknext++, &m_data[1]); |
|
|
|
|
util::crc16_t crc16 = util::crc16_creator::simple( |
|
|
|
|
&m_data[1], m_blksize); |
|
|
|
|
m_data[m_blksize + 1] = (crc16 >> 8) & 0xff; |
|
|
|
|
m_data[m_blksize + 2] = (crc16 & 0xff); |
|
|
|
|
send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
if (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0)) // CMD0 - GO_IDLE_STATE
|
|
|
|
|
{ |
|
|
|
|
do_command(); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -178,12 +204,11 @@ void spi_sdcard_device::spi_clock_w(int state) |
|
|
|
|
{ |
|
|
|
|
m_in_latch <<= 1; |
|
|
|
|
m_out_latch <<= 1; |
|
|
|
|
LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, m_out_latch, m_cur_bit); |
|
|
|
|
if (m_cur_bit == 8) |
|
|
|
|
{ |
|
|
|
|
m_cur_bit = 0; |
|
|
|
|
} |
|
|
|
|
m_out_latch |= 1; |
|
|
|
|
LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, |
|
|
|
|
m_out_latch, m_cur_bit); |
|
|
|
|
|
|
|
|
|
m_cur_bit &= 0x07; |
|
|
|
|
if (m_cur_bit == 0) |
|
|
|
|
{ |
|
|
|
|
if (m_out_count > 0) |
|
|
|
@ -193,163 +218,214 @@ void spi_sdcard_device::spi_clock_w(int state) |
|
|
|
|
m_out_count--; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
write_miso(BIT(m_out_latch, 7) ? ASSERT_LINE : CLEAR_LINE); |
|
|
|
|
write_miso(BIT(m_out_latch, 7)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void spi_sdcard_device::do_command() |
|
|
|
|
{ |
|
|
|
|
LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]); |
|
|
|
|
switch (m_cmd[0] & 0x3f) |
|
|
|
|
if (((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1)) |
|
|
|
|
{ |
|
|
|
|
case 0: // CMD0 - GO_IDLE_STATE
|
|
|
|
|
if (m_harddisk) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x01; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x00; |
|
|
|
|
} |
|
|
|
|
send_data(1); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 8: // CMD8 - SEND_IF_COND (SD v2 only)
|
|
|
|
|
m_data[0] = 0x01; |
|
|
|
|
m_data[1] = 0; |
|
|
|
|
m_data[2] = 0; |
|
|
|
|
m_data[3] = 0; |
|
|
|
|
m_data[4] = 0xaa; |
|
|
|
|
send_data(5); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 10: // CMD10 - SEND_CID
|
|
|
|
|
m_data[0] = 0x01; // initial R1 response
|
|
|
|
|
m_data[1] = 0x00; // throwaway byte before data transfer
|
|
|
|
|
m_data[2] = 0xfe; // data token
|
|
|
|
|
m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
|
|
|
|
|
m_data[4] = 'M'; // OEM ID - MD for MAMEdev
|
|
|
|
|
m_data[5] = 'D'; |
|
|
|
|
m_data[6] = 'M'; // Product Name - "MCARD"
|
|
|
|
|
m_data[7] = 'C'; |
|
|
|
|
m_data[8] = 'A'; |
|
|
|
|
m_data[9] = 'R'; |
|
|
|
|
m_data[10] = 'D'; |
|
|
|
|
m_data[11] = 0x10; // Product Revision in BCD (1.0)
|
|
|
|
|
{ |
|
|
|
|
u32 uSerial = 0x12345678; |
|
|
|
|
m_data[12] = (uSerial>>24) & 0xff; // PSN - Product Serial Number
|
|
|
|
|
m_data[13] = (uSerial>>16) & 0xff; |
|
|
|
|
m_data[14] = (uSerial>>8) & 0xff; |
|
|
|
|
m_data[15] = (uSerial & 0xff); |
|
|
|
|
} |
|
|
|
|
m_data[16] = 0x01; // MDT - Manufacturing Date
|
|
|
|
|
m_data[17] = 0x59; // 0x15 9 = 2021, September
|
|
|
|
|
m_data[18] = 0x00; // CRC7, bit 0 is always 0
|
|
|
|
|
LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]); |
|
|
|
|
bool clean_cmd = true; |
|
|
|
|
switch (m_cmd[0] & 0x3f) |
|
|
|
|
{ |
|
|
|
|
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16); |
|
|
|
|
m_data[19] = (crc16 >> 8) & 0xff; |
|
|
|
|
m_data[20] = (crc16 & 0xff); |
|
|
|
|
} |
|
|
|
|
send_data(3 + 16 + 2); |
|
|
|
|
break; |
|
|
|
|
case 0: // CMD0 - GO_IDLE_STATE
|
|
|
|
|
if (m_harddisk) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x01; |
|
|
|
|
send_data(1, SD_STATE_IDLE); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x00; |
|
|
|
|
send_data(1, SD_STATE_INA); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 8: // CMD8 - SEND_IF_COND (SD v2 only)
|
|
|
|
|
m_data[0] = 0; // 0x01;
|
|
|
|
|
m_data[1] = m_cmd[1]; // 0;
|
|
|
|
|
m_data[2] = m_cmd[2]; // 0;
|
|
|
|
|
m_data[3] = m_cmd[3]; // 0x01;
|
|
|
|
|
m_data[4] = m_cmd[4]; // 0xaa;
|
|
|
|
|
send_data(5, SD_STATE_IDLE); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 10: // CMD10 - SEND_CID
|
|
|
|
|
m_data[0] = 0x01; // initial R1 response
|
|
|
|
|
m_data[1] = 0x00; // throwaway byte before data transfer
|
|
|
|
|
m_data[2] = 0xfe; // data token
|
|
|
|
|
m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
|
|
|
|
|
m_data[4] = 'M'; // OEM ID - MD for MAMEdev
|
|
|
|
|
m_data[5] = 'D'; |
|
|
|
|
m_data[6] = 'M'; // Product Name - "MCARD"
|
|
|
|
|
m_data[7] = 'C'; |
|
|
|
|
m_data[8] = 'A'; |
|
|
|
|
m_data[9] = 'R'; |
|
|
|
|
m_data[10] = 'D'; |
|
|
|
|
m_data[11] = 0x10; // Product Revision in BCD (1.0)
|
|
|
|
|
{ |
|
|
|
|
u32 uSerial = 0x12345678; |
|
|
|
|
m_data[12] = (uSerial >> 24) & 0xff; // PSN - Product Serial Number
|
|
|
|
|
m_data[13] = (uSerial >> 16) & 0xff; |
|
|
|
|
m_data[14] = (uSerial >> 8) & 0xff; |
|
|
|
|
m_data[15] = (uSerial & 0xff); |
|
|
|
|
} |
|
|
|
|
m_data[16] = 0x01; // MDT - Manufacturing Date
|
|
|
|
|
m_data[17] = 0x59; // 0x15 9 = 2021, September
|
|
|
|
|
m_data[18] = 0x00; // CRC7, bit 0 is always 0
|
|
|
|
|
{ |
|
|
|
|
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16); |
|
|
|
|
m_data[19] = (crc16 >> 8) & 0xff; |
|
|
|
|
m_data[20] = (crc16 & 0xff); |
|
|
|
|
} |
|
|
|
|
send_data(3 + 16 + 2, SD_STATE_STBY); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 16: // CMD16 - SET_BLOCKLEN
|
|
|
|
|
m_blksize = (m_cmd[3] << 8) | m_cmd[4]; |
|
|
|
|
if (hard_disk_set_block_size(m_harddisk, m_blksize)) |
|
|
|
|
{ |
|
|
|
|
case 12: // CMD12 - STOP_TRANSMISSION
|
|
|
|
|
m_data[0] = 0; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; // indicate an error
|
|
|
|
|
// if false was returned, it means the hard disk is a CHD file, and we can't resize the
|
|
|
|
|
// blocks on CHD files.
|
|
|
|
|
logerror("spi_sdcard: Couldn't change block size to %d, wrong CHD file?", m_blksize); |
|
|
|
|
} |
|
|
|
|
send_data(1); |
|
|
|
|
break; |
|
|
|
|
send_data(1, |
|
|
|
|
m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 17: // CMD17 - READ_SINGLE_BLOCK
|
|
|
|
|
if (m_harddisk) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x00; // initial R1 response
|
|
|
|
|
// data token occurs some time after the R1 response. A2SD expects at least 1
|
|
|
|
|
// byte of space between R1 and the data packet.
|
|
|
|
|
m_data[2] = 0xfe; // data token
|
|
|
|
|
u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4]; |
|
|
|
|
if (m_type == SD_TYPE_V2) |
|
|
|
|
case 16: // CMD16 - SET_BLOCKLEN
|
|
|
|
|
m_blksize = (u16(m_cmd[3]) << 8) | u16(m_cmd[4]); |
|
|
|
|
if (hard_disk_set_block_size(m_harddisk, m_blksize)) |
|
|
|
|
{ |
|
|
|
|
blk /= m_blksize; |
|
|
|
|
m_data[0] = 0; |
|
|
|
|
} |
|
|
|
|
LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk); |
|
|
|
|
hard_disk_read(m_harddisk, blk, &m_data[3]); |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize); |
|
|
|
|
m_data[m_blksize + 3] = (crc16 >> 8) & 0xff; |
|
|
|
|
m_data[m_blksize + 4] = (crc16 & 0xff); |
|
|
|
|
m_data[0] = 0xff; // indicate an error
|
|
|
|
|
// if false was returned, it means the hard disk is a CHD file, and we can't resize the
|
|
|
|
|
// blocks on CHD files.
|
|
|
|
|
logerror("spi_sdcard: Couldn't change block size to %d, wrong " |
|
|
|
|
"CHD file?", |
|
|
|
|
m_blksize); |
|
|
|
|
} |
|
|
|
|
send_data(3 + m_blksize + 2); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; // show an error
|
|
|
|
|
send_data(1); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
send_data(1, SD_STATE_TRAN); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 24: // CMD24 - WRITE_BLOCK
|
|
|
|
|
m_data[0] = 0; |
|
|
|
|
send_data(1); |
|
|
|
|
m_state = SD_STATE_WRITE_WAITFE; |
|
|
|
|
break; |
|
|
|
|
case 17: // CMD17 - READ_SINGLE_BLOCK
|
|
|
|
|
if (m_harddisk) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x00; // initial R1 response
|
|
|
|
|
// data token occurs some time after the R1 response. A2SD expects at least 1
|
|
|
|
|
// byte of space between R1 and the data packet.
|
|
|
|
|
m_data[1] = 0xff; |
|
|
|
|
m_data[2] = 0xfe; // data token
|
|
|
|
|
u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); |
|
|
|
|
if (m_type == SD_TYPE_V2) |
|
|
|
|
{ |
|
|
|
|
blk /= m_blksize; |
|
|
|
|
} |
|
|
|
|
LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk); |
|
|
|
|
hard_disk_read(m_harddisk, blk, &m_data[3]); |
|
|
|
|
{ |
|
|
|
|
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize); |
|
|
|
|
m_data[m_blksize + 3] = (crc16 >> 8) & 0xff; |
|
|
|
|
m_data[m_blksize + 4] = (crc16 & 0xff); |
|
|
|
|
} |
|
|
|
|
send_data(3 + m_blksize + 2, SD_STATE_DATA); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; // show an error
|
|
|
|
|
send_data(1, SD_STATE_DATA); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 41: |
|
|
|
|
if (m_bACMD) // ACMD41 - SD_SEND_OP_COND
|
|
|
|
|
{ |
|
|
|
|
case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK
|
|
|
|
|
if (m_harddisk) |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0x00; // initial R1 response
|
|
|
|
|
// data token occurs some time after the R1 response. A2SD
|
|
|
|
|
// expects at least 1 byte of space between R1 and the data
|
|
|
|
|
// packet.
|
|
|
|
|
m_blknext = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]); |
|
|
|
|
if (m_type == SD_TYPE_V2) |
|
|
|
|
{ |
|
|
|
|
m_blknext /= m_blksize; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; // show an error
|
|
|
|
|
} |
|
|
|
|
send_data(1, SD_STATE_DATA_MULTI); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 24: // CMD24 - WRITE_BLOCK
|
|
|
|
|
m_data[0] = 0; |
|
|
|
|
} |
|
|
|
|
else // CMD41 - illegal
|
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; |
|
|
|
|
} |
|
|
|
|
send_data(1); |
|
|
|
|
break; |
|
|
|
|
send_data(1, SD_STATE_RCV); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 55: // CMD55 - APP_CMD
|
|
|
|
|
m_data[0] = 0x01; |
|
|
|
|
send_data(1); |
|
|
|
|
break; |
|
|
|
|
case 41: |
|
|
|
|
if (m_bACMD) // ACMD41 - SD_SEND_OP_COND
|
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0; |
|
|
|
|
send_data(1, SD_STATE_READY); // + SD_STATE_IDLE
|
|
|
|
|
} |
|
|
|
|
else // CMD41 - illegal
|
|
|
|
|
{ |
|
|
|
|
m_data[0] = 0xff; |
|
|
|
|
send_data(1, SD_STATE_INA); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 55: // CMD55 - APP_CMD
|
|
|
|
|
m_data[0] = 0x01; |
|
|
|
|
send_data(1, SD_STATE_IDLE); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 58: // CMD58 - READ_OCR
|
|
|
|
|
m_data[0] = 0; |
|
|
|
|
if (m_type == SD_TYPE_HC) |
|
|
|
|
case 58: // CMD58 - READ_OCR
|
|
|
|
|
m_data[0] = 0; |
|
|
|
|
if (m_type == SD_TYPE_HC) |
|
|
|
|
{ |
|
|
|
|
m_data[1] = 0x40; // indicate SDHC support
|
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[1] = 0; |
|
|
|
|
} |
|
|
|
|
m_data[2] = 0; |
|
|
|
|
m_data[3] = 0; |
|
|
|
|
m_data[4] = 0; |
|
|
|
|
send_data(5, SD_STATE_DATA); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported %02x\n", m_cmd[0] & 0x3f); |
|
|
|
|
clean_cmd = false; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD"
|
|
|
|
|
if ((m_cmd[0] & 0x3f) == 55) |
|
|
|
|
{ |
|
|
|
|
m_data[1] = 0x40; // indicate SDHC support
|
|
|
|
|
m_bACMD = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_data[1] = 0; |
|
|
|
|
m_bACMD = false; |
|
|
|
|
} |
|
|
|
|
m_data[2] = 0; |
|
|
|
|
m_data[3] = 0; |
|
|
|
|
m_data[4] = 0; |
|
|
|
|
send_data(5); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD"
|
|
|
|
|
if ((m_cmd[0] & 0x3f) == 55) |
|
|
|
|
{ |
|
|
|
|
m_bACMD = true; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
m_bACMD = false; |
|
|
|
|
if (clean_cmd) |
|
|
|
|
{ |
|
|
|
|
for (u8 i = 0; i < 6; i++) |
|
|
|
|
{ |
|
|
|
|
m_cmd[i] = 0xff; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void spi_sdcard_device::change_state(sd_state new_state) |
|
|
|
|
{ |
|
|
|
|
// TODO validate if transition is valid using refs below.
|
|
|
|
|
// REF Figure 4-13:SD Memory Card State Diagram (Transition Mode)
|
|
|
|
|
// REF Table 4-35:Card State Transition Table
|
|
|
|
|
m_state = new_state; |
|
|
|
|
} |
|
|
|
|