|
5 | 5 | */ |
6 | 6 |
|
7 | 7 | #include <inttypes.h> |
| 8 | +#include <sys/param.h> // for MIN/MAX |
8 | 9 | #include "esp_private/sdmmc_common.h" |
9 | 10 |
|
| 11 | + |
10 | 12 | static const char* TAG = "sdmmc_cmd"; |
11 | 13 |
|
12 | 14 |
|
@@ -462,31 +464,52 @@ esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src, |
462 | 464 | err = sdmmc_write_sectors_dma(card, src, start_block, block_count, block_size * block_count); |
463 | 465 | } else { |
464 | 466 | // SDMMC peripheral needs DMA-capable buffers. Split the write into |
465 | | - // separate single block writes, if needed, and allocate a temporary |
| 467 | + // separate (multi) block writes, if needed, and allocate a temporary |
466 | 468 | // DMA-capable buffer. |
467 | | - void *tmp_buf = NULL; |
468 | | - size_t actual_size = 0; |
469 | | - // We don't want to force the allocation into SPIRAM, the allocator |
470 | | - // will decide based on the buffer size and memory availability. |
471 | | - tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA); |
472 | | - if (!tmp_buf) { |
473 | | - ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); |
474 | | - return ESP_ERR_NO_MEM; |
| 469 | + size_t blocks_per_write = MIN(CONFIG_SD_UNALIGNED_MULTI_BLOCK_RW_MAX_CHUNK_SIZE, block_count); |
| 470 | + |
| 471 | + // prefer using DMA aligned buffer if available over allocating local temporary buffer |
| 472 | + bool use_dma_aligned_buffer = (card->host.dma_aligned_buffer != NULL); |
| 473 | + void* buf = use_dma_aligned_buffer ? card->host.dma_aligned_buffer : NULL; |
| 474 | + |
| 475 | + // only allocate temporary buffer if we can't use the dma_aligned buffer |
| 476 | + if (!use_dma_aligned_buffer) { |
| 477 | + // We don't want to force the allocation into SPIRAM, the allocator |
| 478 | + // will decide based on the buffer size and memory availability. |
| 479 | + buf = heap_caps_malloc(block_size * blocks_per_write, MALLOC_CAP_DMA); |
| 480 | + if (!buf) { |
| 481 | + ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); |
| 482 | + return ESP_ERR_NO_MEM; |
| 483 | + } |
| 484 | + } |
| 485 | + size_t actual_size = heap_caps_get_allocated_size(buf); |
| 486 | + blocks_per_write = actual_size / card->csd.sector_size; |
| 487 | + // we should still respect the user configured maximum size |
| 488 | + blocks_per_write = MIN(CONFIG_SD_UNALIGNED_MULTI_BLOCK_RW_MAX_CHUNK_SIZE, blocks_per_write); |
| 489 | + if (blocks_per_write == 0) { |
| 490 | + if (!use_dma_aligned_buffer) { |
| 491 | + free(buf); |
| 492 | + } |
| 493 | + ESP_LOGE(TAG, "%s: buffer smaller than sector size: buf=%d, sector=%d", __func__, actual_size, card->csd.sector_size); |
| 494 | + return ESP_ERR_INVALID_SIZE; |
475 | 495 | } |
476 | | - actual_size = heap_caps_get_allocated_size(tmp_buf); |
477 | 496 |
|
478 | 497 | const uint8_t* cur_src = (const uint8_t*) src; |
479 | | - for (size_t i = 0; i < block_count; ++i) { |
480 | | - memcpy(tmp_buf, cur_src, block_size); |
481 | | - cur_src += block_size; |
482 | | - err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size); |
| 498 | + for (size_t i = 0; i < block_count; i += blocks_per_write) { |
| 499 | + // make sure not to write more than the remaining blocks, i.e. block_count - i |
| 500 | + blocks_per_write = MIN(blocks_per_write, (block_count - i)); |
| 501 | + memcpy(buf, cur_src, block_size * blocks_per_write); |
| 502 | + cur_src += block_size * blocks_per_write; |
| 503 | + err = sdmmc_write_sectors_dma(card, buf, start_block + i, blocks_per_write, actual_size); |
483 | 504 | if (err != ESP_OK) { |
484 | | - ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d", |
485 | | - __func__, err, start_block, i); |
| 505 | + ESP_LOGD(TAG, "%s: error 0x%x writing blocks %d+[%d..%d]", |
| 506 | + __func__, err, start_block, i, i + blocks_per_write - 1); |
486 | 507 | break; |
487 | 508 | } |
488 | 509 | } |
489 | | - free(tmp_buf); |
| 510 | + if (!use_dma_aligned_buffer) { |
| 511 | + free(buf); |
| 512 | + } |
490 | 513 | } |
491 | 514 | return err; |
492 | 515 | } |
@@ -600,33 +623,55 @@ esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst, |
600 | 623 | err = sdmmc_read_sectors_dma(card, dst, start_block, block_count, block_size * block_count); |
601 | 624 | } else { |
602 | 625 | // SDMMC peripheral needs DMA-capable buffers. Split the read into |
603 | | - // separate single block reads, if needed, and allocate a temporary |
| 626 | + // separate (multi) block reads, if needed, and allocate a temporary |
604 | 627 | // DMA-capable buffer. |
605 | | - void *tmp_buf = NULL; |
606 | | - size_t actual_size = 0; |
607 | | - tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA); |
608 | | - if (!tmp_buf) { |
609 | | - ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); |
610 | | - return ESP_ERR_NO_MEM; |
| 628 | + size_t blocks_per_read = MIN(CONFIG_SD_UNALIGNED_MULTI_BLOCK_RW_MAX_CHUNK_SIZE, block_count); |
| 629 | + |
| 630 | + // prefer using DMA aligned buffer if available over allocating local temporary buffer |
| 631 | + bool use_dma_aligned_buffer = (card->host.dma_aligned_buffer != NULL); |
| 632 | + void* buf = use_dma_aligned_buffer ? card->host.dma_aligned_buffer : NULL; |
| 633 | + |
| 634 | + // only allocate temporary buffer if we can't use the dma_aligned buffer |
| 635 | + if (!use_dma_aligned_buffer) { |
| 636 | + // We don't want to force the allocation into SPIRAM, the allocator |
| 637 | + // will decide based on the buffer size and memory availability. |
| 638 | + buf = heap_caps_malloc(block_size * blocks_per_read, MALLOC_CAP_DMA); |
| 639 | + if (!buf) { |
| 640 | + ESP_LOGE(TAG, "%s: not enough mem, err=0x%x", __func__, ESP_ERR_NO_MEM); |
| 641 | + return ESP_ERR_NO_MEM; |
| 642 | + } |
| 643 | + } |
| 644 | + size_t actual_size = heap_caps_get_allocated_size(buf); |
| 645 | + blocks_per_read = actual_size / card->csd.sector_size; |
| 646 | + // we should still respect the user configured maximum size |
| 647 | + blocks_per_read = MIN(CONFIG_SD_UNALIGNED_MULTI_BLOCK_RW_MAX_CHUNK_SIZE, blocks_per_read); |
| 648 | + if (blocks_per_read == 0) { |
| 649 | + if (!use_dma_aligned_buffer) { |
| 650 | + free(buf); |
| 651 | + } |
| 652 | + ESP_LOGE(TAG, "%s: buffer smaller than sector size: buf=%d, sector=%d", __func__, actual_size, card->csd.sector_size); |
| 653 | + return ESP_ERR_INVALID_SIZE; |
611 | 654 | } |
612 | | - actual_size = heap_caps_get_allocated_size(tmp_buf); |
613 | 655 |
|
614 | 656 | uint8_t* cur_dst = (uint8_t*) dst; |
615 | | - for (size_t i = 0; i < block_count; ++i) { |
616 | | - err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1, actual_size); |
| 657 | + for (size_t i = 0; i < block_count; i += blocks_per_read) { |
| 658 | + // make sure not to read more than the remaining blocks, i.e. block_count - i |
| 659 | + blocks_per_read = MIN(blocks_per_read, (block_count - i)); |
| 660 | + err = sdmmc_read_sectors_dma(card, buf, start_block + i, blocks_per_read, actual_size); |
617 | 661 | if (err != ESP_OK) { |
618 | | - ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d", |
619 | | - __func__, err, start_block, i); |
| 662 | + ESP_LOGD(TAG, "%s: error 0x%x reading blocks %d+[%d..%d]", |
| 663 | + __func__, err, start_block, i, i + blocks_per_read - 1); |
620 | 664 | break; |
621 | 665 | } |
622 | | - memcpy(cur_dst, tmp_buf, block_size); |
623 | | - cur_dst += block_size; |
| 666 | + memcpy(cur_dst, buf, block_size * blocks_per_read); |
| 667 | + cur_dst += block_size * blocks_per_read; |
| 668 | + } |
| 669 | + if (!use_dma_aligned_buffer) { |
| 670 | + free(buf); |
624 | 671 | } |
625 | | - free(tmp_buf); |
626 | 672 | } |
627 | 673 | return err; |
628 | 674 | } |
629 | | - |
630 | 675 | esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst, |
631 | 676 | size_t start_block, size_t block_count, size_t buffer_len) |
632 | 677 | { |
|
0 commit comments