rp2_sd: Improve error handling

* In sd_cmd_read_complete, check read token and report appropriate error
  _before_ performing CRC check.
* In sd_read_csd, correctly handle the sd_cmd_read() failing.
* In sd_init, deinit the lower level sd_spi driver correctly on failure.

Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
This commit is contained in:
2025-10-19 16:53:57 +02:00
parent 9357b4d243
commit 8e4f2fde21
2 changed files with 74 additions and 59 deletions

View File

@@ -130,57 +130,62 @@ static void sd_dump_cid [[maybe_unused]] (void)
static bool sd_read_csd(struct sd_context *sd_context)
{
uint8_t buf[16];
if (sd_cmd_read(9, 0, 16, buf)) {
const uint8_t crc = sd_crc7(15, buf);
const uint8_t card_crc = buf[15] >> 1;
if (card_crc != crc) {
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc);
// Some cheap SD cards always report CRC=0, don't fail in that case
if (card_crc != 0) {
return false;
}
}
const unsigned csd_ver = buf[0] >> 6;
unsigned blocksize [[maybe_unused]] = 0;
unsigned blocks = 0;
unsigned version [[maybe_unused]] = 0;
switch (csd_ver) {
case 0: {
if (sd_context->sdhc_sdxc) {
printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n");
return false;
}
const unsigned read_bl_len = buf[5] & 0xf;
if (read_bl_len < 9 || read_bl_len > 11) {
printf("Invalid read_bl_len in CSD 1.0\n");
return false;
}
blocksize = 1 << (buf[5] & 0xf);
const unsigned c_size_mult = (buf[9] & 0x1) << 2 | (buf[10] & 0xc0) >> 6;
const unsigned c_size = (buf[6] & 0x3) << 10 | (buf[7] << 2) | (buf[8] & 0xc0) >> 6;
blocks = (c_size + 1) * (1 << (c_size_mult + 2));
version = 1;
break;
}
case 1: {
blocksize = SD_SECTOR_SIZE;
const unsigned c_size = (buf[7] & 0x3f) << 16 | buf[8] << 8 | buf[9];
blocks = (c_size + 1) * 1024;
version = 2;
break;
}
case 2: {
printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n");
if (!sd_cmd_read(9, 0, 16, buf)) {
printf("Failed to read CSD\n");
return false;
}
const uint8_t crc = sd_crc7(15, buf);
const uint8_t card_crc = buf[15] >> 1;
if (card_crc != crc) {
printf("CRC mismatch: Got %02hhx, expected %02hhx\n", card_crc, crc);
// Some cheap SD cards always report CRC=0, don't fail in that case
if (card_crc != 0) {
return false;
}
}
sd_context->blocks = blocks;
sd_context->blocksize = blocksize;
#ifdef SD_DEBUG
printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB\n", version, blocksize, blocks,
((uint64_t)blocksize * blocks) / (1024 * 1024));
#endif
}
const unsigned csd_ver = buf[0] >> 6;
unsigned blocksize [[maybe_unused]] = 0;
unsigned blocks = 0;
unsigned version [[maybe_unused]] = 0;
unsigned max_speed [[maybe_unused]] = 0;
switch (csd_ver) {
case 0: {
if (sd_context->sdhc_sdxc) {
printf("sd_init: Got CSD v1.0 but card is SDHC/SDXC?\n");
return false;
}
const unsigned read_bl_len = buf[5] & 0xf;
if (read_bl_len < 9 || read_bl_len > 11) {
printf("Invalid read_bl_len in CSD 1.0\n");
return false;
}
blocksize = 1 << (buf[5] & 0xf);
const unsigned c_size_mult = (buf[9] & 0x1) << 2 | (buf[10] & 0xc0) >> 6;
const unsigned c_size = (buf[6] & 0x3) << 10 | (buf[7] << 2) | (buf[8] & 0xc0) >> 6;
blocks = (c_size + 1) * (1 << (c_size_mult + 2));
version = 1;
max_speed = buf[3];
break;
}
case 1: {
blocksize = SD_SECTOR_SIZE;
const unsigned c_size = (buf[7] & 0x3f) << 16 | buf[8] << 8 | buf[9];
blocks = (c_size + 1) * 1024;
version = 2;
max_speed = buf[3];
break;
}
case 2: {
printf("sd_init: Got CSD v3.0, but SDUC does not support SPI.\n");
return false;
}
}
sd_context->blocks = blocks;
sd_context->blocksize = blocksize;
#ifdef SD_DEBUG
printf("CSD version %u.0, blocksize %u, blocks %u, capacity %llu MiB, max speed %u\n", version, blocksize, blocks,
((uint64_t)blocksize * blocks) / (1024 * 1024), max_speed);
#endif
return true;
}
@@ -191,52 +196,52 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
}
if (!sd_early_init()) {
return false;
goto out_spi;
}
if (!sd_check_interface_condition()) {
return false;
goto out_spi;
}
uint32_t ocr;
if (!sd_read_ocr(&ocr)) {
printf("sd_init: read OCR failed\n");
return false;
goto out_spi;
}
if ((ocr & 0x00380000) != 0x00380000) {
printf("sd_init: unsupported card voltage range\n");
return false;
goto out_spi;
}
if (!sd_send_op_cond())
return false;
goto out_spi;
sd_spi_set_bitrate(rate);
if (!sd_read_ocr(&ocr)) {
printf("sd_init: read OCR failed\n");
return false;
goto out_spi;
}
if (!(ocr & (1 << 31))) {
printf("sd_init: card not powered up but !idle?\n");
return false;
goto out_spi;
}
sd_context->sdhc_sdxc = (ocr & (1 << 30));
if (!sd_read_csd(sd_context)) {
return false;
goto out_spi;
}
if (sd_context->blocksize != SD_SECTOR_SIZE) {
if (sd_context->blocksize != 1024 && sd_context->blocksize != 2048) {
printf("sd_init: Unsupported block size %u\n", sd_context->blocksize);
return false;
goto out_spi;
}
// Attempt SET_BLOCKLEN command
uint8_t resp[1];
if (!sd_cmd(16, SD_SECTOR_SIZE, 1, resp)) {
printf("sd_init: SET_BLOCKLEN failed\n");
return false;
goto out_spi;
}
// Successfully set blocksize to SD_SECTOR_SIZE, adjust context
sd_context->blocks *= sd_context->blocksize / SD_SECTOR_SIZE;
@@ -253,6 +258,10 @@ bool sd_init(struct sd_context *sd_context, int mosi, int miso, int sck, int ss,
sd_context->initialized = true;
return true;
out_spi:
sd_spi_deinit();
return false;
}
bool sd_deinit(struct sd_context *sd_context)

View File

@@ -208,6 +208,12 @@ bool sd_cmd_read_complete(void)
sd_spi_wait_complete();
gpio_put(sd_spi_context.ss, true);
sd_spi_read_blocking(0xff, &buf, 1);
if (sd_spi_context.sd_dma_context.read_token_buf != 0xfe) {
#ifdef SD_DEBUG
printf("read failed: invalid read token %02hhx\n", sd_spi_context.sd_dma_context.read_token_buf);
#endif
return false;
}
#ifdef SD_READ_CRC_CHECK
const uint16_t expect_crc = sd_crc16(sd_spi_context.sd_dma_context.len, sd_spi_context.sd_dma_context.read_buf);
const uint16_t act_crc = sd_spi_context.sd_dma_context.crc_buf[0] << 8 | sd_spi_context.sd_dma_context.crc_buf[1];
@@ -218,7 +224,7 @@ bool sd_cmd_read_complete(void)
return false;
}
#endif
return (sd_spi_context.sd_dma_context.read_token_buf == 0xfe);
return true;
}
bool sd_cmd_write(uint8_t cmd, uint32_t arg, unsigned datalen, uint8_t data[const static datalen])