mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
refactor(parlio_rx): refactor to support unaligned user payload buffer
Closes https://github.com/espressif/esp-idf/issues/17581
This commit is contained in:
@@ -29,7 +29,7 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t* stash_buffer = NULL;
|
||||
ESP_RETURN_ON_FALSE(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(rx_buffer && buffer_len && align_buf_array, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
// read the cache line size of internal and external memory, we also use this information to check if a given memory is behind the cache
|
||||
size_t int_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
|
||||
@@ -41,80 +41,83 @@ esp_err_t esp_dma_split_rx_buffer_to_cache_aligned(void *rx_buffer, size_t buffe
|
||||
} else if (esp_ptr_internal(rx_buffer)) {
|
||||
split_line_size = int_mem_cache_line_size;
|
||||
}
|
||||
ESP_LOGV(TAG, "split_line_size:%zu", split_line_size);
|
||||
bool align_required = split_line_size > 0;
|
||||
ESP_EARLY_LOGV(TAG, "split_line_size:%zu", split_line_size);
|
||||
|
||||
// allocate the stash buffer from internal RAM
|
||||
// Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine
|
||||
stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer");
|
||||
if (*ret_stash_buffer == NULL) {
|
||||
// If the stash buffer is not offered by the caller, allocate the stash buffer from internal RAM
|
||||
// Note, the split_line_size can be 0, in this case, the stash_buffer is also NULL, which is fine
|
||||
stash_buffer = heap_caps_calloc(2, split_line_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE_ISR(!(split_line_size && !stash_buffer), ESP_ERR_NO_MEM, TAG, "no mem for stash buffer");
|
||||
} else {
|
||||
// If the stash buffer is offered by the caller, check if it is aligned
|
||||
ESP_RETURN_ON_FALSE_ISR(split_line_size == 0 || (uintptr_t)(*ret_stash_buffer) % split_line_size == 0,
|
||||
ESP_ERR_INVALID_ARG, TAG, "the offered stash buffer is not aligned");
|
||||
// If the stash buffer is offered by the caller, use it
|
||||
stash_buffer = *ret_stash_buffer;
|
||||
}
|
||||
|
||||
// clear align_array to avoid garbage data
|
||||
memset(align_buf_array, 0, sizeof(dma_buffer_split_array_t));
|
||||
bool need_cache_sync[3] = {false};
|
||||
|
||||
// if split_line_size is non-zero, split the buffer into head, body and tail
|
||||
if (split_line_size > 0) {
|
||||
// if align_required, split the buffer into head, body and tail
|
||||
if (align_required) {
|
||||
// calculate head_overflow_len
|
||||
size_t head_overflow_len = (uintptr_t)rx_buffer % split_line_size;
|
||||
head_overflow_len = head_overflow_len ? split_line_size - head_overflow_len : 0;
|
||||
ESP_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
|
||||
ESP_EARLY_LOGV(TAG, "head_addr:%p head_overflow_len:%zu", rx_buffer, head_overflow_len);
|
||||
// calculate tail_overflow_len
|
||||
size_t tail_overflow_len = ((uintptr_t)rx_buffer + buffer_len) % split_line_size;
|
||||
ESP_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
|
||||
|
||||
uint8_t extra_buf_count = 0;
|
||||
uint8_t* input_buffer = (uint8_t*)rx_buffer;
|
||||
align_buf_array->buf.head.recovery_address = input_buffer;
|
||||
align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
|
||||
align_buf_array->buf.head.length = head_overflow_len;
|
||||
need_cache_sync[0] = int_mem_cache_line_size > 0;
|
||||
align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len;
|
||||
align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len;
|
||||
align_buf_array->buf.body.length = buffer_len - head_overflow_len - tail_overflow_len;
|
||||
need_cache_sync[1] = true;
|
||||
align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len;
|
||||
align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
|
||||
align_buf_array->buf.tail.length = tail_overflow_len;
|
||||
need_cache_sync[2] = int_mem_cache_line_size > 0;
|
||||
ESP_EARLY_LOGV(TAG, "tail_addr:%p tail_overflow_len:%zu", rx_buffer + buffer_len - tail_overflow_len, tail_overflow_len);
|
||||
|
||||
// special handling when input_buffer length is no more than buffer alignment
|
||||
if (head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len) {
|
||||
align_buf_array->buf.head.length = buffer_len ;
|
||||
align_buf_array->buf.body.length = 0 ;
|
||||
align_buf_array->buf.tail.length = 0 ;
|
||||
bool is_small_buf = head_overflow_len >= buffer_len || tail_overflow_len >= buffer_len;
|
||||
uint8_t extra_buf_count = 0;
|
||||
uint8_t* input_buffer = (uint8_t*)rx_buffer;
|
||||
if (head_overflow_len || is_small_buf) {
|
||||
align_buf_array->buf.head.recovery_address = input_buffer;
|
||||
align_buf_array->buf.head.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
|
||||
align_buf_array->buf.head.length = is_small_buf ? buffer_len : head_overflow_len;
|
||||
need_cache_sync[0] = int_mem_cache_line_size > 0;
|
||||
}
|
||||
int body_len = (int)buffer_len - (int)head_overflow_len - (int)tail_overflow_len;
|
||||
if (body_len > 0) {
|
||||
align_buf_array->buf.body.recovery_address = input_buffer + head_overflow_len;
|
||||
align_buf_array->buf.body.aligned_buffer = input_buffer + head_overflow_len;
|
||||
align_buf_array->buf.body.length = body_len;
|
||||
need_cache_sync[1] = true;
|
||||
}
|
||||
if (tail_overflow_len && !is_small_buf) {
|
||||
align_buf_array->buf.tail.recovery_address = input_buffer + buffer_len - tail_overflow_len;
|
||||
align_buf_array->buf.tail.aligned_buffer = stash_buffer + split_line_size * extra_buf_count++;
|
||||
align_buf_array->buf.tail.length = tail_overflow_len;
|
||||
need_cache_sync[2] = int_mem_cache_line_size > 0;
|
||||
}
|
||||
} else {
|
||||
align_buf_array->buf.body.aligned_buffer = rx_buffer;
|
||||
align_buf_array->buf.body.recovery_address = rx_buffer;
|
||||
align_buf_array->buf.body.length = buffer_len;
|
||||
need_cache_sync[1] = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (align_buf_array->aligned_buffer[i].length == 0) {
|
||||
align_buf_array->aligned_buffer[i].aligned_buffer = NULL;
|
||||
align_buf_array->aligned_buffer[i].recovery_address = NULL;
|
||||
need_cache_sync[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate the aligned buffer if necessary
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (need_cache_sync[i]) {
|
||||
size_t sync_size = align_buf_array->aligned_buffer[i].length;
|
||||
size_t sync_size = align_buf_array->aligned_buffer[i].length;
|
||||
if (need_cache_sync[i] && sync_size > 0) {
|
||||
if (sync_size < split_line_size) {
|
||||
// If the buffer is smaller than the cache line size, we need to sync the whole buffer
|
||||
sync_size = split_line_size;
|
||||
}
|
||||
esp_err_t res = esp_cache_msync(align_buf_array->aligned_buffer[i].aligned_buffer, sync_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
ESP_GOTO_ON_ERROR(res, err, TAG, "failed to do cache sync");
|
||||
ESP_GOTO_ON_ERROR_ISR(res, err, TAG, "failed to do cache sync");
|
||||
}
|
||||
}
|
||||
|
||||
*ret_stash_buffer = stash_buffer;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (stash_buffer) {
|
||||
// Only free the stash buffer if it is not offered by the caller
|
||||
if (stash_buffer && *ret_stash_buffer == NULL) {
|
||||
free(stash_buffer);
|
||||
}
|
||||
return ret;
|
||||
|
||||
@@ -203,20 +203,24 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
|
||||
lli_nc->dw0.size = lli_nc->dw0.length;
|
||||
// mark the EOF node
|
||||
lli_nc->dw0.suc_eof = (config->flags.mark_eof == 1) && (i == num_items_need - 1);
|
||||
// mark the final node
|
||||
switch (config->flags.mark_final) {
|
||||
case GDMA_FINAL_LINK_TO_NULL:
|
||||
lli_nc->next = NULL;
|
||||
break;
|
||||
case GDMA_FINAL_LINK_TO_HEAD:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items);
|
||||
break;
|
||||
case GDMA_FINAL_LINK_TO_START:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items + begin_item_idx * item_size);
|
||||
break;
|
||||
default:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
|
||||
break;
|
||||
if (i == num_items_need - 1) {
|
||||
// mark the final node
|
||||
switch (config->flags.mark_final) {
|
||||
case GDMA_FINAL_LINK_TO_NULL:
|
||||
lli_nc->next = NULL;
|
||||
break;
|
||||
case GDMA_FINAL_LINK_TO_HEAD:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items);
|
||||
break;
|
||||
case GDMA_FINAL_LINK_TO_START:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size);
|
||||
break;
|
||||
default:
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
lli_nc->next = (gdma_link_list_item_t *)(list->items + (i + begin_item_idx + 1) % list_item_capacity * item_size);
|
||||
}
|
||||
lli_nc->dw0.owner = GDMA_LLI_OWNER_DMA;
|
||||
buf += max_buffer_mount_length;
|
||||
|
||||
@@ -62,7 +62,7 @@ esp_err_t gdma_del_link_list(gdma_link_list_handle_t list);
|
||||
*/
|
||||
typedef enum {
|
||||
GDMA_FINAL_LINK_TO_DEFAULT = 0, /*!< The next node is linked to the default next item in the link list */
|
||||
GDMA_FINAL_LINK_TO_NULL = 1, /*!< The next node is linked to the final item in the link list */
|
||||
GDMA_FINAL_LINK_TO_NULL = 1, /*!< No next node is linked */
|
||||
GDMA_FINAL_LINK_TO_HEAD = 2, /*!< The next node is linked to the head item in the link list */
|
||||
GDMA_FINAL_LINK_TO_START = 3, /*!< The next node is linked to the start item in the link list */
|
||||
} gdma_final_node_link_type_t;
|
||||
@@ -77,9 +77,11 @@ typedef struct {
|
||||
struct gdma_buffer_mount_flags {
|
||||
uint32_t mark_eof: 1; /*!< Whether to mark the list item as the "EOF" item.
|
||||
Note, an "EOF" descriptor can be interrupted differently by peripheral.
|
||||
But it doesn't mean to terminate a DMA link (use `mark_final` instead).
|
||||
But it doesn't mean to terminate a DMA link (set `mark_final` to GDMA_FINAL_LINK_TO_NULL instead).
|
||||
EOF link list item can also trigger an interrupt. */
|
||||
gdma_final_node_link_type_t mark_final: 2; /*!< The next node of the final item in the link list */
|
||||
gdma_final_node_link_type_t mark_final: 2; /*!< Specify the next item of the final item of this mount.
|
||||
For the other items that not the final one, it will be linked to the next item automatically and this field takes no effect.
|
||||
Note, the final item here does not mean the last item in the link list. It is `start_item_index + num_items - 1` */
|
||||
uint32_t bypass_buffer_align_check: 1; /*!< Whether to bypass the buffer alignment check.
|
||||
Only enable it when you know what you are doing. */
|
||||
} flags; //!< Flags for buffer mount configurations
|
||||
|
||||
Reference in New Issue
Block a user