Skip to content

Commit b547175

Browse files
committed
machine/spi_sdcard.cpp: More fixes:
* Send faux CRC status response immediately after receiving data. * Allocate a large enough buffer for an SD Card with 2048-byte blocks. * Don't indicate partial block read support for SDHC cards. * Reject tranfers that cross block boundaries for SD Card (misaligned read support is not flagged as supported). * Reject partial block writes and writes that cross block boundaries (also not flagged as supported). * Behave a bit better when no card is present.
1 parent e9ea8d6 commit b547175

File tree

2 files changed

+146
-68
lines changed

2 files changed

+146
-68
lines changed

Diff for: src/devices/machine/spi_sdcard.cpp

+134-63
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ ALLOW_SAVE_TYPE(spi_sdcard_device::sd_state);
142142

143143
DEFINE_DEVICE_TYPE(SPI_SDCARD, spi_sdcard_device, "spi_sdcard", "SD Card (SPI interface)")
144144

145-
spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
145+
spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
146146
spi_sdcard_device(mconfig, SPI_SDCARD, tag, owner, clock)
147147
{
148148
}
149149

150-
spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
150+
spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
151151
device_t(mconfig, type, tag, owner, clock),
152152
write_miso(*this),
153153
m_image(*this, "image"),
@@ -158,7 +158,7 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type
158158
m_state(SD_STATE_IDLE),
159159
m_ss(0), m_in_bit(0), m_clk_state(0),
160160
m_in_latch(0), m_out_latch(0xff), m_cur_bit(0),
161-
m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_blknext(0),
161+
m_out_delay(0), m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_xferblk(512), m_blknext(0),
162162
m_bACMD(false)
163163
{
164164
std::fill(std::begin(m_csd), std::end(m_csd), 0);
@@ -170,18 +170,22 @@ spi_sdcard_device::~spi_sdcard_device()
170170

171171
void spi_sdcard_device::device_start()
172172
{
173-
save_item(NAME(m_state));
174-
save_item(NAME(m_data));
173+
m_data = make_unique_clear<u8 []>(2048 + 8);
174+
175+
save_pointer(NAME(m_data), 2048 + 8);
175176
save_item(NAME(m_cmd));
177+
save_item(NAME(m_state));
176178
save_item(NAME(m_ss));
177179
save_item(NAME(m_in_bit));
178180
save_item(NAME(m_clk_state));
179181
save_item(NAME(m_in_latch));
180182
save_item(NAME(m_out_latch));
181183
save_item(NAME(m_cur_bit));
184+
save_item(NAME(m_out_delay));
182185
save_item(NAME(m_out_count));
183186
save_item(NAME(m_out_ptr));
184187
save_item(NAME(m_write_ptr));
188+
save_item(NAME(m_xferblk));
185189
save_item(NAME(m_blknext));
186190
save_item(NAME(m_bACMD));
187191
}
@@ -190,40 +194,50 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima
190194
{
191195
// need block size and total blocks to create CSD
192196
auto const info = m_image->get_info();
193-
uint64_t const total_blocks = uint64_t(info.cylinders) * info.heads * info.sectors;
197+
u64 const total_blocks = u64(info.cylinders) * info.heads * info.sectors;
194198
if (!total_blocks)
195199
{
196-
osd_printf_error("%s: SD card cannot mount a zero-block image\n", tag());
200+
osd_printf_error("%s: SD Card cannot mount a zero-block image\n", tag());
197201
return image_error::INVALIDIMAGE;
198202
}
199203

200204
// ensure block size can be expressed in the CSD
201205
if ((info.sectorbytes & (info.sectorbytes - 1)) || !info.sectorbytes || (512 > info.sectorbytes) || (2048 < info.sectorbytes))
202206
{
203-
osd_printf_error("%s: SD card cannot use sector size %u (must be a power of 2 from 512 to 2048)\n", tag());
207+
osd_printf_error("%s: SD Card cannot use sector size %u (must be a power of 2 from 512 to 2048)\n", tag());
204208
return image_error::INVALIDIMAGE;
205209
}
206210
u8 block_size_exp = 0;
207211
for (auto i = info.sectorbytes; !BIT(i, 0); i >>= 1)
208212
++block_size_exp;
209213

210214
// see how we can express the total block count
211-
uint64_t total_mant = total_blocks;
212-
uint8_t total_exp = 0;
215+
u64 total_mant = total_blocks;
216+
u8 total_exp = 0;
213217
while (!BIT(total_mant, 0))
214218
{
215219
total_mant >>= 1;
216220
++total_exp;
217221
}
218222
bool const sd_ok = (2 <= total_exp) && ((1 << 12) >= (total_mant << ((9 < total_exp) ? (total_exp - 9) : 0)));
219-
bool const sdhc_ok = (512 == info.sectorbytes) && (10 <= total_exp) && ((uint32_t(1) << 16) >= (total_mant << (total_exp - 10)));
223+
bool const sdhc_ok = (512 == info.sectorbytes) && (10 <= total_exp) && ((u32(1) << 16) >= (total_mant << (total_exp - 10)));
220224
if (!sd_ok && !sdhc_ok)
221225
{
222-
osd_printf_error("%s: image size %u blocks of %u bytes is not supported by SD or SDHC\n", tag(), total_blocks, info.sectorbytes);
226+
osd_printf_error("%s: SD Card image size %u blocks of %u bytes is not supported by SD or SDHC\n", tag(), total_blocks, info.sectorbytes);
223227
return image_error::INVALIDIMAGE;
224228
}
225229

226-
m_blksize = info.sectorbytes;
230+
try
231+
{
232+
m_sectorbuf.resize(info.sectorbytes);
233+
}
234+
catch (std::bad_alloc const &)
235+
{
236+
osd_printf_error("%s: Error allocating %u-byte SD Card sector buffer\n", tag(), info.sectorbytes);
237+
return std::errc::not_enough_memory;
238+
}
239+
240+
m_blksize = m_xferblk = info.sectorbytes;
227241

228242
// set up common CSD fields
229243
m_csd[0] = 0x00; // 127: CSD_STRUCTURE:2 (00b) 0:6
@@ -233,7 +247,7 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima
233247
m_csd[4] = 0x5b; // 95: CCC:12 (01x110110101b)
234248
m_csd[5] = 0x50; // .. READ_BL_LN:4
235249
m_csd[5] |= block_size_exp;
236-
m_csd[6] = 0x80; // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
250+
m_csd[6] = 0x00; // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
237251
m_csd[7] = 0x00; // ..
238252
m_csd[8] = 0x00; // .. VDD_R_CURR_MIN:3 VDD_R_CURR_MAX:3
239253
m_csd[9] = 0x00; // 55: VDD_W_CURR_MIN:3 VDD_W_CURR_MAX:3 C_SIZE_MUL:3
@@ -248,9 +262,9 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima
248262

249263
if (sdhc_ok && ((SD_TYPE_HC == m_preferred_type) || !sd_ok))
250264
{
251-
uint32_t const c_size = (total_blocks >> 10) - 1;
265+
u32 const c_size = (total_blocks >> 10) - 1;
252266
osd_printf_verbose(
253-
"%s: mounted as SDHC, %u blocks of %u bytes, device size ((%u + 1) << 10) * (1 << %u)\n",
267+
"%s: SD Card image mounted as SDHC, %u blocks of %u bytes, device size ((%u + 1) << 10) * (1 << %u)\n",
254268
tag(),
255269
total_blocks, info.sectorbytes,
256270
c_size, block_size_exp);
@@ -267,10 +281,10 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima
267281
}
268282
else
269283
{
270-
uint8_t const c_size_mult = std::min<uint8_t>(total_exp, 9) - 2;
271-
uint16_t const c_size = (total_blocks >> (c_size_mult + 2)) - 1;
284+
u8 const c_size_mult = std::min<u8>(total_exp, 9) - 2;
285+
u16 const c_size = (total_blocks >> (c_size_mult + 2)) - 1;
272286
osd_printf_verbose(
273-
"%s: mounted as SD, %u blocks of %u bytes, device size ((%u + 1) << (%u + 2)) * (1 << %u)\n",
287+
"%s: SD Card image mounted as SD, %u blocks of %u bytes, device size ((%u + 1) << (%u + 2)) * (1 << %u)\n",
274288
tag(),
275289
total_blocks, info.sectorbytes,
276290
c_size, c_size_mult, block_size_exp);
@@ -281,7 +295,8 @@ std::error_condition spi_sdcard_device::image_loaded(device_image_interface &ima
281295
m_csd[0] |= CSD_STRUCTURE_V10 << 6; // 127: CSD_STRUCTURE:2 (00b) 0:6
282296
m_csd[1] = TAAC_UNIT_1MS | TAAC_VALUE_1_5; // 119: TAAC:8
283297

284-
m_csd[6] |= BIT(c_size, 10, 2); // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
298+
m_csd[6] |= 0x80; // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
299+
m_csd[6] |= BIT(c_size, 10, 2);
285300
m_csd[7] |= BIT(c_size, 2, 8); // ..
286301
m_csd[8] |= BIT(c_size, 0, 2) << 6; // .. VDD_R_CURR_MIN:3 VDD_R_CURR_MAX:3
287302
m_csd[8] |= VDD_CURR_MIN_100MA << 3;
@@ -312,8 +327,9 @@ void spi_sdcard_device::device_add_mconfig(machine_config &config)
312327
m_image->set_device_unload(FUNC(spi_sdcard_device::image_unloaded));
313328
}
314329

315-
void spi_sdcard_device::send_data(u16 count, sd_state new_state)
330+
void spi_sdcard_device::send_data(u16 count, sd_state new_state, u8 delay)
316331
{
332+
m_out_delay = delay;
317333
m_out_ptr = 0;
318334
m_out_count = count;
319335
change_state(new_state);
@@ -359,9 +375,10 @@ void spi_sdcard_device::latch_in()
359375
else if (m_state == SD_STATE_WRITE_DATA)
360376
{
361377
m_data[m_write_ptr++] = m_in_latch;
362-
if (m_write_ptr == (m_blksize + 2))
378+
if (m_write_ptr == (m_xferblk + 2))
363379
{
364380
LOG("writing LBA %x, data %02x %02x %02x %02x\n", m_blknext, m_data[0], m_data[1], m_data[2], m_data[3]);
381+
// TODO: this is supposed to be a CRC response, the actual write will take some time
365382
if (m_image->write(m_blknext, &m_data[0]))
366383
{
367384
m_data[0] = DATA_RESPONSE_OK;
@@ -372,7 +389,7 @@ void spi_sdcard_device::latch_in()
372389
}
373390
m_data[1] = 0x01;
374391

375-
send_data(2, SD_STATE_IDLE);
392+
send_data(2, SD_STATE_IDLE, 0); // zero delay - must immediately follow the data
376393
}
377394
}
378395
else // receive CMD
@@ -385,6 +402,7 @@ void spi_sdcard_device::latch_in()
385402
do_command();
386403
if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
387404
{
405+
// FIXME: support multi-block read when transfer size is smaller than block size
388406
m_data[0] = 0xfe; // data token
389407
m_image->read(m_blknext++, &m_data[1]);
390408
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[1], m_blksize);
@@ -411,13 +429,13 @@ void spi_sdcard_device::shift_out()
411429
m_cur_bit &= 0x07;
412430
if (m_cur_bit == 0)
413431
{
414-
if (m_out_ptr < SPI_DELAY_RESPONSE)
432+
if (m_out_ptr < m_out_delay)
415433
{
416434
m_out_ptr++;
417435
}
418436
else if (m_out_count > 0)
419437
{
420-
m_out_latch = m_data[m_out_ptr - SPI_DELAY_RESPONSE];
438+
m_out_latch = m_data[m_out_ptr - m_out_delay];
421439
m_out_ptr++;
422440
LOGMASKED(LOG_SPI, "SDCARD: latching %02x (start of shift)\n", m_out_latch);
423441
m_out_count--;
@@ -471,16 +489,16 @@ void spi_sdcard_device::do_command()
471489
break;
472490

473491
case 10: // CMD10 - SEND_CID
474-
m_data[0] = 0x00; // initial R1 response
475-
m_data[1] = 0xff; // throwaway byte before data transfer
476-
m_data[2] = 0xfe; // data token
477-
m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
478-
m_data[4] = 'M'; // OEM ID - MD for MAMEdev
479-
m_data[5] = 'D';
480-
m_data[6] = 'M'; // Product Name - "MCARD"
481-
m_data[7] = 'C';
482-
m_data[8] = 'A';
483-
m_data[9] = 'R';
492+
m_data[0] = 0x00; // initial R1 response
493+
m_data[1] = 0xff; // throwaway byte before data transfer
494+
m_data[2] = 0xfe; // data token
495+
m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
496+
m_data[4] = 'M'; // OEM ID - MD for MAMEdev
497+
m_data[5] = 'D';
498+
m_data[6] = 'M'; // Product Name - "MCARD"
499+
m_data[7] = 'C';
500+
m_data[8] = 'A';
501+
m_data[9] = 'R';
484502
m_data[10] = 'D';
485503
m_data[11] = 0x10; // Product Revision in BCD (1.0)
486504
{
@@ -509,17 +527,22 @@ void spi_sdcard_device::do_command()
509527
break;
510528

511529
case 16: // CMD16 - SET_BLOCKLEN
512-
m_blksize = get_u16be(&m_cmd[3]);
513-
if (m_image->exists() && m_image->set_block_size(m_blksize))
530+
if (m_image->exists())
514531
{
515-
m_data[0] = 0;
532+
u16 const blocklen = get_u16be(&m_cmd[3]);
533+
if (blocklen && ((m_type == SD_TYPE_V2) || (blocklen == m_blksize)) && (blocklen <= m_blksize))
534+
{
535+
m_xferblk = blocklen;
536+
m_data[0] = 0x00;
537+
}
538+
else
539+
{
540+
m_data[0] = 0x40; // parameter error
541+
}
516542
}
517543
else
518544
{
519-
m_data[0] = 0xff; // indicate an error
520-
// if false was returned, it means the hard disk is a CHD file, and we can't resize the
521-
// blocks on CHD files.
522-
logerror("spi_sdcard: Couldn't change block size to %d, wrong CHD file?", m_blksize);
545+
m_data[0] = 0xff; // show an error
523546
}
524547
send_data(1, SD_STATE_TRAN);
525548
break;
@@ -533,53 +556,101 @@ void spi_sdcard_device::do_command()
533556
m_data[1] = 0xff;
534557
m_data[2] = 0xfe; // data token
535558
u32 blk = get_u32be(&m_cmd[1]);
536-
if (m_type == SD_TYPE_V2)
559+
if ((m_type == SD_TYPE_V2) && ((blk / m_blksize) != ((blk + (m_xferblk - 1)) / m_blksize)))
560+
{
561+
LOG("rejecting read of %u bytes at %u that crosses %u-byte block boundary\n", m_xferblk, blk, m_blksize);
562+
m_data[0] = 0x40; // parameter error
563+
send_data(1, SD_STATE_TRAN);
564+
}
565+
else if (m_xferblk == m_blksize)
537566
{
538-
blk /= m_blksize;
567+
// optimise for reading an entire block
568+
if (m_type == SD_TYPE_V2)
569+
{
570+
blk /= m_blksize;
571+
}
572+
m_image->read(blk, &m_data[3]);
573+
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_xferblk);
574+
put_u16be(&m_data[m_xferblk + 3], crc16);
575+
LOG("reading LBA %x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", blk, m_data[3], m_data[4], m_xferblk - 2, m_data[m_xferblk + 1], m_data[m_xferblk + 2], crc16);
576+
send_data(3 + m_xferblk + 2, SD_STATE_DATA);
539577
}
540-
m_image->read(blk, &m_data[3]);
578+
else
541579
{
542-
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize);
543-
put_u16be(&m_data[m_blksize + 3], crc16);
544-
LOG("reading LBA %x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", blk, m_data[3], m_data[4], m_blksize - 2, m_data[m_blksize + 1], m_data[m_blksize + 2], crc16);
580+
assert(m_type == SD_TYPE_V2);
581+
m_image->read(blk / m_blksize, &m_sectorbuf[0]);
582+
std::copy_n(&m_sectorbuf[blk % m_blksize], m_xferblk, &m_data[3]);
583+
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_xferblk);
584+
put_u16be(&m_data[m_xferblk + 3], crc16);
585+
LOG("reading LBA %x+%x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", blk / m_blksize, blk % m_blksize, m_data[3], m_data[4], m_xferblk - 2, m_data[m_xferblk + 1], m_data[m_xferblk + 2], crc16);
586+
send_data(3 + m_xferblk + 2, SD_STATE_DATA);
545587
}
546-
send_data(3 + m_blksize + 2, SD_STATE_DATA);
547588
}
548589
else
549590
{
550591
m_data[0] = 0xff; // show an error
551-
send_data(1, SD_STATE_DATA);
592+
send_data(1, SD_STATE_TRAN);
552593
}
553594
break;
554595

555596
case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK
556597
if (m_image->exists())
557598
{
558-
m_data[0] = 0x00; // initial R1 response
559-
// data token occurs some time after the R1 response. A2SD
560-
// expects at least 1 byte of space between R1 and the data
561-
// packet.
562-
m_blknext = get_u32be(&m_cmd[1]);
563-
if (m_type == SD_TYPE_V2)
599+
if (m_xferblk == m_blksize)
564600
{
565-
m_blknext /= m_blksize;
601+
m_data[0] = 0x00; // initial R1 response
602+
// data token occurs some time after the R1 response. A2SD
603+
// expects at least 1 byte of space between R1 and the data
604+
// packet.
605+
m_blknext = get_u32be(&m_cmd[1]);
606+
if (m_type == SD_TYPE_V2)
607+
{
608+
m_blknext /= m_xferblk;
609+
}
610+
send_data(1, SD_STATE_DATA_MULTI);
611+
}
612+
else
613+
{
614+
// FIXME: support multi-block read when transfer size is smaller than block size
615+
m_data[0] = 0x40; // parameter error
616+
send_data(1, SD_STATE_TRAN);
566617
}
567618
}
568619
else
569620
{
570621
m_data[0] = 0xff; // show an error
622+
send_data(1, SD_STATE_TRAN);
571623
}
572-
send_data(1, SD_STATE_DATA_MULTI);
573624
break;
574625

575626
case 24: // CMD24 - WRITE_BLOCK
576-
m_data[0] = 0;
577-
m_blknext = get_u32be(&m_cmd[1]);
578-
if (m_type == SD_TYPE_V2)
627+
if (m_xferblk != m_blksize)
579628
{
580-
m_blknext /= m_blksize;
629+
// partial block write not supported
630+
LOG("rejecting write of %u bytes that is not a full %u-byte block\n", m_xferblk, m_blksize);
631+
m_data[0] = 0x40; // parameter error
632+
send_data(1, SD_STATE_TRAN);
633+
}
634+
else
635+
{
636+
m_blknext = get_u32be(&m_cmd[1]);
637+
if ((m_type == SD_TYPE_V2) && (m_blknext % m_blksize))
638+
{
639+
// misaligned write not supported
640+
LOG("rejecting write of %u bytes at %u that crosses %u-byte block boundary\n", m_xferblk, m_blknext, m_blksize);
641+
m_data[0] = 0x40; // parameter error
642+
send_data(1, SD_STATE_TRAN);
643+
}
644+
else
645+
{
646+
if (m_type == SD_TYPE_V2)
647+
{
648+
m_blknext /= m_xferblk;
649+
}
650+
m_data[0] = 0;
651+
send_data(1, SD_STATE_WRITE_WAITFE);
652+
}
581653
}
582-
send_data(1, SD_STATE_WRITE_WAITFE);
583654
break;
584655

585656
case 41:

0 commit comments

Comments
 (0)