Coverage Report

Created: 2026-07-30 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}