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:
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user