/src/tinyusb/src/common/tusb_fifo.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-FileCopyrightText: Copyright (c) 2019 Ha Thach (tinyusb.org) |
3 | | * SPDX-FileCopyrightText: Copyright (c) 2020 Reinhard Panhuber |
4 | | * SPDX-License-Identifier: MIT |
5 | | * |
6 | | * This file is part of the TinyUSB stack. |
7 | | */ |
8 | | |
9 | | #include "osal/osal.h" |
10 | | #include "tusb_fifo.h" |
11 | | |
12 | | #define TU_FIFO_DBG 0 |
13 | | |
14 | | |
15 | | #if OSAL_MUTEX_REQUIRED |
16 | | |
17 | | TU_ATTR_ALWAYS_INLINE static inline void ff_lock(osal_mutex_t mutex) { |
18 | | if (mutex != NULL) { |
19 | | osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER); |
20 | | } |
21 | | } |
22 | | |
23 | | TU_ATTR_ALWAYS_INLINE static inline void ff_unlock(osal_mutex_t mutex) { |
24 | | if (mutex != NULL) { |
25 | | osal_mutex_unlock(mutex); |
26 | | } |
27 | | } |
28 | | |
29 | | #else |
30 | | #define ff_lock(_mutex) |
31 | | #define ff_unlock(_mutex) |
32 | | |
33 | | #endif |
34 | | |
35 | | //--------------------------------------------------------------------+ |
36 | | // Setup API |
37 | | //--------------------------------------------------------------------+ |
38 | 4 | bool tu_fifo_config(tu_fifo_t *f, void *buffer, uint16_t depth, bool overwritable) { |
39 | | // Limit index space to 2*depth - this allows for a fast "modulo" calculation |
40 | | // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable |
41 | | // only if overflow happens once (important for unsupervised DMA applications) |
42 | 4 | if (depth > 0x8000) { |
43 | 0 | return false; |
44 | 0 | } |
45 | | |
46 | 4 | ff_lock(f->mutex_wr); |
47 | 4 | ff_lock(f->mutex_rd); |
48 | | |
49 | 4 | f->buffer = (uint8_t *)buffer; |
50 | 4 | f->depth = depth; |
51 | 4 | f->overwritable = overwritable; |
52 | 4 | f->rd_idx = 0u; |
53 | 4 | f->wr_idx = 0u; |
54 | | |
55 | 4 | ff_unlock(f->mutex_wr); |
56 | 4 | ff_unlock(f->mutex_rd); |
57 | | |
58 | 4 | return true; |
59 | 4 | } |
60 | | |
61 | | // clear fifo by resetting read and write indices |
62 | 10.8k | void tu_fifo_clear(tu_fifo_t *f) { |
63 | 10.8k | ff_lock(f->mutex_wr); |
64 | 10.8k | ff_lock(f->mutex_rd); |
65 | | |
66 | 10.8k | f->rd_idx = 0; |
67 | 10.8k | f->wr_idx = 0; |
68 | | |
69 | 10.8k | ff_unlock(f->mutex_wr); |
70 | 10.8k | ff_unlock(f->mutex_rd); |
71 | 10.8k | } |
72 | | |
73 | | // Change the fifo overwritable mode |
74 | 38.4k | void tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable) { |
75 | 38.4k | if (f->overwritable == overwritable) { |
76 | 38.4k | return; |
77 | 38.4k | } |
78 | | |
79 | 0 | ff_lock(f->mutex_wr); |
80 | 0 | ff_lock(f->mutex_rd); |
81 | |
|
82 | 0 | f->overwritable = overwritable; |
83 | |
|
84 | 0 | ff_unlock(f->mutex_wr); |
85 | 0 | ff_unlock(f->mutex_rd); |
86 | 0 | } |
87 | | |
88 | | //--------------------------------------------------------------------+ |
89 | | // Hardware FIFO API |
90 | | // Support different data access width and address increment scheme |
91 | | // Can support multiple i.e both 16 and 32-bit data access if needed |
92 | | //--------------------------------------------------------------------+ |
93 | | #if CFG_TUSB_FIFO_HWFIFO_API |
94 | | #if CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE > 0 |
95 | | #define HWFIFO_ADDR_NEXT_N(_hwfifo, _const, _n) _hwfifo = (_const volatile void *)((uintptr_t)(_hwfifo) + _n) |
96 | | #else |
97 | | #define HWFIFO_ADDR_NEXT_N(_hwfifo, _const, _n) |
98 | | #endif |
99 | | |
100 | | #define HWFIFO_ADDR_NEXT(_hwfifo, _const) HWFIFO_ADDR_NEXT_N(_hwfifo, _const, CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE) |
101 | | |
102 | | // the fixed ratio works since in the only case of dynamic/multiple data_stride (rusb2): addr_stride is 0 |
103 | | #define HWFIFO_ADDR_DATA_RATIO (CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE / CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE) |
104 | | |
105 | | //------------- Write -------------// |
106 | | #ifndef CFG_TUSB_FIFO_HWFIFO_CUSTOM_WRITE |
107 | | TU_ATTR_ALWAYS_INLINE static inline void stride_write(volatile void *hwfifo, const void *src, uint8_t data_stride) { |
108 | | (void)data_stride; // possible unused |
109 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4 |
110 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 4 |
111 | | if (data_stride == 4) |
112 | | #endif |
113 | | { |
114 | | *((volatile uint32_t *)hwfifo) = tu_unaligned_read32(src); |
115 | | } |
116 | | #endif |
117 | | |
118 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2 |
119 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 2 |
120 | | if (data_stride == 2) |
121 | | #endif |
122 | | { |
123 | | *((volatile uint16_t *)hwfifo) = tu_unaligned_read16(src); |
124 | | } |
125 | | #endif |
126 | | |
127 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 |
128 | | *((volatile uint8_t *)hwfifo) = *(const uint8_t *)src; |
129 | | #endif |
130 | | } |
131 | | |
132 | | // Copy from fifo to fixed address buffer (usually a tx register) with TU_FIFO_FIXED_ADDR_RW32 mode |
133 | | void tu_hwfifo_write(volatile void *hwfifo, const uint8_t *src, uint16_t len, const tu_hwfifo_access_t *access_mode) { |
134 | | // Write full available 16/32 bit words to dest |
135 | | const uint8_t data_stride = (access_mode != NULL) ? access_mode->data_stride : CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE; |
136 | | while (len >= data_stride) { |
137 | | stride_write(hwfifo, src, data_stride); |
138 | | src += data_stride; |
139 | | len -= data_stride; |
140 | | HWFIFO_ADDR_NEXT(hwfifo, ); |
141 | | } |
142 | | |
143 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE > 1 |
144 | | #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS |
145 | | // 16-bit access is allowed for odd bytes |
146 | | if (len >= 2) { |
147 | | *((volatile uint16_t *)hwfifo) = tu_unaligned_read16(src); |
148 | | src += 2; |
149 | | len -= 2; |
150 | | HWFIFO_ADDR_NEXT_N(hwfifo, , 2); |
151 | | } |
152 | | #endif |
153 | | |
154 | | #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS |
155 | | // 8-bit access is allowed for odd bytes |
156 | | while (len > 0) { |
157 | | *((volatile uint8_t *)hwfifo) = *src++; |
158 | | len--; |
159 | | HWFIFO_ADDR_NEXT_N(hwfifo, , 1); |
160 | | } |
161 | | #else |
162 | | |
163 | | // Write odd bytes i.e 1 byte for 16 bit or 1-3 bytes for 32 bit |
164 | | if (len > 0) { |
165 | | uint32_t tmp = 0u; |
166 | | memcpy(&tmp, src, len); |
167 | | stride_write(hwfifo, &tmp, data_stride); |
168 | | HWFIFO_ADDR_NEXT(hwfifo, ); |
169 | | } |
170 | | #endif |
171 | | #endif |
172 | | } |
173 | | #endif |
174 | | |
175 | | //------------- Read -------------// |
176 | | #ifndef CFG_TUSB_FIFO_HWFIFO_CUSTOM_READ |
177 | | TU_ATTR_ALWAYS_INLINE static inline void stride_read(const volatile void *hwfifo, void *dest, uint8_t data_stride) { |
178 | | (void)data_stride; // possible unused |
179 | | |
180 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 4 |
181 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 4 |
182 | | if (data_stride == 4) |
183 | | #endif |
184 | | { |
185 | | tu_unaligned_write32(dest, *((const volatile uint32_t *)hwfifo)); |
186 | | } |
187 | | #endif |
188 | | |
189 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE & 2 |
190 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE != 2 |
191 | | if (data_stride == 2) |
192 | | #endif |
193 | | { |
194 | | tu_unaligned_write16(dest, *((const volatile uint16_t *)hwfifo)); |
195 | | } |
196 | | #endif |
197 | | |
198 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 |
199 | | *(uint8_t *)dest = *((const volatile uint8_t *)hwfifo); |
200 | | #endif |
201 | | } |
202 | | |
203 | | void tu_hwfifo_read(const volatile void *hwfifo, uint8_t *dest, uint16_t len, const tu_hwfifo_access_t *access_mode) { |
204 | | // Reading full available 16/32-bit hwfifo and write to fifo |
205 | | const uint8_t data_stride = (access_mode != NULL) ? access_mode->data_stride : CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE; |
206 | | while (len >= data_stride) { |
207 | | stride_read(hwfifo, dest, data_stride); |
208 | | dest += data_stride; |
209 | | len -= data_stride; |
210 | | HWFIFO_ADDR_NEXT(hwfifo, const); |
211 | | } |
212 | | |
213 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE > 1 |
214 | | #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_16BIT_ACCESS |
215 | | // 16-bit access is allowed for odd bytes |
216 | | if (len >= 2) { |
217 | | tu_unaligned_write16(dest, *((const volatile uint16_t *)hwfifo)); |
218 | | dest += 2; |
219 | | len -= 2; |
220 | | HWFIFO_ADDR_NEXT_N(hwfifo, const, 2); |
221 | | } |
222 | | #endif |
223 | | |
224 | | #ifdef CFG_TUSB_FIFO_HWFIFO_DATA_ODD_8BIT_ACCESS |
225 | | // 8-bit access is allowed for odd bytes |
226 | | while (len > 0) { |
227 | | *dest++ = *((const volatile uint8_t *)hwfifo); |
228 | | len--; |
229 | | HWFIFO_ADDR_NEXT_N(hwfifo, const, 1); |
230 | | } |
231 | | #else |
232 | | // Read odd bytes i.e 1 byte for 16 bit or 1-3 bytes for 32 bit |
233 | | if (len > 0) { |
234 | | uint32_t tmp; |
235 | | stride_read(hwfifo, &tmp, data_stride); |
236 | | memcpy(dest, &tmp, len); |
237 | | HWFIFO_ADDR_NEXT(hwfifo, const); |
238 | | } |
239 | | #endif |
240 | | #endif |
241 | | } |
242 | | #endif |
243 | | |
244 | | // push to sw fifo from hwfifo |
245 | | static void hwff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr, |
246 | | const tu_hwfifo_access_t *access_mode) { |
247 | | uint16_t lin_bytes = f->depth - wr_ptr; |
248 | | uint16_t wrap_bytes = n - lin_bytes; |
249 | | uint8_t *ff_buf = f->buffer + wr_ptr; |
250 | | |
251 | | const volatile void *hwfifo = (const volatile void *)app_buf; |
252 | | if (n <= lin_bytes) { |
253 | | // Linear only case |
254 | | tu_hwfifo_read(hwfifo, ff_buf, n, access_mode); |
255 | | } else { |
256 | | // Wrap around case |
257 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 |
258 | | tu_hwfifo_read(hwfifo, ff_buf, lin_bytes, access_mode); // linear part |
259 | | HWFIFO_ADDR_NEXT_N(hwfifo, const, lin_bytes); |
260 | | tu_hwfifo_read(hwfifo, f->buffer, wrap_bytes, access_mode); // wrapped part |
261 | | #else |
262 | | // Write full words to the linear part of the buffer |
263 | | const uint8_t data_stride = access_mode->data_stride; |
264 | | const uint32_t odd_mask = data_stride - 1; |
265 | | uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask); |
266 | | tu_hwfifo_read(hwfifo, ff_buf, lin_even, access_mode); |
267 | | HWFIFO_ADDR_NEXT_N(hwfifo, const, lin_even * HWFIFO_ADDR_DATA_RATIO); |
268 | | ff_buf += lin_even; |
269 | | |
270 | | // There could be an odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary |
271 | | // combine it with the wrapped part to form a full word for data stride |
272 | | const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask); |
273 | | if (lin_odd > 0) { |
274 | | const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd); |
275 | | uint8_t buf_temp[4]; |
276 | | tu_hwfifo_read(hwfifo, buf_temp, lin_odd + wrap_odd, access_mode); |
277 | | HWFIFO_ADDR_NEXT(hwfifo, const); |
278 | | |
279 | | for (uint8_t i = 0; i < lin_odd; ++i) { |
280 | | ff_buf[i] = buf_temp[i]; |
281 | | } |
282 | | for (uint8_t i = 0; i < wrap_odd; ++i) { |
283 | | f->buffer[i] = buf_temp[lin_odd + i]; |
284 | | } |
285 | | |
286 | | wrap_bytes -= wrap_odd; |
287 | | ff_buf = f->buffer + wrap_odd; // wrap around |
288 | | } else { |
289 | | ff_buf = f->buffer; // wrap around to beginning |
290 | | } |
291 | | |
292 | | // Write data wrapped part |
293 | | if (wrap_bytes > 0) { |
294 | | tu_hwfifo_read(hwfifo, ff_buf, wrap_bytes, access_mode); |
295 | | } |
296 | | #endif |
297 | | } |
298 | | } |
299 | | |
300 | | // pull from sw fifo to hwfifo |
301 | | static void hwff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr, |
302 | | const tu_hwfifo_access_t *access_mode) { |
303 | | uint16_t lin_bytes = f->depth - rd_ptr; |
304 | | uint16_t wrap_bytes = n - lin_bytes; // only used if wrapped |
305 | | const uint8_t *ff_buf = f->buffer + rd_ptr; |
306 | | |
307 | | volatile void *hwfifo = (volatile void *)app_buf; |
308 | | |
309 | | if (n <= lin_bytes) { |
310 | | // Linear only case |
311 | | tu_hwfifo_write(hwfifo, ff_buf, n, access_mode); |
312 | | } else { |
313 | | // Wrap around case |
314 | | #if CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE == 1 |
315 | | tu_hwfifo_write(hwfifo, ff_buf, lin_bytes, access_mode); // linear part |
316 | | HWFIFO_ADDR_NEXT_N(hwfifo, , lin_bytes); |
317 | | tu_hwfifo_write(hwfifo, f->buffer, wrap_bytes, access_mode); // wrapped part |
318 | | #else |
319 | | // Read full words from linear part |
320 | | const uint8_t data_stride = access_mode->data_stride; |
321 | | const uint32_t odd_mask = data_stride - 1; |
322 | | uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask); |
323 | | tu_hwfifo_write(hwfifo, ff_buf, lin_even, access_mode); |
324 | | HWFIFO_ADDR_NEXT_N(hwfifo, , lin_even * HWFIFO_ADDR_DATA_RATIO); |
325 | | ff_buf += lin_even; |
326 | | |
327 | | // There could be odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary |
328 | | const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask); |
329 | | if (lin_odd > 0) { |
330 | | const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd); |
331 | | |
332 | | uint8_t buf_temp[4]; |
333 | | for (uint8_t i = 0; i < lin_odd; ++i) { |
334 | | buf_temp[i] = ff_buf[i]; |
335 | | } |
336 | | for (uint8_t i = 0; i < wrap_odd; ++i) { |
337 | | buf_temp[lin_odd + i] = f->buffer[i]; |
338 | | } |
339 | | |
340 | | tu_hwfifo_write(hwfifo, buf_temp, lin_odd + wrap_odd, access_mode); |
341 | | HWFIFO_ADDR_NEXT(hwfifo, ); |
342 | | |
343 | | wrap_bytes -= wrap_odd; |
344 | | ff_buf = f->buffer + wrap_odd; // wrap around |
345 | | } else { |
346 | | ff_buf = f->buffer; // wrap around to beginning |
347 | | } |
348 | | |
349 | | // Read data wrapped part |
350 | | if (wrap_bytes > 0) { |
351 | | tu_hwfifo_write(hwfifo, ff_buf, wrap_bytes, access_mode); |
352 | | } |
353 | | #endif |
354 | | } |
355 | | } |
356 | | #endif |
357 | | |
358 | | //--------------------------------------------------------------------+ |
359 | | // Pull & Push |
360 | | // copy data to/from fifo without updating read/write pointers |
361 | | //--------------------------------------------------------------------+ |
362 | | // send n items to fifo WITHOUT updating write pointer |
363 | 141k | static void ff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uint16_t wr_ptr) { |
364 | 141k | uint16_t lin_bytes = f->depth - wr_ptr; |
365 | 141k | uint16_t wrap_bytes = n - lin_bytes; |
366 | 141k | uint8_t *ff_buf = f->buffer + wr_ptr; |
367 | | |
368 | 141k | if (n <= lin_bytes) { |
369 | | // Linear only case |
370 | 139k | memcpy(ff_buf, app_buf, n); |
371 | 139k | } else { |
372 | | // Wrap around case |
373 | 2.37k | memcpy(ff_buf, app_buf, lin_bytes); // linear part |
374 | 2.37k | memcpy(f->buffer, ((const uint8_t *)app_buf) + lin_bytes, wrap_bytes); // wrapped part |
375 | 2.37k | } |
376 | 141k | } |
377 | | |
378 | | // get n items from fifo WITHOUT updating read pointer |
379 | 128k | static void ff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t rd_ptr) { |
380 | 128k | uint16_t lin_bytes = f->depth - rd_ptr; |
381 | 128k | uint16_t wrap_bytes = n - lin_bytes; // only used if wrapped |
382 | 128k | const uint8_t *ff_buf = f->buffer + rd_ptr; |
383 | | |
384 | | // single byte access |
385 | 128k | if (n <= lin_bytes) { |
386 | | // Linear only |
387 | 126k | memcpy(app_buf, ff_buf, n); |
388 | 126k | } else { |
389 | | // Wrap around |
390 | 1.70k | memcpy(app_buf, ff_buf, lin_bytes); // linear part |
391 | 1.70k | memcpy((uint8_t *)app_buf + lin_bytes, f->buffer, wrap_bytes); // wrapped part |
392 | 1.70k | } |
393 | 128k | } |
394 | | |
395 | | //--------------------------------------------------------------------+ |
396 | | // Index Helper |
397 | | //--------------------------------------------------------------------+ |
398 | | |
399 | | // Advance an absolute index |
400 | | // "absolute" index is only in the range of [0..2*depth) |
401 | 463k | static uint16_t advance_index(uint16_t depth, uint16_t idx, uint16_t offset) { |
402 | | // We limit the index space of p such that a correct wrap around happens |
403 | | // Check for a wrap around or if we are in unused index space - This has to be checked first!! |
404 | | // We are exploiting the wrap around to the correct index |
405 | 463k | uint16_t new_idx = (uint16_t)(idx + offset); |
406 | 463k | if ((idx > new_idx) || (new_idx >= 2 * depth)) { |
407 | 10.2k | const uint16_t non_used_index_space = (uint16_t)(UINT16_MAX - (2 * depth - 1)); |
408 | 10.2k | new_idx = (uint16_t)(new_idx + non_used_index_space); |
409 | 10.2k | } |
410 | | |
411 | 463k | return new_idx; |
412 | 463k | } |
413 | | |
414 | | // index to pointer (0..depth-1), simply a modulo with minus. |
415 | 270k | TU_ATTR_ALWAYS_INLINE static inline uint16_t idx2ptr(uint16_t depth, uint16_t idx) { |
416 | | // Only run at most 3 times since index is limit in the range of [0..2*depth) |
417 | 405k | while (idx >= depth) { |
418 | 134k | idx -= depth; |
419 | 134k | } |
420 | 270k | return idx; |
421 | 270k | } |
422 | | |
423 | | // Works on local copies of w |
424 | | // When an overwritable fifo is overflowed, rd_idx will be re-index so that it forms a full fifo |
425 | 946 | static uint16_t correct_read_index(tu_fifo_t *f, uint16_t wr_idx) { |
426 | 946 | uint16_t rd_idx; |
427 | 946 | if (wr_idx >= f->depth) { |
428 | 500 | rd_idx = wr_idx - f->depth; |
429 | 500 | } else { |
430 | 446 | rd_idx = wr_idx + f->depth; |
431 | 446 | } |
432 | | |
433 | 946 | f->rd_idx = rd_idx; |
434 | 946 | return rd_idx; |
435 | 946 | } |
436 | | |
437 | | //--------------------------------------------------------------------+ |
438 | | // n-API |
439 | | //--------------------------------------------------------------------+ |
440 | | |
441 | | // Works on local copies of w and r |
442 | | // Must be protected by read mutex since in case of an overflow read pointer gets modified |
443 | | uint16_t tu_fifo_peek_n_access_mode(tu_fifo_t *f, void *p_buffer, uint16_t n, uint16_t wr_idx, uint16_t rd_idx, |
444 | 321k | const tu_hwfifo_access_t *access_mode) { |
445 | 321k | uint16_t count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); |
446 | 321k | if (count == 0) { |
447 | 192k | return 0; // nothing to peek |
448 | 192k | } |
449 | | |
450 | | // Check overflow and correct if required |
451 | 128k | if (count > f->depth) { |
452 | 946 | rd_idx = correct_read_index(f, wr_idx); |
453 | 946 | count = f->depth; |
454 | 946 | } |
455 | | |
456 | 128k | if (count < n) { |
457 | 406 | n = count; // limit to available count |
458 | 406 | } |
459 | | |
460 | 128k | const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); |
461 | | |
462 | | #if CFG_TUSB_FIFO_HWFIFO_API |
463 | | if (access_mode != NULL) { |
464 | | hwff_pull_n(f, p_buffer, n, rd_ptr, access_mode); |
465 | | } else |
466 | | #endif |
467 | 128k | { |
468 | 128k | (void)access_mode; |
469 | 128k | ff_pull_n(f, p_buffer, n, rd_ptr); |
470 | 128k | } |
471 | | |
472 | 128k | return n; |
473 | 321k | } |
474 | | |
475 | | // Read n items without removing it from the FIFO, correct read pointer if overflowed |
476 | 0 | uint16_t tu_fifo_peek_n(tu_fifo_t *f, void *p_buffer, uint16_t n) { |
477 | 0 | ff_lock(f->mutex_rd); |
478 | 0 | const uint16_t wr_idx = f->wr_idx; |
479 | 0 | const uint16_t rd_idx = f->rd_idx; |
480 | 0 | const uint16_t ret = tu_fifo_peek_n_access_mode(f, p_buffer, n, wr_idx, rd_idx, NULL); |
481 | 0 | ff_unlock(f->mutex_rd); |
482 | 0 | return ret; |
483 | 0 | } |
484 | | |
485 | | // Read n items from fifo with access mode |
486 | 321k | uint16_t tu_fifo_read_n_access_mode(tu_fifo_t *f, void *buffer, uint16_t n, const tu_hwfifo_access_t *access_mode) { |
487 | 321k | ff_lock(f->mutex_rd); |
488 | | |
489 | | // Peek the data: f->rd_idx might get modified in case of an overflow so we can not use a local variable |
490 | 321k | const uint16_t wr_idx = f->wr_idx; |
491 | 321k | n = tu_fifo_peek_n_access_mode(f, buffer, n, wr_idx, f->rd_idx, access_mode); |
492 | 321k | f->rd_idx = advance_index(f->depth, f->rd_idx, n); |
493 | | |
494 | 321k | ff_unlock(f->mutex_rd); |
495 | 321k | return n; |
496 | 321k | } |
497 | | |
498 | | // Write n items to fifo with access mode |
499 | | uint16_t tu_fifo_write_n_access_mode(tu_fifo_t *f, const void *data, uint16_t n, |
500 | 141k | const tu_hwfifo_access_t *access_mode) { |
501 | 141k | if (n == 0) { |
502 | 0 | return 0; |
503 | 0 | } |
504 | | |
505 | 141k | ff_lock(f->mutex_wr); |
506 | | |
507 | 141k | uint16_t wr_idx = f->wr_idx; |
508 | 141k | uint16_t rd_idx = f->rd_idx; |
509 | | |
510 | 141k | const uint8_t *buf8 = (const uint8_t *)data; |
511 | | |
512 | 141k | TU_LOG(TU_FIFO_DBG, "rd = %3u, wr = %3u, count = %3u, remain = %3u, n = %3u: ", rd_idx, wr_idx, |
513 | 141k | tu_ff_overflow_count(f->depth, wr_idx, rd_idx), tu_ff_remaining_local(f->depth, wr_idx, rd_idx), n); |
514 | | |
515 | 141k | if (!f->overwritable) { |
516 | | // limit up to full |
517 | 126k | const uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx); |
518 | 126k | n = tu_min16(n, remain); |
519 | 126k | } else { |
520 | | // In over-writable mode, fifo_write() is allowed even when fifo is full. In such case, |
521 | | // oldest data in fifo i.e. at read pointer data will be overwritten |
522 | | // Note: we can modify read buffer contents however we must not modify the read index itself within a write |
523 | | // function! Since it would end up in a race condition with read functions! |
524 | 15.6k | if (n >= f->depth) { |
525 | | // Only copy last part |
526 | 3.70k | if (access_mode == NULL) { |
527 | 3.70k | buf8 += (n - f->depth); |
528 | 3.70k | } else { |
529 | | // TODO should read from hw fifo to discard data, however reading an odd number could |
530 | | // accidentally discard data. |
531 | 0 | } |
532 | | |
533 | 3.70k | n = f->depth; |
534 | | |
535 | | // We start writing at the read pointer's position since we fill the whole buffer |
536 | 3.70k | wr_idx = rd_idx; |
537 | 11.9k | } else { |
538 | 11.9k | const uint16_t overflowable_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); |
539 | 11.9k | if (overflowable_count + n >= 2 * f->depth) { |
540 | | // Double overflowed |
541 | | // Index is bigger than the allowed range [0,2*depth) |
542 | | // re-position write index to have a full fifo after pushed |
543 | 946 | wr_idx = advance_index(f->depth, rd_idx, f->depth - n); |
544 | | |
545 | | // TODO we should also shift out n bytes from read index since we avoid changing rd index !! |
546 | | // However memmove() is expensive due to actual copying + wrapping consideration. |
547 | | // Also race condition could happen anyway if read() is invoke while moving result in corrupted memory |
548 | | // currently deliberately not implemented --> result in incorrect data read back |
549 | 10.9k | } else { |
550 | | // normal + single overflowed: |
551 | | // Index is in the range of [0,2*depth) and thus detect and recoverable. Recovering is handled in read() |
552 | | // Therefore we just increase write index |
553 | | // we will correct (re-position) read index later on in fifo_read() function |
554 | 10.9k | } |
555 | 11.9k | } |
556 | 15.6k | } |
557 | | |
558 | 141k | if (n) { |
559 | 141k | const uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); |
560 | 141k | TU_LOG(TU_FIFO_DBG, "actual_n = %u, wr_ptr = %u", n, wr_ptr); |
561 | | |
562 | | #if CFG_TUSB_FIFO_HWFIFO_API |
563 | | if (access_mode != NULL) { |
564 | | hwff_push_n(f, buf8, n, wr_ptr, access_mode); |
565 | | } else |
566 | | #endif |
567 | 141k | { |
568 | 141k | ff_push_n(f, buf8, n, wr_ptr); |
569 | 141k | } |
570 | 141k | f->wr_idx = advance_index(f->depth, wr_idx, n); |
571 | | |
572 | 141k | TU_LOG(TU_FIFO_DBG, "\tnew_wr = %u\r\n", f->wr_idx); |
573 | 141k | } |
574 | | |
575 | 141k | ff_unlock(f->mutex_wr); |
576 | | |
577 | 141k | return n; |
578 | 141k | } |
579 | | |
580 | 0 | uint16_t tu_fifo_discard_n(tu_fifo_t *f, uint16_t n) { |
581 | 0 | const uint16_t count = tu_min16(n, tu_fifo_count(f)); // limit to available count |
582 | 0 | ff_lock(f->mutex_rd); |
583 | 0 | f->rd_idx = advance_index(f->depth, f->rd_idx, count); |
584 | 0 | ff_unlock(f->mutex_rd); |
585 | |
|
586 | 0 | return count; |
587 | 0 | } |
588 | | |
589 | | //--------------------------------------------------------------------+ |
590 | | // One API |
591 | | //--------------------------------------------------------------------+ |
592 | | |
593 | | // peek() using local write/read index, correct read index if overflowed |
594 | | // Be careful, caller must not lock mutex, since this Will also try to lock mutex |
595 | 2.32k | static bool ff_peek_local(tu_fifo_t *f, void *buf, uint16_t wr_idx, uint16_t rd_idx) { |
596 | 2.32k | const uint16_t ovf_count = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); |
597 | 2.32k | if (ovf_count == 0) { |
598 | 2.32k | return false; // nothing to peek |
599 | 2.32k | } |
600 | | |
601 | | // Correct read index if overflow |
602 | 0 | if (ovf_count > f->depth) { |
603 | 0 | ff_lock(f->mutex_rd); |
604 | 0 | rd_idx = correct_read_index(f, wr_idx); |
605 | 0 | ff_unlock(f->mutex_rd); |
606 | 0 | } |
607 | |
|
608 | 0 | const uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); |
609 | 0 | memcpy(buf, f->buffer + rd_ptr, 1); |
610 | |
|
611 | 0 | return true; |
612 | 2.32k | } |
613 | | |
614 | | // Read one element out of the buffer, correct read index if overflowed |
615 | 0 | bool tu_fifo_read(tu_fifo_t *f, void *buffer) { |
616 | | // Peek the data |
617 | | // f->rd_idx might get modified in case of an overflow so we can not use a local variable |
618 | 0 | const uint16_t wr_idx = f->wr_idx; |
619 | 0 | const bool ret = ff_peek_local(f, buffer, wr_idx, f->rd_idx); |
620 | 0 | if (ret) { |
621 | 0 | ff_lock(f->mutex_rd); |
622 | 0 | f->rd_idx = advance_index(f->depth, f->rd_idx, 1); |
623 | 0 | ff_unlock(f->mutex_rd); |
624 | 0 | } |
625 | |
|
626 | 0 | return ret; |
627 | 0 | } |
628 | | |
629 | | // Read one item without removing it from the FIFO, correct read index if overflowed |
630 | 2.32k | bool tu_fifo_peek(tu_fifo_t *f, void *p_buffer) { |
631 | 2.32k | const uint16_t wr_idx = f->wr_idx; |
632 | 2.32k | const uint16_t rd_idx = f->rd_idx; |
633 | 2.32k | return ff_peek_local(f, p_buffer, wr_idx, rd_idx); |
634 | 2.32k | } |
635 | | |
636 | | // Write one element into the buffer |
637 | 0 | bool tu_fifo_write(tu_fifo_t *f, const void *data) { |
638 | 0 | bool ret; |
639 | 0 | ff_lock(f->mutex_wr); |
640 | |
|
641 | 0 | const uint16_t wr_idx = f->wr_idx; |
642 | |
|
643 | 0 | if (tu_fifo_full(f) && !f->overwritable) { |
644 | 0 | ret = false; |
645 | 0 | } else { |
646 | 0 | const uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); |
647 | 0 | memcpy(f->buffer + wr_ptr, data, 1); |
648 | 0 | f->wr_idx = advance_index(f->depth, wr_idx, 1); |
649 | 0 | ret = true; |
650 | 0 | } |
651 | |
|
652 | 0 | ff_unlock(f->mutex_wr); |
653 | |
|
654 | 0 | return ret; |
655 | 0 | } |
656 | | |
657 | | //--------------------------------------------------------------------+ |
658 | | // Index API |
659 | | //--------------------------------------------------------------------+ |
660 | | |
661 | | /******************************************************************************/ |
662 | | /*! |
663 | | @brief Advance write pointer - intended to be used in combination with DMA. |
664 | | It is possible to fill the FIFO by use of a DMA in circular mode. Within |
665 | | DMA ISRs you may update the write pointer to be able to read from the FIFO. |
666 | | As long as the DMA is the only process writing into the FIFO this is safe |
667 | | to use. |
668 | | |
669 | | USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! |
670 | | |
671 | | @param[in] f |
672 | | Pointer to the FIFO buffer to manipulate |
673 | | @param[in] n |
674 | | Number of items the write pointer moves forward |
675 | | */ |
676 | | /******************************************************************************/ |
677 | 0 | void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n) { |
678 | 0 | f->wr_idx = advance_index(f->depth, f->wr_idx, n); |
679 | 0 | } |
680 | | |
681 | | // Correct the read index in case tu_fifo_overflow() returned true! |
682 | 0 | void tu_fifo_correct_read_pointer(tu_fifo_t *f) { |
683 | 0 | ff_lock(f->mutex_rd); |
684 | 0 | correct_read_index(f, f->wr_idx); |
685 | 0 | ff_unlock(f->mutex_rd); |
686 | 0 | } |
687 | | |
688 | | /******************************************************************************/ |
689 | | /*! |
690 | | @brief Advance read pointer - intended to be used in combination with DMA. |
691 | | It is possible to read from the FIFO by use of a DMA in linear mode. Within |
692 | | DMA ISRs you may update the read pointer to be able to again write into the |
693 | | FIFO. As long as the DMA is the only process reading from the FIFO this is |
694 | | safe to use. |
695 | | |
696 | | USE WITH CARE - WE DO NOT CONDUCT SAFETY CHECKS HERE! |
697 | | |
698 | | @param[in] f |
699 | | Pointer to the FIFO buffer to manipulate |
700 | | @param[in] n |
701 | | Number of items the read pointer moves forward |
702 | | */ |
703 | | /******************************************************************************/ |
704 | 0 | void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n) { |
705 | 0 | f->rd_idx = advance_index(f->depth, f->rd_idx, n); |
706 | 0 | } |
707 | | |
708 | | /******************************************************************************/ |
709 | | /*! |
710 | | @brief Get read info |
711 | | |
712 | | Returns the length and pointer from which bytes can be read in a linear manner. |
713 | | This is of major interest for DMA transmissions. If returned length is zero the |
714 | | corresponding pointer is invalid. |
715 | | The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to |
716 | | do so! |
717 | | @param[in] f |
718 | | Pointer to FIFO |
719 | | @param[out] *info |
720 | | Pointer to struct which holds the desired infos |
721 | | */ |
722 | | /******************************************************************************/ |
723 | 0 | void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { |
724 | | // Operate on temporary values in case they change in between |
725 | 0 | uint16_t wr_idx = f->wr_idx; |
726 | 0 | uint16_t rd_idx = f->rd_idx; |
727 | |
|
728 | 0 | uint16_t cnt = tu_ff_overflow_count(f->depth, wr_idx, rd_idx); |
729 | | |
730 | | // Check overflow and correct if required - may happen in case a DMA wrote too fast |
731 | 0 | if (cnt > f->depth) { |
732 | 0 | ff_lock(f->mutex_rd); |
733 | 0 | rd_idx = correct_read_index(f, wr_idx); |
734 | 0 | ff_unlock(f->mutex_rd); |
735 | |
|
736 | 0 | cnt = f->depth; |
737 | 0 | } |
738 | | |
739 | | // Check if fifo is empty |
740 | 0 | if (cnt == 0) { |
741 | 0 | info->linear.len = 0; |
742 | 0 | info->wrapped.len = 0; |
743 | 0 | info->linear.ptr = NULL; |
744 | 0 | info->wrapped.ptr = NULL; |
745 | 0 | return; |
746 | 0 | } |
747 | | |
748 | | // Get relative pointers |
749 | 0 | uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); |
750 | 0 | uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); |
751 | | |
752 | | // Copy pointer to buffer to start reading from |
753 | 0 | info->linear.ptr = &f->buffer[rd_ptr]; |
754 | | |
755 | | // Check if there is a wrap around necessary |
756 | 0 | if (wr_ptr > rd_ptr) { |
757 | | // Non wrapping case |
758 | 0 | info->linear.len = cnt; |
759 | |
|
760 | 0 | info->wrapped.len = 0; |
761 | 0 | info->wrapped.ptr = NULL; |
762 | 0 | } else { |
763 | 0 | info->linear.len = f->depth - rd_ptr; // Also the case if FIFO was full |
764 | |
|
765 | 0 | info->wrapped.len = cnt - info->linear.len; |
766 | 0 | info->wrapped.ptr = f->buffer; |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | | /******************************************************************************/ |
771 | | /*! |
772 | | @brief Get linear write info |
773 | | |
774 | | Returns the length and pointer to which bytes can be written into FIFO in a linear manner. |
775 | | This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the |
776 | | corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO. |
777 | | The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! |
778 | | TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE! |
779 | | @param[in] f |
780 | | Pointer to FIFO |
781 | | @param[out] *info |
782 | | Pointer to struct which holds the desired infos |
783 | | */ |
784 | | /******************************************************************************/ |
785 | 0 | void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info) { |
786 | 0 | uint16_t wr_idx = f->wr_idx; |
787 | 0 | uint16_t rd_idx = f->rd_idx; |
788 | 0 | uint16_t remain = tu_ff_remaining_local(f->depth, wr_idx, rd_idx); |
789 | |
|
790 | 0 | if (remain == 0) { |
791 | 0 | info->linear.len = 0; |
792 | 0 | info->wrapped.len = 0; |
793 | 0 | info->linear.ptr = NULL; |
794 | 0 | info->wrapped.ptr = NULL; |
795 | 0 | return; |
796 | 0 | } |
797 | | |
798 | | // Get relative pointers |
799 | 0 | uint16_t wr_ptr = idx2ptr(f->depth, wr_idx); |
800 | 0 | uint16_t rd_ptr = idx2ptr(f->depth, rd_idx); |
801 | | |
802 | | // Copy pointer to buffer to start writing to |
803 | 0 | info->linear.ptr = &f->buffer[wr_ptr]; |
804 | |
|
805 | 0 | if (wr_ptr < rd_ptr) { |
806 | | // Non wrapping case |
807 | 0 | info->linear.len = rd_ptr - wr_ptr; |
808 | 0 | info->wrapped.len = 0; |
809 | 0 | info->wrapped.ptr = NULL; |
810 | 0 | } else { |
811 | 0 | info->linear.len = f->depth - wr_ptr; |
812 | 0 | info->wrapped.len = remain - info->linear.len; // Remaining length - n already was limited to remain or FIFO depth |
813 | 0 | info->wrapped.ptr = f->buffer; // Always start of buffer |
814 | 0 | } |
815 | 0 | } |