diff --git a/components/driver/sdmmc_transaction.c b/components/driver/sdmmc_transaction.c index 52dec2991c..c53b4b11c4 100644 --- a/components/driver/sdmmc_transaction.c +++ b/components/driver/sdmmc_transaction.c @@ -122,9 +122,10 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo) // convert cmdinfo to hardware register value sdmmc_hw_cmd_t hw_cmd = make_hw_cmd(cmdinfo); if (cmdinfo->data) { - if (cmdinfo->datalen < 4 || cmdinfo->blklen % 4 != 0) { - ESP_LOGD(TAG, "%s: invalid size: total=%d block=%d", - __func__, cmdinfo->datalen, cmdinfo->blklen); + // Length should be either <4 or >=4 and =0 (mod 4). + if (cmdinfo->datalen >= 4 && cmdinfo->datalen % 4 != 0) { + ESP_LOGD(TAG, "%s: invalid size: total=%d", + __func__, cmdinfo->datalen); ret = ESP_ERR_INVALID_SIZE; goto out; } @@ -212,7 +213,8 @@ static void fill_dma_descriptors(size_t num_desc) desc->owned_by_idmac = 1; desc->buffer1_ptr = s_cur_transfer.ptr; desc->next_desc_ptr = (last) ? NULL : &s_dma_desc[(next + 1) % SDMMC_DMA_DESC_CNT]; - desc->buffer1_size = size_to_fill; + assert(size_to_fill < 4 || size_to_fill % 4 == 0); + desc->buffer1_size = (size_to_fill + 3) & (~3); s_cur_transfer.size_remaining -= size_to_fill; s_cur_transfer.ptr += size_to_fill; diff --git a/components/sdmmc/sdmmc_cmd.c b/components/sdmmc/sdmmc_cmd.c index 7279dec44c..ab88b7780f 100644 --- a/components/sdmmc/sdmmc_cmd.c +++ b/components/sdmmc/sdmmc_cmd.c @@ -1269,22 +1269,57 @@ static esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func, esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function, uint32_t addr, void* dst, size_t size) { - return sdmmc_io_rw_extended(card, function, addr, - SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT, - dst, size); + /* host quirk: SDIO transfer with length not divisible by 4 bytes + * has to be split into two transfers: one with aligned length, + * the other one for the remaining 1-3 bytes. + */ + uint8_t *pc_dst = dst; + while (size > 0) { + size_t size_aligned = size & (~3); + size_t will_transfer = size_aligned > 0 ? size_aligned : size; + + esp_err_t err = sdmmc_io_rw_extended(card, function, addr, + SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT, + pc_dst, will_transfer); + if (err != ESP_OK) { + return err; + } + pc_dst += will_transfer; + size -= will_transfer; + addr += will_transfer; + } + return ESP_OK; } esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function, uint32_t addr, const void* src, size_t size) { - return sdmmc_io_rw_extended(card, function, addr, - SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT, - (void*) src, size); + /* same host quirk as in sdmmc_io_read_bytes */ + const uint8_t *pc_src = (const uint8_t*) src; + + while (size > 0) { + size_t size_aligned = size & (~3); + size_t will_transfer = size_aligned > 0 ? size_aligned : size; + + esp_err_t err = sdmmc_io_rw_extended(card, function, addr, + SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT, + (void*) pc_src, will_transfer); + if (err != ESP_OK) { + return err; + } + pc_src += will_transfer; + size -= will_transfer; + addr += will_transfer; + } + return ESP_OK; } esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function, uint32_t addr, void* dst, size_t size) { + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } return sdmmc_io_rw_extended(card, function, addr, SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE, dst, size); @@ -1293,6 +1328,9 @@ esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function, esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function, uint32_t addr, const void* src, size_t size) { + if (size % 4 != 0) { + return ESP_ERR_INVALID_SIZE; + } return sdmmc_io_rw_extended(card, function, addr, SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE, (void*) src, size); diff --git a/components/sdmmc/test/test_sdio.c b/components/sdmmc/test/test_sdio.c index 8b885baf55..f4eae05980 100644 --- a/components/sdmmc/test/test_sdio.c +++ b/components/sdmmc/test/test_sdio.c @@ -309,35 +309,33 @@ static void test_cmd52_read_write_single_byte(sdmmc_card_t* card) TEST_ASSERT_EQUAL_UINT8(test_byte_2, val); } -static void test_cmd53_read_write_multiple_bytes(sdmmc_card_t* card) +static void test_cmd53_read_write_multiple_bytes(sdmmc_card_t* card, size_t n_bytes) { printf("Write multiple bytes using CMD53\n"); const size_t scratch_area_reg = SLCHOST_CONF_W0 - DR_REG_SLCHOST_BASE; uint8_t* src = heap_caps_malloc(512, MALLOC_CAP_DMA); uint32_t* src_32 = (uint32_t*) src; - const size_t n_words = 6; - srand(0); - for (size_t i = 0; i < n_words; ++i) { + + for (size_t i = 0; i < (n_bytes + 3) / 4; ++i) { src_32[i] = rand(); } - size_t len = n_words * sizeof(uint32_t); - TEST_ESP_OK(sdmmc_io_write_bytes(card, 1, scratch_area_reg, src, len)); - ESP_LOG_BUFFER_HEX(TAG, src, len); + TEST_ESP_OK(sdmmc_io_write_bytes(card, 1, scratch_area_reg, src, n_bytes)); + ESP_LOG_BUFFER_HEX(TAG, src, n_bytes); printf("Read back using CMD52\n"); uint8_t* dst = heap_caps_malloc(512, MALLOC_CAP_DMA); - for (size_t i = 0; i < len; ++i) { + for (size_t i = 0; i < n_bytes; ++i) { TEST_ESP_OK(sdmmc_io_read_byte(card, 1, scratch_area_reg + i, &dst[i])); } - ESP_LOG_BUFFER_HEX(TAG, dst, len); - TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, len); + ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes); + TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes); printf("Read back using CMD53\n"); - TEST_ESP_OK(sdmmc_io_read_bytes(card, 1, scratch_area_reg, dst, len)); - ESP_LOG_BUFFER_HEX(TAG, dst, len); - TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, len); + TEST_ESP_OK(sdmmc_io_read_bytes(card, 1, scratch_area_reg, dst, n_bytes)); + ESP_LOG_BUFFER_HEX(TAG, dst, n_bytes); + TEST_ASSERT_EQUAL_UINT8_ARRAY(src, dst, n_bytes); free(src); free(dst); @@ -364,9 +362,16 @@ TEST_CASE("can probe and talk to ESP32 SDIO slave", "[sdio][ignore]") /* Set up standard SDIO registers */ sdio_slave_common_init(card); - for (int repeat = 0; repeat < 10; ++repeat) { + srand(0); + for (int repeat = 0; repeat < 4; ++repeat) { test_cmd52_read_write_single_byte(card); - test_cmd53_read_write_multiple_bytes(card); + test_cmd53_read_write_multiple_bytes(card, 1); + test_cmd53_read_write_multiple_bytes(card, 2); + test_cmd53_read_write_multiple_bytes(card, 3); + test_cmd53_read_write_multiple_bytes(card, 4); + test_cmd53_read_write_multiple_bytes(card, 5); + test_cmd53_read_write_multiple_bytes(card, 23); + test_cmd53_read_write_multiple_bytes(card, 24); } sdio_slave_set_blocksize(card, 0, 512);