1
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
47
48
49
50
51
61
62
63
64
65
66
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
85
90
91
92
93
94
95
100
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
123
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
189
190
191
192
193
194
195
196
197
198
199
200
201
206
207
208
209
210
211
212
213
214
217
218
219
220
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
269
270
271
272
273
274
282
283
/* ... */
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_memory_utils.h"
#include "esp_heap_caps.h"
#include "esp_private/gdma_link.h"
#include "hal/cache_hal.h"
#include "hal/cache_ll.h"
#include "esp_cache.h"12 includes
static const char *TAG = "gdma";
#if SOC_NON_CACHEABLE_OFFSET
#define GDMA_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) ((addr) + SOC_NON_CACHEABLE_OFFSET)
#else
#define GDMA_CACHE_ADDR_TO_NON_CACHE_ADDR(addr) (addr)
#endif
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1))
typedef struct gdma_link_list_item_t gdma_link_list_item_t;
struct gdma_link_list_item_t {
struct {
uint32_t size : 12;
uint32_t length : 12;
uint32_t reversed24 : 4;
uint32_t err_eof : 1;
uint32_t reserved29 : 1;
uint32_t suc_eof : 1;
uint32_t owner : 1;
}{ ... } dw0;
void *buffer;
gdma_link_list_item_t *next;
}{ ... };
#define GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM 4095
typedef struct gdma_link_list_t {
uint32_t num_items;
size_t item_size;
size_t buffer_alignment;
uint8_t *items;
uint8_t *items_nc;
struct {
uint32_t check_owner: 1;
}{ ... } flags;
}{ ... } gdma_link_list_t;
esp_err_t gdma_new_link_list(const gdma_link_list_config_t *config, gdma_link_list_handle_t *ret_list)
{
esp_err_t ret = ESP_OK;
uint8_t *items = NULL;
gdma_link_list_t *list = NULL;
ESP_RETURN_ON_FALSE(config && ret_list, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->num_items, ESP_ERR_INVALID_ARG, TAG, "invalid number of items");
size_t buffer_alignment = config->buffer_alignment;
if (buffer_alignment == 0) {
buffer_alignment = 1;
}{...}
ESP_RETURN_ON_FALSE((buffer_alignment & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "invalid buffer alignment: %zu", buffer_alignment);
list = heap_caps_calloc(1, sizeof(gdma_link_list_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(list, ESP_ERR_NO_MEM, err, TAG, "no mem for link list");
uint32_t num_items = config->num_items;
size_t item_alignment = config->item_alignment ? config->item_alignment : 4;
size_t item_size = ALIGN_UP(sizeof(gdma_link_list_item_t), item_alignment);
uint32_t list_items_mem_caps = MALLOC_CAP_8BIT | MALLOC_CAP_DMA;
if (config->flags.items_in_ext_mem) {
list_items_mem_caps |= MALLOC_CAP_SPIRAM;
}{...} else {
list_items_mem_caps |= MALLOC_CAP_INTERNAL;
}{...}
items = heap_caps_aligned_calloc(item_alignment, num_items, item_size, list_items_mem_caps);
ESP_GOTO_ON_FALSE(items, ESP_ERR_NO_MEM, err, TAG, "no mem for link list items");
uint32_t data_cache_line_size = 0;
if (config->flags.items_in_ext_mem) {
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
}{...} else {
data_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA);
}{...}
if (data_cache_line_size) {
ESP_GOTO_ON_ERROR(esp_cache_msync(items, ALIGN_UP(num_items * item_size, data_cache_line_size),
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE),
err, TAG, "cache sync failed");
}{...}
list->num_items = num_items;
list->item_size = item_size;
list->items = items;
list->items_nc = GDMA_CACHE_ADDR_TO_NON_CACHE_ADDR(items);
list->buffer_alignment = buffer_alignment;
list->flags.check_owner = config->flags.check_owner;
ESP_LOGD(TAG, "new link list @%p, items @%p", list, items);
*ret_list = list;
return ESP_OK;
err:
if (list) {
free(list);
}{...}
if (items) {
free(items);
}{...}
return ret;
}{ ... }
esp_err_t gdma_del_link_list(gdma_link_list_handle_t list)
{
ESP_RETURN_ON_FALSE(list, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_LOGD(TAG, "del link list at %p", list);
free(list->items);
free(list);
return ESP_OK;
}{ ... }
esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_index, const gdma_buffer_mount_config_t *buf_config_array, size_t num_buf, int *end_item_index)
{
ESP_RETURN_ON_FALSE(list && buf_config_array && num_buf, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
size_t buffer_alignment = list->buffer_alignment;
size_t item_size = list->item_size;
uint32_t list_item_capacity = list->num_items;
start_item_index = (start_item_index % list_item_capacity + list_item_capacity) % list_item_capacity;
size_t max_buffer_mount_length = ALIGN_DOWN(GDMA_MAX_BUFFER_SIZE_PER_LINK_ITEM, buffer_alignment);
uint32_t begin_item_idx = start_item_index;
gdma_link_list_item_t *lli_nc = NULL;
uint32_t num_items_avail = 0;
if (list->flags.check_owner) {
for (int i = 0; i < list_item_capacity; i++) {
lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + start_item_index) % list_item_capacity * item_size);
if (lli_nc->dw0.owner == GDMA_LLI_OWNER_CPU) {
num_items_avail++;
}{...}
}{...}
}{...} else {
num_items_avail = list_item_capacity;
}{...}
for (size_t bi = 0; bi < num_buf; bi++) {
const gdma_buffer_mount_config_t *config = &buf_config_array[bi];
uint8_t *buf = (uint8_t *)config->buffer;
size_t len = config->length;
ESP_RETURN_ON_FALSE(((uintptr_t)buf & (buffer_alignment - 1)) == 0, ESP_ERR_INVALID_ARG, TAG, "buffer not aligned to %zu", buffer_alignment);
uint32_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
ESP_RETURN_ON_FALSE((begin_item_idx + num_items_need) <= (start_item_index + num_items_avail), ESP_ERR_INVALID_ARG, TAG, "no more space for buffer mounting");
begin_item_idx += num_items_need;
}{...}
lli_nc = (gdma_link_list_item_t *)(list->items_nc + (start_item_index + list_item_capacity - 1) % list_item_capacity * item_size);
lli_nc->next = (gdma_link_list_item_t *)(list->items + start_item_index * item_size);
begin_item_idx = start_item_index;
for (size_t bi = 0; bi < num_buf; bi++) {
const gdma_buffer_mount_config_t *config = &buf_config_array[bi];
uint8_t *buf = (uint8_t *)config->buffer;
size_t len = config->length;
if (len == 0 || buf == NULL) {
continue;
}{...}
uint32_t num_items_need = (len + max_buffer_mount_length - 1) / max_buffer_mount_length;
for (int i = 0; i < num_items_need; i++) {
lli_nc = (gdma_link_list_item_t *)(list->items_nc + (i + begin_item_idx) % list_item_capacity * item_size);
lli_nc->buffer = buf;
lli_nc->dw0.length = len > max_buffer_mount_length ? max_buffer_mount_length : len;
lli_nc->dw0.size = lli_nc->dw0.length;
lli_nc->dw0.suc_eof = (config->flags.mark_eof == 1) && (i == num_items_need - 1);
if ((config->flags.mark_final == 1) && (i == num_items_need - 1)) {
lli_nc->next = NULL;
}{...} 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;
len -= max_buffer_mount_length;
}{...}
begin_item_idx += num_items_need;
}{...}
if (end_item_index) {
*end_item_index = (begin_item_idx - 1 + list_item_capacity) % list_item_capacity;
}{...}
return ESP_OK;
}{ ... }
uintptr_t gdma_link_get_head_addr(gdma_link_list_handle_t list)
{
ESP_RETURN_ON_FALSE(list, 0, TAG, "invalid argument");
return (uintptr_t)(list->items);
}{ ... }
esp_err_t gdma_link_concat(gdma_link_list_handle_t first_link, int first_link_item_index, gdma_link_list_handle_t second_link, int second_link_item_index)
{
ESP_RETURN_ON_FALSE(first_link && second_link, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
gdma_link_list_item_t *lli_nc = NULL;
int num_items = first_link->num_items;
first_link_item_index = (first_link_item_index % num_items + num_items) % num_items;
lli_nc = (gdma_link_list_item_t *)(first_link->items_nc + first_link_item_index * first_link->item_size);
num_items = second_link->num_items;
second_link_item_index = (second_link_item_index % num_items + num_items) % num_items;
lli_nc->next = (gdma_link_list_item_t *)(second_link->items + second_link_item_index * second_link->item_size);
return ESP_OK;
}{ ... }
esp_err_t gdma_link_set_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t owner)
{
ESP_RETURN_ON_FALSE_ISR(list, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
int num_items = list->num_items;
item_index = (item_index % num_items + num_items) % num_items;
gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size);
lli->dw0.owner = owner;
return ESP_OK;
}{ ... }
esp_err_t gdma_link_get_owner(gdma_link_list_handle_t list, int item_index, gdma_lli_owner_t *owner)
{
ESP_RETURN_ON_FALSE_ISR(list && owner, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
int num_items = list->num_items;
item_index = (item_index % num_items + num_items) % num_items;
gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size);
*owner = lli->dw0.owner;
return ESP_OK;
}{ ... }
size_t gdma_link_count_buffer_size_till_eof(gdma_link_list_handle_t list, int start_item_index)
{
if (!list) {
return 0;
}{...}
int num_items = list->num_items;
start_item_index = (start_item_index % num_items + num_items) % num_items;
size_t buf_size = 0;
gdma_link_list_item_t *lli_nc = NULL;
for (int i = 0; i < num_items; i++) {
lli_nc = (gdma_link_list_item_t *)(list->items_nc + (start_item_index + i) % num_items * list->item_size);
buf_size += lli_nc->dw0.length;
if (lli_nc->dw0.suc_eof || lli_nc->next == NULL) {
break;
}{...}
}{...}
return buf_size;
}{ ... }