summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authornxf58843 <[email protected]>2021-08-23 09:55:04 -0700
committerGitHub <[email protected]>2021-08-23 09:55:04 -0700
commitfb16e80e575a44828f5367f4fb7380162b0354f0 (patch)
treebb965cc935083d99e083667d4f0f1533d50a29f5 /src/common
parent616562a48aa1d8ce2f4ca71f6e780a8bec9fb5eb (diff)
parentea902493db49843dcdeca6bfa2b2efe540f44b2f (diff)
Merge pull request #2 from hathach/master
Pulling latest from source
Diffstat (limited to 'src/common')
-rw-r--r--src/common/sys_queue.h871
-rw-r--r--src/common/tusb_common.h286
-rw-r--r--src/common/tusb_compiler.h99
-rw-r--r--src/common/tusb_error.h1
-rw-r--r--src/common/tusb_fifo.c944
-rw-r--r--src/common/tusb_fifo.h144
-rw-r--r--src/common/tusb_types.h71
-rw-r--r--src/common/tusb_verify.h30
8 files changed, 1296 insertions, 1150 deletions
diff --git a/src/common/sys_queue.h b/src/common/sys_queue.h
deleted file mode 100644
index 443f01b22..000000000
--- a/src/common/sys_queue.h
+++ /dev/null
@@ -1,871 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- * $FreeBSD$
- */
-
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
-
-#include <sys/cdefs.h>
-
-/*
- * This file defines four types of data structures: singly-linked lists,
- * singly-linked tail queues, lists and tail queues.
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A singly-linked tail queue is headed by a pair of pointers, one to the
- * head of the list and the other to the tail of the list. The elements are
- * singly linked for minimum space and pointer manipulation overhead at the
- * expense of O(n) removal for arbitrary elements. New elements can be added
- * to the list after an existing element, at the head of the list, or at the
- * end of the list. Elements being removed from the head of the tail queue
- * should use the explicit macro for this purpose for optimum efficiency.
- * A singly-linked tail queue may only be traversed in the forward direction.
- * Singly-linked tail queues are ideal for applications with large datasets
- * and few or no removals or for implementing a FIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may be traversed in either direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- *
- * Below is a summary of implemented functions where:
- * + means the macro is available
- * - means the macro is not available
- * s means the macro is available but is slow (runs in O(n) time)
- *
- * SLIST LIST STAILQ TAILQ
- * _HEAD + + + +
- * _CLASS_HEAD + + + +
- * _HEAD_INITIALIZER + + + +
- * _ENTRY + + + +
- * _CLASS_ENTRY + + + +
- * _INIT + + + +
- * _EMPTY + + + +
- * _FIRST + + + +
- * _NEXT + + + +
- * _PREV - + - +
- * _LAST - - + +
- * _LAST_FAST - - - +
- * _FOREACH + + + +
- * _FOREACH_FROM + + + +
- * _FOREACH_SAFE + + + +
- * _FOREACH_FROM_SAFE + + + +
- * _FOREACH_REVERSE - - - +
- * _FOREACH_REVERSE_FROM - - - +
- * _FOREACH_REVERSE_SAFE - - - +
- * _FOREACH_REVERSE_FROM_SAFE - - - +
- * _INSERT_HEAD + + + +
- * _INSERT_BEFORE - + - +
- * _INSERT_AFTER + + + +
- * _INSERT_TAIL - - + +
- * _CONCAT s s + +
- * _REMOVE_AFTER + - + -
- * _REMOVE_HEAD + - + -
- * _REMOVE s + s +
- * _SWAP + + + +
- *
- */
-#ifdef QUEUE_MACRO_DEBUG
-#warn Use QUEUE_MACRO_DEBUG_TRACE and/or QUEUE_MACRO_DEBUG_TRASH
-#define QUEUE_MACRO_DEBUG_TRACE
-#define QUEUE_MACRO_DEBUG_TRASH
-#endif
-
-#ifdef QUEUE_MACRO_DEBUG_TRACE
-/* Store the last 2 places the queue element or head was altered */
-struct qm_trace {
- unsigned long lastline;
- unsigned long prevline;
- const char *lastfile;
- const char *prevfile;
-};
-
-#define TRACEBUF struct qm_trace trace;
-#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
-
-#define QMD_TRACE_HEAD(head) do { \
- (head)->trace.prevline = (head)->trace.lastline; \
- (head)->trace.prevfile = (head)->trace.lastfile; \
- (head)->trace.lastline = __LINE__; \
- (head)->trace.lastfile = __FILE__; \
-} while (0)
-
-#define QMD_TRACE_ELEM(elem) do { \
- (elem)->trace.prevline = (elem)->trace.lastline; \
- (elem)->trace.prevfile = (elem)->trace.lastfile; \
- (elem)->trace.lastline = __LINE__; \
- (elem)->trace.lastfile = __FILE__; \
-} while (0)
-
-#else /* !QUEUE_MACRO_DEBUG_TRACE */
-#define QMD_TRACE_ELEM(elem)
-#define QMD_TRACE_HEAD(head)
-#define TRACEBUF
-#define TRACEBUF_INITIALIZER
-#endif /* QUEUE_MACRO_DEBUG_TRACE */
-
-#ifdef QUEUE_MACRO_DEBUG_TRASH
-#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
-#define QMD_IS_TRASHED(x) ((x) == (void *)(intptr_t)-1)
-#else /* !QUEUE_MACRO_DEBUG_TRASH */
-#define TRASHIT(x)
-#define QMD_IS_TRASHED(x) 0
-#endif /* QUEUE_MACRO_DEBUG_TRASH */
-
-#if defined(QUEUE_MACRO_DEBUG_TRACE) || defined(QUEUE_MACRO_DEBUG_TRASH)
-#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
-#else /* !QUEUE_MACRO_DEBUG_TRACE && !QUEUE_MACRO_DEBUG_TRASH */
-#define QMD_SAVELINK(name, link)
-#endif /* QUEUE_MACRO_DEBUG_TRACE || QUEUE_MACRO_DEBUG_TRASH */
-
-#ifdef __cplusplus
-/*
- * In C++ there can be structure lists and class lists:
- */
-#define QUEUE_TYPEOF(type) type
-#else
-#define QUEUE_TYPEOF(type) struct type
-#endif
-
-/*
- * Singly-linked List declarations.
- */
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
-
-#define SLIST_CLASS_HEAD(name, type) \
-struct name { \
- class type *slh_first; /* first element */ \
-}
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
-
-#define SLIST_CLASS_ENTRY(type) \
-struct { \
- class type *sle_next; /* next element */ \
-}
-
-/*
- * Singly-linked List functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) do { \
- if (*(prevp) != (elm)) \
- panic("Bad prevptr *(%p) == %p != %p", \
- (prevp), *(prevp), (elm)); \
-} while (0)
-#else
-#define QMD_SLIST_CHECK_PREVPTR(prevp, elm)
-#endif
-
-#define SLIST_CONCAT(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \
- if (curelm == NULL) { \
- if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \
- SLIST_INIT(head2); \
- } else if (SLIST_FIRST(head2) != NULL) { \
- while (SLIST_NEXT(curelm, field) != NULL) \
- curelm = SLIST_NEXT(curelm, field); \
- SLIST_NEXT(curelm, field) = SLIST_FIRST(head2); \
- SLIST_INIT(head2); \
- } \
-} while (0)
-
-#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
-
-#define SLIST_FIRST(head) ((head)->slh_first)
-
-#define SLIST_FOREACH(var, head, field) \
- for ((var) = SLIST_FIRST((head)); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var); \
- (var) = SLIST_NEXT((var), field))
-
-#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = SLIST_FIRST((head)); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
- (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != NULL; \
- (varp) = &SLIST_NEXT((var), field))
-
-#define SLIST_INIT(head) do { \
- SLIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
- SLIST_NEXT((slistelm), field) = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
- SLIST_FIRST((head)) = (elm); \
-} while (0)
-
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
- if (SLIST_FIRST((head)) == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } \
- else { \
- QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
- while (SLIST_NEXT(curelm, field) != (elm)) \
- curelm = SLIST_NEXT(curelm, field); \
- SLIST_REMOVE_AFTER(curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define SLIST_REMOVE_AFTER(elm, field) do { \
- SLIST_NEXT(elm, field) = \
- SLIST_NEXT(SLIST_NEXT(elm, field), field); \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
-} while (0)
-
-#define SLIST_REMOVE_PREVPTR(prevp, elm, field) do { \
- QMD_SLIST_CHECK_PREVPTR(prevp, elm); \
- *(prevp) = SLIST_NEXT(elm, field); \
- TRASHIT((elm)->field.sle_next); \
-} while (0)
-
-#define SLIST_SWAP(head1, head2, type) do { \
- QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
- SLIST_FIRST(head1) = SLIST_FIRST(head2); \
- SLIST_FIRST(head2) = swap_first; \
-} while (0)
-
-/*
- * Singly-linked Tail queue declarations.
- */
-#define STAILQ_HEAD(name, type) \
-struct name { \
- struct type *stqh_first;/* first element */ \
- struct type **stqh_last;/* addr of last next element */ \
-}
-
-#define STAILQ_CLASS_HEAD(name, type) \
-struct name { \
- class type *stqh_first; /* first element */ \
- class type **stqh_last; /* addr of last next element */ \
-}
-
-#define STAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).stqh_first }
-
-#define STAILQ_ENTRY(type) \
-struct { \
- struct type *stqe_next; /* next element */ \
-}
-
-#define STAILQ_CLASS_ENTRY(type) \
-struct { \
- class type *stqe_next; /* next element */ \
-}
-
-/*
- * Singly-linked Tail queue functions.
- */
-#define STAILQ_CONCAT(head1, head2) do { \
- if (!STAILQ_EMPTY((head2))) { \
- *(head1)->stqh_last = (head2)->stqh_first; \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_INIT((head2)); \
- } \
-} while (0)
-
-#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
-
-#define STAILQ_FIRST(head) ((head)->stqh_first)
-
-#define STAILQ_FOREACH(var, head, field) \
- for((var) = STAILQ_FIRST((head)); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var); \
- (var) = STAILQ_NEXT((var), field))
-
-#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = STAILQ_FIRST((head)); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
- (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define STAILQ_INIT(head) do { \
- STAILQ_FIRST((head)) = NULL; \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_NEXT((tqelm), field) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_HEAD(head, elm, field) do { \
- if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
- STAILQ_FIRST((head)) = (elm); \
-} while (0)
-
-#define STAILQ_INSERT_TAIL(head, elm, field) do { \
- STAILQ_NEXT((elm), field) = NULL; \
- *(head)->stqh_last = (elm); \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_LAST(head, type, field) \
- (STAILQ_EMPTY((head)) ? NULL : \
- __containerof((head)->stqh_last, \
- QUEUE_TYPEOF(type), field.stqe_next))
-
-#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
-
-#define STAILQ_REMOVE(head, elm, type, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
- if (STAILQ_FIRST((head)) == (elm)) { \
- STAILQ_REMOVE_HEAD((head), field); \
- } \
- else { \
- QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
- while (STAILQ_NEXT(curelm, field) != (elm)) \
- curelm = STAILQ_NEXT(curelm, field); \
- STAILQ_REMOVE_AFTER(head, curelm, field); \
- } \
- TRASHIT(*oldnext); \
-} while (0)
-
-#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
- if ((STAILQ_NEXT(elm, field) = \
- STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
- (head)->stqh_last = &STAILQ_NEXT((elm), field); \
-} while (0)
-
-#define STAILQ_REMOVE_HEAD(head, field) do { \
- if ((STAILQ_FIRST((head)) = \
- STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
- (head)->stqh_last = &STAILQ_FIRST((head)); \
-} while (0)
-
-#define STAILQ_SWAP(head1, head2, type) do { \
- QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
- QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
- STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
- (head1)->stqh_last = (head2)->stqh_last; \
- STAILQ_FIRST(head2) = swap_first; \
- (head2)->stqh_last = swap_last; \
- if (STAILQ_EMPTY(head1)) \
- (head1)->stqh_last = &STAILQ_FIRST(head1); \
- if (STAILQ_EMPTY(head2)) \
- (head2)->stqh_last = &STAILQ_FIRST(head2); \
-} while (0)
-
-
-/*
- * List declarations.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
-
-#define LIST_CLASS_HEAD(name, type) \
-struct name { \
- class type *lh_first; /* first element */ \
-}
-
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
-
-#define LIST_CLASS_ENTRY(type) \
-struct { \
- class type *le_next; /* next element */ \
- class type **le_prev; /* address of previous next element */ \
-}
-
-/*
- * List functions.
- */
-
-#if (defined(_KERNEL) && defined(INVARIANTS))
-/*
- * QMD_LIST_CHECK_HEAD(LIST_HEAD *head, LIST_ENTRY NAME)
- *
- * If the list is non-empty, validates that the first element of the list
- * points back at 'head.'
- */
-#define QMD_LIST_CHECK_HEAD(head, field) do { \
- if (LIST_FIRST((head)) != NULL && \
- LIST_FIRST((head))->field.le_prev != \
- &LIST_FIRST((head))) \
- panic("Bad list head %p first->prev != head", (head)); \
-} while (0)
-
-/*
- * QMD_LIST_CHECK_NEXT(TYPE *elm, LIST_ENTRY NAME)
- *
- * If an element follows 'elm' in the list, validates that the next element
- * points back at 'elm.'
- */
-#define QMD_LIST_CHECK_NEXT(elm, field) do { \
- if (LIST_NEXT((elm), field) != NULL && \
- LIST_NEXT((elm), field)->field.le_prev != \
- &((elm)->field.le_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-/*
- * QMD_LIST_CHECK_PREV(TYPE *elm, LIST_ENTRY NAME)
- *
- * Validates that the previous element (or head of the list) points to 'elm.'
- */
-#define QMD_LIST_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.le_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_LIST_CHECK_HEAD(head, field)
-#define QMD_LIST_CHECK_NEXT(elm, field)
-#define QMD_LIST_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define LIST_CONCAT(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \
- if (curelm == NULL) { \
- if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) { \
- LIST_FIRST(head2)->field.le_prev = \
- &LIST_FIRST((head1)); \
- LIST_INIT(head2); \
- } \
- } else if (LIST_FIRST(head2) != NULL) { \
- while (LIST_NEXT(curelm, field) != NULL) \
- curelm = LIST_NEXT(curelm, field); \
- LIST_NEXT(curelm, field) = LIST_FIRST(head2); \
- LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field); \
- LIST_INIT(head2); \
- } \
-} while (0)
-
-#define LIST_EMPTY(head) ((head)->lh_first == NULL)
-
-#define LIST_FIRST(head) ((head)->lh_first)
-
-#define LIST_FOREACH(var, head, field) \
- for ((var) = LIST_FIRST((head)); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var); \
- (var) = LIST_NEXT((var), field))
-
-#define LIST_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = LIST_FIRST((head)); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
- (var) && ((tvar) = LIST_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define LIST_INIT(head) do { \
- LIST_FIRST((head)) = NULL; \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- QMD_LIST_CHECK_NEXT(listelm, field); \
- if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
- LIST_NEXT((listelm), field)->field.le_prev = \
- &LIST_NEXT((elm), field); \
- LIST_NEXT((listelm), field) = (elm); \
- (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_LIST_CHECK_PREV(listelm, field); \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- LIST_NEXT((elm), field) = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- QMD_LIST_CHECK_HEAD((head), field); \
- if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
- LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
- LIST_FIRST((head)) = (elm); \
- (elm)->field.le_prev = &LIST_FIRST((head)); \
-} while (0)
-
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_PREV(elm, head, type, field) \
- ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
- __containerof((elm)->field.le_prev, \
- QUEUE_TYPEOF(type), field.le_next))
-
-#define LIST_REMOVE(elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.le_next); \
- QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
- QMD_LIST_CHECK_NEXT(elm, field); \
- QMD_LIST_CHECK_PREV(elm, field); \
- if (LIST_NEXT((elm), field) != NULL) \
- LIST_NEXT((elm), field)->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = LIST_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
-} while (0)
-
-#define LIST_SWAP(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
- LIST_FIRST((head1)) = LIST_FIRST((head2)); \
- LIST_FIRST((head2)) = swap_tmp; \
- if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
- if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
- swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
-} while (0)
-
-/*
- * Tail queue declarations.
- */
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
-}
-
-#define TAILQ_CLASS_HEAD(name, type) \
-struct name { \
- class type *tqh_first; /* first element */ \
- class type **tqh_last; /* addr of last next element */ \
- TRACEBUF \
-}
-
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
-
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
-}
-
-#define TAILQ_CLASS_ENTRY(type) \
-struct { \
- class type *tqe_next; /* next element */ \
- class type **tqe_prev; /* address of previous next element */ \
- TRACEBUF \
-}
-
-/*
- * Tail queue functions.
- */
-#if (defined(_KERNEL) && defined(INVARIANTS))
-/*
- * QMD_TAILQ_CHECK_HEAD(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
- *
- * If the tailq is non-empty, validates that the first element of the tailq
- * points back at 'head.'
- */
-#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
- if (!TAILQ_EMPTY(head) && \
- TAILQ_FIRST((head))->field.tqe_prev != \
- &TAILQ_FIRST((head))) \
- panic("Bad tailq head %p first->prev != head", (head)); \
-} while (0)
-
-/*
- * QMD_TAILQ_CHECK_TAIL(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
- *
- * Validates that the tail of the tailq is a pointer to pointer to NULL.
- */
-#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
- if (*(head)->tqh_last != NULL) \
- panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
-} while (0)
-
-/*
- * QMD_TAILQ_CHECK_NEXT(TYPE *elm, TAILQ_ENTRY NAME)
- *
- * If an element follows 'elm' in the tailq, validates that the next element
- * points back at 'elm.'
- */
-#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
- if (TAILQ_NEXT((elm), field) != NULL && \
- TAILQ_NEXT((elm), field)->field.tqe_prev != \
- &((elm)->field.tqe_next)) \
- panic("Bad link elm %p next->prev != elm", (elm)); \
-} while (0)
-
-/*
- * QMD_TAILQ_CHECK_PREV(TYPE *elm, TAILQ_ENTRY NAME)
- *
- * Validates that the previous element (or head of the tailq) points to 'elm.'
- */
-#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
- if (*(elm)->field.tqe_prev != (elm)) \
- panic("Bad link elm %p prev->next != elm", (elm)); \
-} while (0)
-#else
-#define QMD_TAILQ_CHECK_HEAD(head, field)
-#define QMD_TAILQ_CHECK_TAIL(head, headname)
-#define QMD_TAILQ_CHECK_NEXT(elm, field)
-#define QMD_TAILQ_CHECK_PREV(elm, field)
-#endif /* (_KERNEL && INVARIANTS) */
-
-#define TAILQ_CONCAT(head1, head2, field) do { \
- if (!TAILQ_EMPTY(head2)) { \
- *(head1)->tqh_last = (head2)->tqh_first; \
- (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
- (head1)->tqh_last = (head2)->tqh_last; \
- TAILQ_INIT((head2)); \
- QMD_TRACE_HEAD(head1); \
- QMD_TRACE_HEAD(head2); \
- } \
-} while (0)
-
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define TAILQ_FOREACH(var, head, field) \
- for ((var) = TAILQ_FIRST((head)); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_FROM(var, head, field) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var); \
- (var) = TAILQ_NEXT((var), field))
-
-#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST((head)); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var); \
- (var) = TAILQ_PREV((var), headname, field))
-
-#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
- for ((var) = TAILQ_LAST((head), headname); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
- for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
- (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
- (var) = (tvar))
-
-#define TAILQ_INIT(head) do { \
- TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- QMD_TAILQ_CHECK_NEXT(listelm, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else { \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- } \
- TAILQ_NEXT((listelm), field) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&(listelm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- QMD_TAILQ_CHECK_PREV(listelm, field); \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- TAILQ_NEXT((elm), field) = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_ELEM(&(elm)->field); \
- QMD_TRACE_ELEM(&(listelm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- QMD_TAILQ_CHECK_HEAD(head, field); \
- if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
- TAILQ_FIRST((head))->field.tqe_prev = \
- &TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- QMD_TAILQ_CHECK_TAIL(head, field); \
- TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &TAILQ_NEXT((elm), field); \
- QMD_TRACE_HEAD(head); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-/*
- * The FAST function is fast in that it causes no data access other
- * then the access to the head. The standard LAST function above
- * will cause a data access of both the element you want and
- * the previous element. FAST is very useful for instances when
- * you may want to prefetch the last data element.
- */
-#define TAILQ_LAST_FAST(head, type, field) \
- (TAILQ_EMPTY(head) ? NULL : __containerof((head)->tqh_last, QUEUE_TYPEOF(type), field.tqe_next))
-
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
- QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
- QMD_TAILQ_CHECK_NEXT(elm, field); \
- QMD_TAILQ_CHECK_PREV(elm, field); \
- if ((TAILQ_NEXT((elm), field)) != NULL) \
- TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else { \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- QMD_TRACE_HEAD(head); \
- } \
- *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
- TRASHIT(*oldnext); \
- TRASHIT(*oldprev); \
- QMD_TRACE_ELEM(&(elm)->field); \
-} while (0)
-
-#define TAILQ_SWAP(head1, head2, type, field) do { \
- QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
- QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
- (head1)->tqh_first = (head2)->tqh_first; \
- (head1)->tqh_last = (head2)->tqh_last; \
- (head2)->tqh_first = swap_first; \
- (head2)->tqh_last = swap_last; \
- if ((swap_first = (head1)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head1)->tqh_first; \
- else \
- (head1)->tqh_last = &(head1)->tqh_first; \
- if ((swap_first = (head2)->tqh_first) != NULL) \
- swap_first->field.tqe_prev = &(head2)->tqh_first; \
- else \
- (head2)->tqh_last = &(head2)->tqh_first; \
-} while (0)
-
-#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index 5e6a8bb10..c2356ffee 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -24,10 +24,6 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup Group_Common
- * \defgroup Group_CommonH common.h
- * @{ */
-
#ifndef _TUSB_COMMON_H_
#define _TUSB_COMMON_H_
@@ -47,15 +43,15 @@
#define U16_TO_U8S_BE(u16) TU_U16_HIGH(u16), TU_U16_LOW(u16)
#define U16_TO_U8S_LE(u16) TU_U16_LOW(u16), TU_U16_HIGH(u16)
-#define U32_B1_U8(u32) ((uint8_t) (((u32) >> 24) & 0x000000ff)) // MSB
-#define U32_B2_U8(u32) ((uint8_t) (((u32) >> 16) & 0x000000ff))
-#define U32_B3_U8(u32) ((uint8_t) (((u32) >> 8) & 0x000000ff))
-#define U32_B4_U8(u32) ((uint8_t) ((u32) & 0x000000ff)) // LSB
+#define TU_U32_BYTE3(u32) ((uint8_t) ((((uint32_t) u32) >> 24) & 0x000000ff)) // MSB
+#define TU_U32_BYTE2(u32) ((uint8_t) ((((uint32_t) u32) >> 16) & 0x000000ff))
+#define TU_U32_BYTE1(u32) ((uint8_t) ((((uint32_t) u32) >> 8) & 0x000000ff))
+#define TU_U32_BYTE0(u32) ((uint8_t) (((uint32_t) u32) & 0x000000ff)) // LSB
-#define U32_TO_U8S_BE(u32) U32_B1_U8(u32), U32_B2_U8(u32), U32_B3_U8(u32), U32_B4_U8(u32)
-#define U32_TO_U8S_LE(u32) U32_B4_U8(u32), U32_B3_U8(u32), U32_B2_U8(u32), U32_B1_U8(u32)
+#define U32_TO_U8S_BE(u32) TU_U32_BYTE3(u32), TU_U32_BYTE2(u32), TU_U32_BYTE1(u32), TU_U32_BYTE0(u32)
+#define U32_TO_U8S_LE(u32) TU_U32_BYTE0(u32), TU_U32_BYTE1(u32), TU_U32_BYTE2(u32), TU_U32_BYTE3(u32)
-#define TU_BIT(n) (1U << (n))
+#define TU_BIT(n) (1UL << (n))
//--------------------------------------------------------------------+
// Includes
@@ -72,55 +68,82 @@
#include "tusb_option.h"
#include "tusb_compiler.h"
#include "tusb_verify.h"
-#include "tusb_error.h" // TODO remove
-#include "tusb_timeout.h"
#include "tusb_types.h"
+#include "tusb_error.h" // TODO remove
+#include "tusb_timeout.h" // TODO remove
+
+//--------------------------------------------------------------------+
+// Internal Helper used by Host and Device Stack
+//--------------------------------------------------------------------+
+
+// Check if endpoint descriptor is valid per USB specs
+bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed);
+
+// Bind all endpoint of a interface descriptor to class driver
+void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
+
+// Calculate total length of n interfaces (depending on IAD)
+uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len);
+
//--------------------------------------------------------------------+
-// Inline Functions
+// Internal Inline Functions
//--------------------------------------------------------------------+
+
+//------------- Mem -------------//
#define tu_memclr(buffer, size) memset((buffer), 0, (size))
#define tu_varclr(_var) tu_memclr(_var, sizeof(*(_var)))
-static inline uint32_t tu_u32(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4)
+//------------- Bytes -------------//
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0)
{
- return ( ((uint32_t) b1) << 24) + ( ((uint32_t) b2) << 16) + ( ((uint32_t) b3) << 8) + b4;
+ return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0;
}
-static inline uint16_t tu_u16(uint8_t high, uint8_t low)
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low)
{
- return (uint16_t)((((uint16_t) high) << 8) + low);
+ return (uint16_t) ((((uint16_t) high) << 8) | low);
}
-static inline uint8_t tu_u16_high(uint16_t u16) { return (uint8_t) (((uint16_t) (u16 >> 8)) & 0x00ff); }
-static inline uint8_t tu_u16_low (uint16_t u16) { return (uint8_t) (u16 & 0x00ff); }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t u32) { return TU_U32_BYTE3(u32); }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte2(uint32_t u32) { return TU_U32_BYTE2(u32); }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte1(uint32_t u32) { return TU_U32_BYTE1(u32); }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte0(uint32_t u32) { return TU_U32_BYTE0(u32); }
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_high(uint16_t u16) { return TU_U16_HIGH(u16); }
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u16_low (uint16_t u16) { return TU_U16_LOW(u16); }
-// Min
-static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; }
-static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; }
-static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; }
+//------------- Bits -------------//
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); }
+TU_ATTR_ALWAYS_INLINE static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; }
-// Max
-static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; }
-static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; }
-static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
+//------------- Min -------------//
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_min8 (uint8_t x, uint8_t y ) { return (x < y) ? x : y; }
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_min16 (uint16_t x, uint16_t y) { return (x < y) ? x : y; }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_min32 (uint32_t x, uint32_t y) { return (x < y) ? x : y; }
-// Align
-static inline uint32_t tu_align_n(uint32_t value, uint32_t alignment)
+//------------- Max -------------//
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_max8 (uint8_t x, uint8_t y ) { return (x > y) ? x : y; }
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_max16 (uint16_t x, uint16_t y) { return (x > y) ? x : y; }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_max32 (uint32_t x, uint32_t y) { return (x > y) ? x : y; }
+
+//------------- Align -------------//
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align(uint32_t value, uint32_t alignment)
{
return value & ((uint32_t) ~(alignment-1));
}
-static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
-static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
-static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
-static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align16 (uint32_t value) { return (value & 0xFFFFFFF0UL); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align32 (uint32_t value) { return (value & 0xFFFFFFE0UL); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_align4k (uint32_t value) { return (value & 0xFFFFF000UL); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_offset4k(uint32_t value) { return (value & 0xFFFUL); }
//------------- Mathematics -------------//
-static inline uint32_t tu_abs(int32_t value) { return (uint32_t)((value < 0) ? (-value) : value); }
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_div_ceil(uint32_t v, uint32_t d) { return (v + d -1)/d; }
-/// inclusive range checking
-static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
+/// inclusive range checking TODO remove
+TU_ATTR_ALWAYS_INLINE static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
{
return (lower <= value) && (value <= upper);
}
@@ -130,18 +153,87 @@ static inline bool tu_within(uint32_t lower, uint32_t value, uint32_t upper)
static inline uint8_t tu_log2(uint32_t value)
{
uint8_t result = 0;
-
- while (value >>= 1)
- {
- result++;
- }
+ while (value >>= 1) { result++; }
return result;
}
-// Bit
-static inline uint32_t tu_bit_set (uint32_t value, uint8_t pos) { return value | TU_BIT(pos); }
-static inline uint32_t tu_bit_clear(uint32_t value, uint8_t pos) { return value & (~TU_BIT(pos)); }
-static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value & TU_BIT(pos)) ? true : false; }
+//------------- Unaligned Access -------------//
+#if TUP_ARCH_STRICT_ALIGN
+
+// Rely on compiler to generate correct code for unaligned access
+
+typedef struct { uint16_t val; } TU_ATTR_PACKED tu_unaligned_uint16_t;
+typedef struct { uint32_t val; } TU_ATTR_PACKED tu_unaligned_uint32_t;
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
+{
+ tu_unaligned_uint32_t const* ua32 = (tu_unaligned_uint32_t const*) mem;
+ return ua32->val;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
+{
+ tu_unaligned_uint32_t* ua32 = (tu_unaligned_uint32_t*) mem;
+ ua32->val = value;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
+{
+ tu_unaligned_uint16_t const* ua16 = (tu_unaligned_uint16_t const*) mem;
+ return ua16->val;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
+{
+ tu_unaligned_uint16_t* ua16 = (tu_unaligned_uint16_t*) mem;
+ ua16->val = value;
+}
+
+#elif TUP_MCU_STRICT_ALIGN
+
+// MCU such as LPC_IP3511 Highspeed cannot access unaligned memory on USB_RAM although it is ARM M4.
+// We have to manually pick up bytes since tu_unaligned_uint32_t will still generate unaligned code
+// NOTE: volatile cast to memory to prevent compiler to optimize and generate unaligned code
+// TODO Big Endian may need minor changes
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32(const void* mem)
+{
+ volatile uint8_t const* buf8 = (uint8_t const*) mem;
+ return tu_u32(buf8[3], buf8[2], buf8[1], buf8[0]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32(void* mem, uint32_t value)
+{
+ volatile uint8_t* buf8 = (uint8_t*) mem;
+ buf8[0] = tu_u32_byte0(value);
+ buf8[1] = tu_u32_byte1(value);
+ buf8[2] = tu_u32_byte2(value);
+ buf8[3] = tu_u32_byte3(value);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16(const void* mem)
+{
+ volatile uint8_t const* buf8 = (uint8_t const*) mem;
+ return tu_u16(buf8[1], buf8[0]);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16(void* mem, uint16_t value)
+{
+ volatile uint8_t* buf8 = (uint8_t*) mem;
+ buf8[0] = tu_u16_low(value);
+ buf8[1] = tu_u16_high(value);
+}
+
+
+#else
+
+// MCU that could access unaligned memory natively
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_unaligned_read32 (const void* mem ) { return *((uint32_t*) mem); }
+TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_unaligned_read16 (const void* mem ) { return *((uint16_t*) mem); }
+
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write32 (void* mem, uint32_t value ) { *((uint32_t*) mem) = value; }
+TU_ATTR_ALWAYS_INLINE static inline void tu_unaligned_write16 (void* mem, uint16_t value ) { *((uint16_t*) mem) = value; }
+
+#endif
/*------------------------------------------------------------------*/
/* Count number of arguments of __VA_ARGS__
@@ -153,9 +245,9 @@ static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value
*------------------------------------------------------------------*/
#ifndef TU_ARGS_NUM
-#define TU_ARGS_NUM(...) NARG_(_0, ##__VA_ARGS__,_RSEQ_N())
+#define TU_ARGS_NUM(...) _TU_NARG(_0, ##__VA_ARGS__,_RSEQ_N())
-#define NARG_(...) _GET_NTH_ARG(__VA_ARGS__)
+#define _TU_NARG(...) _GET_NTH_ARG(__VA_ARGS__)
#define _GET_NTH_ARG( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
@@ -209,42 +301,73 @@ static inline bool tu_bit_test (uint32_t value, uint8_t pos) { return (value
// CFG_TUSB_DEBUG for debugging
// 0 : no debug
-// 1 : print when there is error
-// 2 : print out log
+// 1 : print error
+// 2 : print warning
+// 3 : print info
#if CFG_TUSB_DEBUG
-void tu_print_mem(void const *buf, uint16_t count, uint8_t indent);
+void tu_print_mem(void const *buf, uint32_t count, uint8_t indent);
-#ifndef tu_printf
- #define tu_printf printf
+#ifdef CFG_TUSB_DEBUG_PRINTF
+ extern int CFG_TUSB_DEBUG_PRINTF(const char *format, ...);
+ #define tu_printf CFG_TUSB_DEBUG_PRINTF
+#else
+ #define tu_printf printf
#endif
-// Log with debug level 1
+static inline
+void tu_print_var(uint8_t const* buf, uint32_t bufsize)
+{
+ for(uint32_t i=0; i<bufsize; i++) tu_printf("%02X ", buf[i]);
+}
+
+// Log with Level
+#define TU_LOG(n, ...) TU_XSTRCAT(TU_LOG, n)(__VA_ARGS__)
+#define TU_LOG_MEM(n, ...) TU_XSTRCAT3(TU_LOG, n, _MEM)(__VA_ARGS__)
+#define TU_LOG_VAR(n, ...) TU_XSTRCAT3(TU_LOG, n, _VAR)(__VA_ARGS__)
+#define TU_LOG_INT(n, ...) TU_XSTRCAT3(TU_LOG, n, _INT)(__VA_ARGS__)
+#define TU_LOG_HEX(n, ...) TU_XSTRCAT3(TU_LOG, n, _HEX)(__VA_ARGS__)
+#define TU_LOG_LOCATION() tu_printf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__)
+#define TU_LOG_FAILED() tu_printf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__)
+
+// Log Level 1: Error
#define TU_LOG1 tu_printf
#define TU_LOG1_MEM tu_print_mem
-#define TU_LOG1_LOCATION() tu_printf("%s: %d:\n", __PRETTY_FUNCTION__, __LINE__)
+#define TU_LOG1_VAR(_x) tu_print_var((uint8_t const*)(_x), sizeof(*(_x)))
+#define TU_LOG1_INT(_x) tu_printf(#_x " = %ld\r\n", (uint32_t) (_x) )
+#define TU_LOG1_HEX(_x) tu_printf(#_x " = %lX\r\n", (uint32_t) (_x) )
-// Log with debug level 2
-#if CFG_TUSB_DEBUG > 1
+// Log Level 2: Warn
+#if CFG_TUSB_DEBUG >= 2
#define TU_LOG2 TU_LOG1
#define TU_LOG2_MEM TU_LOG1_MEM
- #define TU_LOG2_LOCATION() TU_LOG1_LOCATION()
+ #define TU_LOG2_VAR TU_LOG1_VAR
+ #define TU_LOG2_INT TU_LOG1_INT
+ #define TU_LOG2_HEX TU_LOG1_HEX
#endif
+// Log Level 3: Info
+#if CFG_TUSB_DEBUG >= 3
+ #define TU_LOG3 TU_LOG1
+ #define TU_LOG3_MEM TU_LOG1_MEM
+ #define TU_LOG3_VAR TU_LOG1_VAR
+ #define TU_LOG3_INT TU_LOG1_INT
+ #define TU_LOG3_HEX TU_LOG1_HEX
+#endif
typedef struct
{
uint32_t key;
- char const * data;
-}lookup_entry_t;
+ const char* data;
+} tu_lookup_entry_t;
typedef struct
{
uint16_t count;
- lookup_entry_t const* items;
-} lookup_table_t;
+ tu_lookup_entry_t const* items;
+} tu_lookup_table_t;
-static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t key)
+static inline const char* tu_lookup_find(tu_lookup_table_t const* p_table, uint32_t key)
{
for(uint16_t i=0; i<p_table->count; i++)
{
@@ -256,14 +379,47 @@ static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t ke
#endif // CFG_TUSB_DEBUG
+#ifndef TU_LOG
+#define TU_LOG(n, ...)
+#define TU_LOG_MEM(n, ...)
+#define TU_LOG_VAR(n, ...)
+#define TU_LOG_INT(n, ...)
+#define TU_LOG_HEX(n, ...)
+#define TU_LOG_LOCATION()
+#define TU_LOG_FAILED()
+#endif
+
+// TODO replace all TU_LOGn with TU_LOG(n)
+
+#define TU_LOG0(...)
+#define TU_LOG0_MEM(...)
+#define TU_LOG0_VAR(...)
+#define TU_LOG0_INT(...)
+#define TU_LOG0_HEX(...)
+
+
#ifndef TU_LOG1
#define TU_LOG1(...)
#define TU_LOG1_MEM(...)
+ #define TU_LOG1_VAR(...)
+ #define TU_LOG1_INT(...)
+ #define TU_LOG1_HEX(...)
#endif
#ifndef TU_LOG2
#define TU_LOG2(...)
#define TU_LOG2_MEM(...)
+ #define TU_LOG2_VAR(...)
+ #define TU_LOG2_INT(...)
+ #define TU_LOG2_HEX(...)
+#endif
+
+#ifndef TU_LOG3
+ #define TU_LOG3(...)
+ #define TU_LOG3_MEM(...)
+ #define TU_LOG3_VAR(...)
+ #define TU_LOG3_INT(...)
+ #define TU_LOG3_HEX(...)
#endif
#ifdef __cplusplus
@@ -271,5 +427,3 @@ static inline char const* lookup_find(lookup_table_t const* p_table, uint32_t ke
#endif
#endif /* _TUSB_COMMON_H_ */
-
-/** @} */
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index e403c749b..d3adbbc2d 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -32,10 +32,14 @@
#ifndef _TUSB_COMPILER_H_
#define _TUSB_COMPILER_H_
-#define TU_STRING(x) #x ///< stringify without expand
-#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
-#define TU_STRCAT(a, b) a##b ///< concat without expand
-#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
+#define TU_STRING(x) #x ///< stringify without expand
+#define TU_XSTRING(x) TU_STRING(x) ///< expand then stringify
+
+#define TU_STRCAT(a, b) a##b ///< concat without expand
+#define TU_STRCAT3(a, b, c) a##b##c ///< concat without expand
+
+#define TU_XSTRCAT(a, b) TU_STRCAT(a, b) ///< expand then concat
+#define TU_XSTRCAT3(a, b, c) TU_STRCAT3(a, b, c) ///< expand then concat 3 tokens
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
#define _TU_COUNTER_ __COUNTER__
@@ -44,8 +48,12 @@
#endif
// Compile-time Assert
-#if __STDC_VERSION__ >= 201112L
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define TU_VERIFY_STATIC _Static_assert
+#elif defined (__cplusplus) && __cplusplus >= 201103L
+ #define TU_VERIFY_STATIC static_assert
+#elif defined(__CCRX__)
+ #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0];
#else
#define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
#endif
@@ -59,16 +67,23 @@
//--------------------------------------------------------------------+
// Compiler porting with Attribute and Endian
//--------------------------------------------------------------------+
+
+// TODO refactor since __attribute__ is supported across many compiler
#if defined(__GNUC__)
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
- #define TU_ATTR_PREPACKED
#define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
+ #define TU_ATTR_PACKED_BEGIN
+ #define TU_ATTR_PACKED_END
+ #define TU_ATTR_BIT_FIELD_ORDER_BEGIN
+ #define TU_ATTR_BIT_FIELD_ORDER_END
+
// Endian conversion use well-known host to network (big endian) naming
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
@@ -79,16 +94,25 @@
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
+ // List of obsolete callback function that is renamed and should not be defined.
+ // Put it here since only gcc support this pragma
+ #pragma GCC poison tud_vendor_control_request_cb
+
#elif defined(__TI_COMPILER_VERSION__)
#define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
#define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
#define TU_ATTR_PACKED __attribute__ ((packed))
- #define TU_ATTR_PREPACKED
#define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
#define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
#define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
#define TU_ATTR_USED __attribute__ ((used))
+ #define TU_ATTR_PACKED_BEGIN
+ #define TU_ATTR_PACKED_END
+ #define TU_ATTR_BIT_FIELD_ORDER_BEGIN
+ #define TU_ATTR_BIT_FIELD_ORDER_END
+
// __BYTE_ORDER is defined in the TI ARM compiler, but not MSP430 (which is little endian)
#if ((__BYTE_ORDER__) == (__ORDER_LITTLE_ENDIAN__)) || defined(__MSP430__)
#define TU_BYTE_ORDER TU_LITTLE_ENDIAN
@@ -99,7 +123,58 @@
#define TU_BSWAP16(u16) (__builtin_bswap16(u16))
#define TU_BSWAP32(u32) (__builtin_bswap32(u32))
-#else
+#elif defined(__ICCARM__)
+ #include <intrinsics.h>
+ #define TU_ATTR_ALIGNED(Bytes) __attribute__ ((aligned(Bytes)))
+ #define TU_ATTR_SECTION(sec_name) __attribute__ ((section(#sec_name)))
+ #define TU_ATTR_PACKED __attribute__ ((packed))
+ #define TU_ATTR_WEAK __attribute__ ((weak))
+ #define TU_ATTR_ALWAYS_INLINE __attribute__ ((always_inline))
+ #define TU_ATTR_DEPRECATED(mess) __attribute__ ((deprecated(mess))) // warn if function with this attribute is used
+ #define TU_ATTR_UNUSED __attribute__ ((unused)) // Function/Variable is meant to be possibly unused
+ #define TU_ATTR_USED __attribute__ ((used)) // Function/Variable is meant to be used
+
+ #define TU_ATTR_PACKED_BEGIN
+ #define TU_ATTR_PACKED_END
+ #define TU_ATTR_BIT_FIELD_ORDER_BEGIN
+ #define TU_ATTR_BIT_FIELD_ORDER_END
+
+ // Endian conversion use well-known host to network (big endian) naming
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #else
+ #define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #endif
+
+ #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16))
+ #define TU_BSWAP32(u32) (__iar_builtin_REV(u32))
+
+#elif defined(__CCRX__)
+ #define TU_ATTR_ALIGNED(Bytes)
+ #define TU_ATTR_SECTION(sec_name)
+ #define TU_ATTR_PACKED
+ #define TU_ATTR_WEAK
+ #define TU_ATTR_ALWAYS_INLINE
+ #define TU_ATTR_DEPRECATED(mess)
+ #define TU_ATTR_UNUSED
+ #define TU_ATTR_USED
+
+ #define TU_ATTR_PACKED_BEGIN _Pragma("pack")
+ #define TU_ATTR_PACKED_END _Pragma("packoption")
+ #define TU_ATTR_BIT_FIELD_ORDER_BEGIN _Pragma("bit_order right")
+ #define TU_ATTR_BIT_FIELD_ORDER_END _Pragma("bit_order")
+
+ // Endian conversion use well-known host to network (big endian) naming
+ #if defined(__LIT)
+ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN
+ #else
+ #define TU_BYTE_ORDER TU_BIG_ENDIAN
+ #endif
+
+ #define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16))
+ #define TU_BSWAP32(u32) (_builtin_revl(u32))
+
+#else
#error "Compiler attribute porting is required"
#endif
@@ -125,11 +200,11 @@
#define tu_htonl(u32) (u32)
#define tu_ntohl(u32) (u32)
- #define tu_htole16(u16) (tu_bswap16(u16))
- #define tu_le16toh(u16) (tu_bswap16(u16))
+ #define tu_htole16(u16) (TU_BSWAP16(u16))
+ #define tu_le16toh(u16) (TU_BSWAP16(u16))
- #define tu_htole32(u32) (tu_bswap32(u32))
- #define tu_le32toh(u32) (tu_bswap32(u32))
+ #define tu_htole32(u32) (TU_BSWAP32(u32))
+ #define tu_le32toh(u32) (TU_BSWAP32(u32))
#else
#error Byte order is undefined
diff --git a/src/common/tusb_error.h b/src/common/tusb_error.h
index f600c4ae3..d7ad8c318 100644
--- a/src/common/tusb_error.h
+++ b/src/common/tusb_error.h
@@ -54,6 +54,7 @@
ENTRY(TUSB_ERROR_FAILED )\
/// \brief Error Code returned
+/// TODO obsolete and to be remove
typedef enum
{
ERROR_TABLE(ERROR_ENUM)
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 01e990311..11b8fc5f3 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,187 +25,708 @@
* This file is part of the TinyUSB stack.
*/
-#include <string.h>
-
#include "osal/osal.h"
#include "tusb_fifo.h"
+// Supress IAR warning
+// Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
+#if defined(__ICCARM__)
+#pragma diag_suppress = Pa082
+#endif
+
// implement mutex lock and unlock
#if CFG_FIFO_MUTEX
-static void tu_fifo_lock(tu_fifo_t *f)
+static inline void _ff_lock(tu_fifo_mutex_t mutex)
{
- if (f->mutex)
- {
- osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
- }
+ if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
-static void tu_fifo_unlock(tu_fifo_t *f)
+static inline void _ff_unlock(tu_fifo_mutex_t mutex)
{
- if (f->mutex)
- {
- osal_mutex_unlock(f->mutex);
- }
+ if (mutex) osal_mutex_unlock(mutex);
}
#else
-#define tu_fifo_lock(_ff)
-#define tu_fifo_unlock(_ff)
+#define _ff_lock(_mutex)
+#define _ff_unlock(_mutex)
#endif
+/** \enum tu_fifo_copy_mode_t
+ * \brief Write modes intended to allow special read and write functions to be able to
+ * copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
+ */
+typedef enum
+{
+ TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
+ TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
+} tu_fifo_copy_mode_t;
+
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
{
- tu_fifo_lock(f);
+ if (depth > 0x8000) return false; // Maximum depth is 2^15 items
+
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
f->buffer = (uint8_t*) buffer;
f->depth = depth;
f->item_size = item_size;
f->overwritable = overwritable;
- f->rd_idx = f->wr_idx = f->count = 0;
+ // Limit index space to 2*depth - this allows for a fast "modulo" calculation
+ // but limits the maximum depth to 2^16/2 = 2^15 and buffer overflows are detectable
+ // only if overflow happens once (important for unsupervised DMA applications)
+ f->max_pointer_idx = 2*depth - 1;
+ f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
- tu_fifo_unlock(f);
+ f->rd_idx = f->wr_idx = 0;
+
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
return true;
}
-// retrieve data from fifo
-static void _tu_ff_pull(tu_fifo_t* f, void * buffer)
+// Static functions are intended to work on local variables
+static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
+{
+ while ( idx >= depth) idx -= depth;
+ return idx;
+}
+
+// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
+// Code adapted from dcd_synopsis.c
+// TODO generalize with configurable 1 byte or 4 byte each read
+static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
{
- memcpy(buffer,
- f->buffer + (f->rd_idx * f->item_size),
- f->item_size);
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
- f->rd_idx = (f->rd_idx + 1) % f->depth;
- f->count--;
+ // Reading full available 32 bit words from const app address
+ uint16_t full_words = len >> 2;
+ while(full_words--)
+ {
+ tu_unaligned_write32(ff_buf, *rx_fifo);
+ ff_buf += 4;
+ }
+
+ // Read the remaining 1-3 bytes from const app address
+ uint8_t const bytes_rem = len & 0x03;
+ if ( bytes_rem )
+ {
+ uint32_t tmp32 = *rx_fifo;
+ memcpy(ff_buf, &tmp32, bytes_rem);
+ }
}
-// send data to fifo
-static void _tu_ff_push(tu_fifo_t* f, void const * data)
+// Intended to be used to write to hardware USB FIFO in e.g. STM32
+// where all data is written to a constant address in full word copies
+static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
{
- memcpy( f->buffer + (f->wr_idx * f->item_size),
- data,
- f->item_size);
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
- f->wr_idx = (f->wr_idx + 1) % f->depth;
+ // Pushing full available 32 bit words to const app address
+ uint16_t full_words = len >> 2;
+ while(full_words--)
+ {
+ *tx_fifo = tu_unaligned_read32(ff_buf);
+ ff_buf += 4;
+ }
- if (tu_fifo_full(f))
+ // Write the remaining 1-3 bytes into const app address
+ uint8_t const bytes_rem = len & 0x03;
+ if ( bytes_rem )
{
- f->rd_idx = f->wr_idx; // keep the full state (rd == wr && len = size)
+ uint32_t tmp32 = 0;
+ memcpy(&tmp32, ff_buf, bytes_rem);
+
+ *tx_fifo = tmp32;
+ }
+}
+
+// send one item to FIFO WITHOUT updating write pointer
+static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
+{
+ memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
+}
+
+// send n items to FIFO WITHOUT updating write pointer
+static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
+{
+ uint16_t const nLin = f->depth - rel;
+ uint16_t const nWrap = n - nLin;
+
+ uint16_t nLin_bytes = nLin * f->item_size;
+ uint16_t nWrap_bytes = nWrap * f->item_size;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+
+ switch (copy_mode)
+ {
+ case TU_FIFO_COPY_INC:
+ if(n <= nLin)
+ {
+ // Linear only
+ memcpy(ff_buf, app_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around
+
+ // Write data to linear part of buffer
+ memcpy(ff_buf, app_buf, nLin_bytes);
+
+ // Write data wrapped around
+ memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
+ }
+ break;
+
+ case TU_FIFO_COPY_CST_FULL_WORDS:
+ // Intended for hardware buffers from which it can be read word by word only
+ if(n <= nLin)
+ {
+ // Linear only
+ _ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around case
+
+ // Write full words to linear part of buffer
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
+ _ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
+ ff_buf += nLin_4n_bytes;
+
+ // There could be odd 1-3 bytes before the wrap-around boundary
+ volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
+ uint8_t rem = nLin_bytes & 0x03;
+ if (rem > 0)
+ {
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
+ nWrap_bytes -= remrem;
+
+ uint32_t tmp32 = *rx_fifo;
+ uint8_t * src_u8 = ((uint8_t *) &tmp32);
+
+ // Write 1-3 bytes before wrapped boundary
+ while(rem--) *ff_buf++ = *src_u8++;
+
+ // Read more bytes to beginning to complete a word
+ ff_buf = f->buffer;
+ while(remrem--) *ff_buf++ = *src_u8++;
+ }
+ else
+ {
+ ff_buf = f->buffer; // wrap around to beginning
+ }
+
+ // Write data wrapped part
+ if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
+ }
+ break;
+ }
+}
+
+// get one item from FIFO WITHOUT updating read pointer
+static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
+{
+ memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
+}
+
+// get n items from FIFO WITHOUT updating read pointer
+static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
+{
+ uint16_t const nLin = f->depth - rel;
+ uint16_t const nWrap = n - nLin; // only used if wrapped
+
+ uint16_t nLin_bytes = nLin * f->item_size;
+ uint16_t nWrap_bytes = nWrap * f->item_size;
+
+ // current buffer of fifo
+ uint8_t* ff_buf = f->buffer + (rel * f->item_size);
+
+ switch (copy_mode)
+ {
+ case TU_FIFO_COPY_INC:
+ if ( n <= nLin )
+ {
+ // Linear only
+ memcpy(app_buf, ff_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around
+
+ // Read data from linear part of buffer
+ memcpy(app_buf, ff_buf, nLin_bytes);
+
+ // Read data wrapped part
+ memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
+ }
+ break;
+
+ case TU_FIFO_COPY_CST_FULL_WORDS:
+ if ( n <= nLin )
+ {
+ // Linear only
+ _ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
+ }
+ else
+ {
+ // Wrap around case
+
+ // Read full words from linear part of buffer
+ uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
+ _ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
+ ff_buf += nLin_4n_bytes;
+
+ // There could be odd 1-3 bytes before the wrap-around boundary
+ volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
+ uint8_t rem = nLin_bytes & 0x03;
+ if (rem > 0)
+ {
+ uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
+ nWrap_bytes -= remrem;
+
+ uint32_t tmp32=0;
+ uint8_t * dst_u8 = (uint8_t *)&tmp32;
+
+ // Read 1-3 bytes before wrapped boundary
+ while(rem--) *dst_u8++ = *ff_buf++;
+
+ // Read more bytes from beginning to complete a word
+ ff_buf = f->buffer;
+ while(remrem--) *dst_u8++ = *ff_buf++;
+
+ *tx_fifo = tmp32;
+ }
+ else
+ {
+ ff_buf = f->buffer; // wrap around to beginning
+ }
+
+ // Read data wrapped part
+ if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
+ }
+ break;
+
+ default: break;
+ }
+}
+
+// Advance an absolute pointer
+static uint16_t advance_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!!
+ // We are exploiting the wrap around to the correct index
+ if ((p > (uint16_t)(p + offset)) || ((uint16_t)(p + offset) > f->max_pointer_idx))
+ {
+ p = (p + offset) + f->non_used_index_space;
}
else
{
- f->count++;
+ p += offset;
}
+ return p;
+}
+
+// Backward an absolute pointer
+static uint16_t backward_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
+{
+ // We limit the index space of p such that a correct wrap around happens
+ // Check for a wrap around or if we are in unused index space - This has to be checked first!!
+ // We are exploiting the wrap around to the correct index
+ if ((p < (uint16_t)(p - offset)) || ((uint16_t)(p - offset) > f->max_pointer_idx))
+ {
+ p = (p - offset) - f->non_used_index_space;
+ }
+ else
+ {
+ p -= offset;
+ }
+ return p;
+}
+
+// get relative from absolute pointer
+static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p)
+{
+ return _ff_mod(p, f->depth);
+}
+
+// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
+static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = wAbs-rAbs;
+
+ // In case we have non-power of two depth we need a further modification
+ if (rAbs > wAbs) cnt -= f->non_used_index_space;
+
+ return cnt;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_empty(uint16_t wAbs, uint16_t rAbs)
+{
+ return wAbs == rAbs;
+}
+
+// Works on local copies of w and r
+static inline bool _tu_fifo_full(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) == f->depth);
+}
+
+// Works on local copies of w and r
+// BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
+// Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
+// write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
+// write pointer wraps and you pointer states are messed up. This can only happen if you
+// use DMAs, write functions do not allow such an error.
+static inline bool _tu_fifo_overflowed(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return (_tu_fifo_count(f, wAbs, rAbs) > f->depth);
+}
+
+// Works on local copies of w
+// For more details see _tu_fifo_overflow()!
+static inline void _tu_fifo_correct_read_pointer(tu_fifo_t* f, uint16_t wAbs)
+{
+ f->rd_idx = backward_pointer(f, wAbs, f->depth);
+}
+
+// Works on local copies of w and r
+// Must be protected by mutexes since in case of an overflow read pointer gets modified
+static bool _tu_fifo_peek(tu_fifo_t* f, void * p_buffer, uint16_t wAbs, uint16_t rAbs)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0) return false;
+
+ uint16_t rRel = get_relative_pointer(f, rAbs);
+
+ // Peek data
+ _ff_pull(f, p_buffer, rRel);
+
+ return true;
+}
+
+// Works on local copies of w and r
+// Must be protected by mutexes since in case of an overflow read pointer gets modified
+static uint16_t _tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
+{
+ uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
+
+ // Check overflow and correct if required
+ if (cnt > f->depth)
+ {
+ _tu_fifo_correct_read_pointer(f, wAbs);
+ rAbs = f->rd_idx;
+ cnt = f->depth;
+ }
+
+ // Skip beginning of buffer
+ if (cnt == 0) return 0;
+
+ // Check if we can read something at and after offset - if too less is available we read what remains
+ if (cnt < n) n = cnt;
+
+ uint16_t rRel = get_relative_pointer(f, rAbs);
+
+ // Peek data
+ _ff_pull_n(f, p_buffer, n, rRel, copy_mode);
+
+ return n;
+}
+
+// Works on local copies of w and r
+static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
+{
+ return f->depth - _tu_fifo_count(f, wAbs, rAbs);
+}
+
+static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
+{
+ if ( n == 0 ) return 0;
+
+ _ff_lock(f->mutex_wr);
+
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint8_t const* buf8 = (uint8_t const*) data;
+
+ if (!f->overwritable)
+ {
+ // Not overwritable limit up to full
+ n = tu_min16(n, _tu_fifo_remaining(f, w, r));
+ }
+ else if (n >= f->depth)
+ {
+ // Only copy last part
+ buf8 = buf8 + (n - f->depth) * f->item_size;
+ n = f->depth;
+
+ // We start writing at the read pointer's position since we fill the complete
+ // buffer and we do not want to modify the read pointer within a write function!
+ // This would end up in a race condition with read functions!
+ w = r;
+ }
+
+ uint16_t wRel = get_relative_pointer(f, w);
+
+ // Write data
+ _ff_push_n(f, buf8, n, wRel, copy_mode);
+
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, n);
+
+ _ff_unlock(f->mutex_wr);
+
+ return n;
+}
+
+static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
+{
+ _ff_lock(f->mutex_rd);
+
+ // Peek the data
+ // f->rd_idx might get modified in case of an overflow so we can not use a local variable
+ n = _tu_fifo_peek_n(f, buffer, n, f->wr_idx, f->rd_idx, copy_mode);
+
+ // Advance read pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
+
+ _ff_unlock(f->mutex_rd);
+ return n;
}
/******************************************************************************/
/*!
- @brief Read one byte out of the RX buffer.
+ @brief Get number of items in FIFO.
- This function will return the byte located at the array index of the
- read pointer, and then increment the read pointer index. If the read
- pointer exceeds the maximum buffer size, it will roll over to zero.
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes. In case an
+ overflow occurred, this function return f.depth at maximum. Overflows are
+ checked and corrected for in the read functions!
@param[in] f
Pointer to the FIFO buffer to manipulate
- @param[in] buffer
- Pointer to the place holder for data read from the buffer
- @returns TRUE if the queue is not empty
-*/
+ @returns Number of items in FIFO
+ */
/******************************************************************************/
-bool tu_fifo_read(tu_fifo_t* f, void * buffer)
+uint16_t tu_fifo_count(tu_fifo_t* f)
{
- if( tu_fifo_empty(f) ) return false;
+ return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
+}
- tu_fifo_lock(f);
+/******************************************************************************/
+/*!
+ @brief Check if FIFO is empty.
- _tu_ff_pull(f, buffer);
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
- tu_fifo_unlock(f);
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
- return true;
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_empty(tu_fifo_t* f)
+{
+ return _tu_fifo_empty(f->wr_idx, f->rd_idx);
}
/******************************************************************************/
/*!
- @brief This function will read n elements into the array index specified by
- the write pointer and increment the write index. If the write index
- exceeds the max buffer size, then it will roll over to zero.
+ @brief Check if FIFO is full.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+bool tu_fifo_full(tu_fifo_t* f)
+{
+ return _tu_fifo_full(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Get remaining space in FIFO.
+
+ As this function only reads the read and write pointers once, this function is
+ reentrant and thus thread and ISR save without any mutexes.
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns Number of items in FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_remaining(tu_fifo_t* f)
+{
+ return _tu_fifo_remaining(f, f->wr_idx, f->rd_idx);
+}
+
+/******************************************************************************/
+/*!
+ @brief Check if overflow happened.
+
+ BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
+ Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
+ write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
+ write pointer wraps and your pointer states are messed up. This can only happen if you
+ use DMAs, write functions do not allow such an error. Avoid such nasty things!
+
+ All reading functions (read, peek) check for overflows and correct read pointer on their own such
+ that latest items are read.
+ If required (e.g. for DMA use) you can also correct the read pointer by
+ tu_fifo_correct_read_pointer().
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+
+ @returns True if overflow happened
+ */
+/******************************************************************************/
+bool tu_fifo_overflowed(tu_fifo_t* f)
+{
+ return _tu_fifo_overflowed(f, f->wr_idx, f->rd_idx);
+}
+
+// Only use in case tu_fifo_overflow() returned true!
+void tu_fifo_correct_read_pointer(tu_fifo_t* f)
+{
+ _ff_lock(f->mutex_rd);
+ _tu_fifo_correct_read_pointer(f, f->wr_idx);
+ _ff_unlock(f->mutex_rd);
+}
+
+/******************************************************************************/
+/*!
+ @brief Read one element out of the buffer.
+
+ This function will return the element located at the array index of the
+ read pointer, and then increment the read pointer index.
+ This function checks for an overflow and corrects read pointer if required.
@param[in] f
Pointer to the FIFO buffer to manipulate
@param[in] buffer
- The pointer to data location
- @param[in] count
- Number of element that buffer can afford
+ Pointer to the place holder for data read from the buffer
- @returns number of items read from the FIFO
-*/
+ @returns TRUE if the queue is not empty
+ */
/******************************************************************************/
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * buffer, uint16_t count)
+bool tu_fifo_read(tu_fifo_t* f, void * buffer)
{
- if( tu_fifo_empty(f) ) return 0;
+ _ff_lock(f->mutex_rd);
- tu_fifo_lock(f);
+ // Peek the data
+ // f->rd_idx might get modified in case of an overflow so we can not use a local variable
+ bool ret = _tu_fifo_peek(f, buffer, f->wr_idx, f->rd_idx);
- /* Limit up to fifo's count */
- if ( count > f->count ) count = f->count;
+ // Advance pointer
+ f->rd_idx = advance_pointer(f, f->rd_idx, ret);
- uint8_t* buf8 = (uint8_t*) buffer;
- uint16_t len = 0;
+ _ff_unlock(f->mutex_rd);
+ return ret;
+}
- while (len < count)
- {
- _tu_ff_pull(f, buf8);
+/******************************************************************************/
+/*!
+ @brief This function will read n elements from the array index specified by
+ the read pointer and increment the read index.
+ This function checks for an overflow and corrects read pointer if required.
- len++;
- buf8 += f->item_size;
- }
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] buffer
+ The pointer to data location
+ @param[in] n
+ Number of element that buffer can afford
- tu_fifo_unlock(f);
+ @returns number of items read from the FIFO
+ */
+/******************************************************************************/
+uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
+{
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
+}
- return len;
+uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
+{
+ return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
/******************************************************************************/
/*!
- @brief Reads one item without removing it from the FIFO
+ @brief Read one item without removing it from the FIFO.
+ This function checks for an overflow and corrects read pointer if required.
@param[in] f
Pointer to the FIFO buffer to manipulate
- @param[in] pos
- Position to read from in the FIFO buffer
+ @param[in] offset
+ Position to read from in the FIFO buffer with respect to read pointer
@param[in] p_buffer
Pointer to the place holder for data read from the buffer
@returns TRUE if the queue is not empty
-*/
+ */
/******************************************************************************/
-bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
+bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
- if ( pos >= f->count ) return false;
+ _ff_lock(f->mutex_rd);
+ bool ret = _tu_fifo_peek(f, p_buffer, f->wr_idx, f->rd_idx);
+ _ff_unlock(f->mutex_rd);
+ return ret;
+}
- // rd_idx is pos=0
- uint16_t index = (f->rd_idx + pos) % f->depth;
- memcpy(p_buffer,
- f->buffer + (index * f->item_size),
- f->item_size);
+/******************************************************************************/
+/*!
+ @brief Read n items without removing it from the FIFO
+ This function checks for an overflow and corrects read pointer if required.
- return true;
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] p_buffer
+ Pointer to the place holder for data read from the buffer
+ @param[in] n
+ Number of items to peek
+
+ @returns Number of bytes written to p_buffer
+ */
+/******************************************************************************/
+uint16_t tu_fifo_peek_n(tu_fifo_t* f, void * p_buffer, uint16_t n)
+{
+ _ff_lock(f->mutex_rd);
+ bool ret = _tu_fifo_peek_n(f, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
+ _ff_unlock(f->mutex_rd);
+ return ret;
}
/******************************************************************************/
/*!
- @brief Write one element into the RX buffer.
+ @brief Write one element into the buffer.
This function will write one element into the array index specified by
- the write pointer and increment the write index. If the write index
- exceeds the max buffer size, then it will roll over to zero.
+ the write pointer and increment the write index.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -213,17 +735,25 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t pos, void * p_buffer)
@returns TRUE if the data was written to the FIFO (overwrittable
FIFO will always return TRUE)
-*/
+ */
/******************************************************************************/
-bool tu_fifo_write (tu_fifo_t* f, const void * data)
+bool tu_fifo_write(tu_fifo_t* f, const void * data)
{
- if ( tu_fifo_full(f) && !f->overwritable ) return false;
+ _ff_lock(f->mutex_wr);
+
+ uint16_t w = f->wr_idx;
+
+ if ( _tu_fifo_full(f, w, f->rd_idx) && !f->overwritable ) return false;
- tu_fifo_lock(f);
+ uint16_t wRel = get_relative_pointer(f, w);
- _tu_ff_push(f, data);
+ // Write data
+ _ff_push(f, data, wRel);
- tu_fifo_unlock(f);
+ // Advance pointer
+ f->wr_idx = advance_pointer(f, w, 1);
+
+ _ff_unlock(f->mutex_wr);
return true;
}
@@ -231,8 +761,7 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
/******************************************************************************/
/*!
@brief This function will write n elements into the array index specified by
- the write pointer and increment the write index. If the write index
- exceeds the max buffer size, then it will roll over to zero.
+ the write pointer and increment the write index.
@param[in] f
Pointer to the FIFO buffer to manipulate
@@ -241,48 +770,231 @@ bool tu_fifo_write (tu_fifo_t* f, const void * data)
@param[in] count
Number of element
@return Number of written elements
-*/
+ */
/******************************************************************************/
-uint16_t tu_fifo_write_n (tu_fifo_t* f, const void * data, uint16_t count)
+uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
{
- if ( count == 0 ) return 0;
-
- tu_fifo_lock(f);
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
+}
- // Not overwritable limit up to full
- if (!f->overwritable) count = tu_min16(count, tu_fifo_remaining(f));
+/******************************************************************************/
+/*!
+ @brief This function will write n elements into the array index specified by
+ the write pointer and increment the write index. The source address will
+ not be incremented which is useful for reading from registers.
- uint8_t const* buf8 = (uint8_t const*) data;
- uint16_t len = 0;
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] data
+ The pointer to data to add to the FIFO
+ @param[in] count
+ Number of element
+ @return Number of written elements
+ */
+/******************************************************************************/
+uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
+{
+ return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
+}
- while (len < count)
- {
- _tu_ff_push(f, buf8);
+/******************************************************************************/
+/*!
+ @brief Clear the fifo read and write pointers
- len++;
- buf8 += f->item_size;
- }
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ */
+/******************************************************************************/
+bool tu_fifo_clear(tu_fifo_t *f)
+{
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
- tu_fifo_unlock(f);
+ f->rd_idx = f->wr_idx = 0;
+ f->max_pointer_idx = 2*f->depth-1;
+ f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
- return len;
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
+ return true;
}
/******************************************************************************/
/*!
- @brief Clear the fifo read and write pointers and set length to zero
+ @brief Change the fifo mode to overwritable or not overwritable
@param[in] f
Pointer to the FIFO buffer to manipulate
-*/
+ @param[in] overwritable
+ Overwritable mode the fifo is set to
+ */
/******************************************************************************/
-bool tu_fifo_clear(tu_fifo_t *f)
+bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
{
- tu_fifo_lock(f);
+ _ff_lock(f->mutex_wr);
+ _ff_lock(f->mutex_rd);
- f->rd_idx = f->wr_idx = f->count = 0;
+ f->overwritable = overwritable;
- tu_fifo_unlock(f);
+ _ff_unlock(f->mutex_wr);
+ _ff_unlock(f->mutex_rd);
return true;
}
+
+/******************************************************************************/
+/*!
+ @brief Advance write pointer - intended to be used in combination with DMA.
+ It is possible to fill the FIFO by use of a DMA in circular mode. Within
+ DMA ISRs you may update the write pointer to be able to read from the FIFO.
+ As long as the DMA is the only process writing into the FIFO this is safe
+ to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the write pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->wr_idx = advance_pointer(f, f->wr_idx, n);
+}
+
+/******************************************************************************/
+/*!
+ @brief Advance read pointer - intended to be used in combination with DMA.
+ It is possible to read from the FIFO by use of a DMA in linear mode. Within
+ DMA ISRs you may update the read pointer to be able to again write into the
+ FIFO. As long as the DMA is the only process reading from the FIFO this is
+ safe to use.
+
+ USE WITH CARE - WE DO NOT CONDUCT SAFTY CHECKS HERE!
+
+ @param[in] f
+ Pointer to the FIFO buffer to manipulate
+ @param[in] n
+ Number of items the read pointer moves forward
+ */
+/******************************************************************************/
+void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
+{
+ f->rd_idx = advance_pointer(f, f->rd_idx, n);
+}
+
+/******************************************************************************/
+/*!
+ @brief Get read info
+
+ Returns the length and pointer from which bytes can be read in a linear manner.
+ This is of major interest for DMA transmissions. If returned length is zero the
+ corresponding pointer is invalid.
+ The read pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
+ do so!
+ @param[in] f
+ Pointer to FIFO
+ @param[out] *info
+ Pointer to struct which holds the desired infos
+ */
+/******************************************************************************/
+void tu_fifo_get_read_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
+{
+ // Operate on temporary values in case they change in between
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+
+ uint16_t cnt = _tu_fifo_count(f, w, r);
+
+ // Check overflow and correct if required - may happen in case a DMA wrote too fast
+ if (cnt > f->depth)
+ {
+ _ff_lock(f->mutex_rd);
+ _tu_fifo_correct_read_pointer(f, w);
+ _ff_unlock(f->mutex_rd);
+ r = f->rd_idx;
+ cnt = f->depth;
+ }
+
+ // Check if fifo is empty
+ if (cnt == 0)
+ {
+ info->len_lin = 0;
+ info->len_wrap = 0;
+ info->ptr_lin = NULL;
+ info->ptr_wrap = NULL;
+ return;
+ }
+
+ // Get relative pointers
+ w = get_relative_pointer(f, w);
+ r = get_relative_pointer(f, r);
+
+ // Copy pointer to buffer to start reading from
+ info->ptr_lin = &f->buffer[r];
+
+ // Check if there is a wrap around necessary
+ if (w > r) {
+ // Non wrapping case
+ info->len_lin = cnt;
+ info->len_wrap = 0;
+ info->ptr_wrap = NULL;
+ }
+ else
+ {
+ info->len_lin = f->depth - r; // Also the case if FIFO was full
+ info->len_wrap = cnt - info->len_lin;
+ info->ptr_wrap = f->buffer;
+ }
+}
+
+/******************************************************************************/
+/*!
+ @brief Get linear write info
+
+ Returns the length and pointer to which bytes can be written into FIFO in a linear manner.
+ This is of major interest for DMA transmissions not using circular mode. If a returned length is zero the
+ corresponding pointer is invalid. The returned lengths summed up are the currently free space in the FIFO.
+ The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so!
+ TAKE CARE TO NOT OVERFLOW THE BUFFER MORE THAN TWO TIMES THE FIFO DEPTH - IT CAN NOT RECOVERE OTHERWISE!
+ @param[in] f
+ Pointer to FIFO
+ @param[out] *info
+ Pointer to struct which holds the desired infos
+ */
+/******************************************************************************/
+void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info)
+{
+ uint16_t w = f->wr_idx, r = f->rd_idx;
+ uint16_t free = _tu_fifo_remaining(f, w, r);
+
+ if (free == 0)
+ {
+ info->len_lin = 0;
+ info->len_wrap = 0;
+ info->ptr_lin = NULL;
+ info->ptr_wrap = NULL;
+ return;
+ }
+
+ // Get relative pointers
+ w = get_relative_pointer(f, w);
+ r = get_relative_pointer(f, r);
+
+ // Copy pointer to buffer to start writing to
+ info->ptr_lin = &f->buffer[w];
+
+ if (w < r)
+ {
+ // Non wrapping case
+ info->len_lin = r-w;
+ info->len_wrap = 0;
+ info->ptr_wrap = NULL;
+ }
+ else
+ {
+ info->len_lin = f->depth - w;
+ info->len_wrap = free - info->len_lin; // Remaining length - n already was limited to free or FIFO depth
+ info->ptr_wrap = f->buffer; // Always start of buffer
+ }
+}
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index fb0c896f3..18db289a1 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,108 +25,127 @@
* This file is part of the TinyUSB stack.
*/
-/** \ingroup Group_Common
- * \defgroup group_fifo fifo
- * @{ */
-
#ifndef _TUSB_FIFO_H_
#define _TUSB_FIFO_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Due to the use of unmasked pointers, this FIFO does not suffer from loosing
+// one item slice. Furthermore, write and read operations are completely
+// decoupled as write and read functions do not modify a common state. Henceforth,
+// writing or reading from the FIFO within an ISR is safe as long as no other
+// process (thread or ISR) interferes.
+// Also, this FIFO is ready to be used in combination with a DMA as the write and
+// read pointers can be updated from within a DMA ISR. Overflows are detectable
+// within a certain number (see tu_fifo_overflow()).
+
+#include "common/tusb_common.h"
+
// mutex is only needed for RTOS
// for OS None, we don't get preempted
#define CFG_FIFO_MUTEX (CFG_TUSB_OS != OPT_OS_NONE)
-#include <stdint.h>
-#include <stdbool.h>
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
#if CFG_FIFO_MUTEX
+#include "osal/osal.h"
#define tu_fifo_mutex_t osal_mutex_t
#endif
-
-/** \struct tu_fifo_t
- * \brief Simple Circular FIFO
- */
typedef struct
{
- uint8_t* buffer ; ///< buffer pointer
- uint16_t depth ; ///< max items
- uint16_t item_size ; ///< size of each item
- bool overwritable ;
+ uint8_t* buffer ; ///< buffer pointer
+ uint16_t depth ; ///< max items
+ uint16_t item_size ; ///< size of each item
+ bool overwritable ;
+
+ uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
+ uint16_t max_pointer_idx ; ///< maximum absolute pointer index
- volatile uint16_t count ; ///< number of items in queue
- volatile uint16_t wr_idx ; ///< write pointer
- volatile uint16_t rd_idx ; ///< read pointer
+ volatile uint16_t wr_idx ; ///< write pointer
+ volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
- tu_fifo_mutex_t mutex;
+ tu_fifo_mutex_t mutex_wr;
+ tu_fifo_mutex_t mutex_rd;
#endif
} tu_fifo_t;
-#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
- uint8_t _name##_buf[_depth*sizeof(_type)]; \
- tu_fifo_t _name = { \
- .buffer = _name##_buf, \
- .depth = _depth, \
- .item_size = sizeof(_type), \
- .overwritable = _overwritable, \
- }
+typedef struct
+{
+ uint16_t len_lin ; ///< linear length in item size
+ uint16_t len_wrap ; ///< wrapped length in item size
+ void * ptr_lin ; ///< linear part start pointer
+ void * ptr_wrap ; ///< wrapped part start pointer
+} tu_fifo_buffer_info_t;
+
+#define TU_FIFO_INIT(_buffer, _depth, _type, _overwritable) \
+{ \
+ .buffer = _buffer, \
+ .depth = _depth, \
+ .item_size = sizeof(_type), \
+ .overwritable = _overwritable, \
+ .non_used_index_space = UINT16_MAX - (2*(_depth)-1), \
+ .max_pointer_idx = 2*(_depth)-1, \
+}
+#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
+ uint8_t _name##_buf[_depth*sizeof(_type)]; \
+ tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
+
+
+bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
#if CFG_FIFO_MUTEX
-static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl)
{
- f->mutex = mutex_hdl;
+ f->mutex_wr = write_mutex_hdl;
+ f->mutex_rd = read_mutex_hdl;
}
#endif
-bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
-uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
+bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
+uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
+uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
-bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
-uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count);
+bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
+uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
+uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
-bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
-
-static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
-{
- return tu_fifo_peek_at(f, 0, p_buffer);
-}
+bool tu_fifo_peek (tu_fifo_t* f, void * p_buffer);
+uint16_t tu_fifo_peek_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
-static inline bool tu_fifo_empty(tu_fifo_t* f)
-{
- return (f->count == 0);
-}
+uint16_t tu_fifo_count (tu_fifo_t* f);
+uint16_t tu_fifo_remaining (tu_fifo_t* f);
+bool tu_fifo_empty (tu_fifo_t* f);
+bool tu_fifo_full (tu_fifo_t* f);
+bool tu_fifo_overflowed (tu_fifo_t* f);
+void tu_fifo_correct_read_pointer (tu_fifo_t* f);
-static inline bool tu_fifo_full(tu_fifo_t* f)
+TU_ATTR_ALWAYS_INLINE static inline
+uint16_t tu_fifo_depth(tu_fifo_t* f)
{
- return (f->count == f->depth);
+ return f->depth;
}
-static inline uint16_t tu_fifo_count(tu_fifo_t* f)
-{
- return f->count;
-}
+// Pointer modifications intended to be used in combinations with DMAs.
+// USE WITH CARE - NO SAFTY CHECKS CONDUCTED HERE! NOT MUTEX PROTECTED!
+void tu_fifo_advance_write_pointer(tu_fifo_t *f, uint16_t n);
+void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
-static inline uint16_t tu_fifo_remaining(tu_fifo_t* f)
-{
- return f->depth - f->count;
-}
+// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies
+// to handle a possible wrapping part. These functions deliver a pointer to start
+// reading/writing from/to and a valid linear length along which no wrap occurs.
+void tu_fifo_get_read_info (tu_fifo_t *f, tu_fifo_buffer_info_t *info);
+void tu_fifo_get_write_info(tu_fifo_t *f, tu_fifo_buffer_info_t *info);
-static inline uint16_t tu_fifo_depth(tu_fifo_t* f)
-{
- return f->depth;
-}
#ifdef __cplusplus
- }
+}
#endif
#endif /* _TUSB_FIFO_H_ */
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index c37c19bad..d23c6b2dd 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -1,4 +1,4 @@
-/*
+/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
@@ -48,7 +48,8 @@ typedef enum
{
TUSB_SPEED_FULL = 0,
TUSB_SPEED_LOW ,
- TUSB_SPEED_HIGH
+ TUSB_SPEED_HIGH,
+ TUSB_SPEED_INVALID = 0xff,
}tusb_speed_t;
/// defined base on USB Specs Endpoint's bmAttributes
@@ -139,6 +140,7 @@ typedef enum
TUSB_REQ_RCPT_OTHER
} tusb_request_recipient_t;
+// https://www.usb.org/defined-class-codes
typedef enum
{
TUSB_CLASS_UNSPECIFIED = 0 ,
@@ -178,6 +180,12 @@ typedef enum
typedef enum
{
+ APP_SUBCLASS_USBTMC = 0x03,
+ APP_SUBCLASS_DFU_RUNTIME = 0x01
+} app_subclass_type_t;
+
+typedef enum
+{
DEVICE_CAPABILITY_WIRELESS_USB = 0x01,
DEVICE_CAPABILITY_USB20_EXTENSION = 0x02,
DEVICE_CAPABILITY_SUPERSPEED_USB = 0x03,
@@ -243,10 +251,21 @@ typedef enum
MS_OS_20_FEATURE_VENDOR_REVISION = 0x08
} microsoft_os_20_type_t;
+enum
+{
+ CONTROL_STAGE_SETUP,
+ CONTROL_STAGE_DATA,
+ CONTROL_STAGE_ACK
+};
+
//--------------------------------------------------------------------+
// USB Descriptors
//--------------------------------------------------------------------+
+// Start of all packed definitions for compiler without per-type packed
+TU_ATTR_PACKED_BEGIN
+TU_ATTR_BIT_FIELD_ORDER_BEGIN
+
/// USB Device Descriptor
typedef struct TU_ATTR_PACKED
{
@@ -269,6 +288,8 @@ typedef struct TU_ATTR_PACKED
uint8_t bNumConfigurations ; ///< Number of possible configurations.
} tusb_desc_device_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_device_t) == 18, "size is not correct");
+
// USB Binary Device Object Store (BOS) Descriptor
typedef struct TU_ATTR_PACKED
{
@@ -278,6 +299,8 @@ typedef struct TU_ATTR_PACKED
uint8_t bNumDeviceCaps ; ///< Number of device capability descriptors in the BOS
} tusb_desc_bos_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_bos_t) == 5, "size is not correct");
+
/// USB Configuration Descriptor
typedef struct TU_ATTR_PACKED
{
@@ -292,6 +315,8 @@ typedef struct TU_ATTR_PACKED
uint8_t bMaxPower ; ///< Maximum power consumption of the USB device from the bus in this specific configuration when the device is fully operational. Expressed in 2 mA units (i.e., 50 = 100 mA).
} tusb_desc_configuration_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_configuration_t) == 9, "size is not correct");
+
/// USB Interface Descriptor
typedef struct TU_ATTR_PACKED
{
@@ -307,6 +332,8 @@ typedef struct TU_ATTR_PACKED
uint8_t iInterface ; ///< Index of string descriptor describing this interface
} tusb_desc_interface_t;
+TU_VERIFY_STATIC( sizeof(tusb_desc_interface_t) == 9, "size is not correct");
+
/// USB Endpoint Descriptor
typedef struct TU_ATTR_PACKED
{
@@ -323,9 +350,14 @@ typedef struct TU_ATTR_PACKED
} bmAttributes ; ///< This field describes the endpoint's attributes when it is configured using the bConfigurationValue. \n Bits 1..0: Transfer Type \n- 00 = Control \n- 01 = Isochronous \n- 10 = Bulk \n- 11 = Interrupt \n If not an isochronous endpoint, bits 5..2 are reserved and must be set to zero. If isochronous, they are defined as follows: \n Bits 3..2: Synchronization Type \n- 00 = No Synchronization \n- 01 = Asynchronous \n- 10 = Adaptive \n- 11 = Synchronous \n Bits 5..4: Usage Type \n- 00 = Data endpoint \n- 01 = Feedback endpoint \n- 10 = Implicit feedback Data endpoint \n- 11 = Reserved \n Refer to Chapter 5 of USB 2.0 specification for more information. \n All other bits are reserved and must be reset to zero. Reserved bits must be ignored by the host.
struct TU_ATTR_PACKED {
+#if defined(__CCRX__)
+ //FIXME the original defined bit field has a problem with the CCRX toolchain, so only a size field is defined
+ uint16_t size;
+#else
uint16_t size : 11; ///< Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. \n For isochronous endpoints, this value is used to reserve the bus time in the schedule, required for the per-(micro)frame data payloads. The pipe may, on an ongoing basis, actually use less bandwidth than that reserved. The device reports, if necessary, the actual bandwidth used via its normal, non-USB defined mechanisms. \n For all endpoints, bits 10..0 specify the maximum packet size (in bytes). \n For high-speed isochronous and interrupt endpoints: \n Bits 12..11 specify the number of additional transaction opportunities per microframe: \n- 00 = None (1 transaction per microframe) \n- 01 = 1 additional (2 per microframe) \n- 10 = 2 additional (3 per microframe) \n- 11 = Reserved \n Bits 15..13 are reserved and must be set to zero.
uint16_t hs_period_mult : 2;
- uint16_t : 0;
+ uint16_t TU_RESERVED : 3;
+#endif
}wMaxPacketSize;
uint8_t bInterval ; ///< Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units). \n- For full-/high-speed isochronous endpoints, this value must be in the range from 1 to 16. The bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$). \n- For full-/low-speed interrupt endpoints, the value of this field may be from 1 to 255. \n- For high-speed interrupt endpoints, the bInterval value is used as the exponent for a \f$ 2^(bInterval-1) \f$ value; e.g., a bInterval of 4 means a period of 8 (\f$ 2^(4-1) \f$) . This value must be from 1 to 16. \n- For high-speed bulk/control OUT endpoints, the bInterval must specify the maximum NAK rate of the endpoint. A value of 0 indicates the endpoint never NAKs. Other values indicate at most 1 NAK each bInterval number of microframes. This value must be in the range from 0 to 255. \n Refer to Chapter 5 of USB 2.0 specification for more information.
@@ -404,6 +436,29 @@ typedef struct TU_ATTR_PACKED
char url[];
} tusb_desc_webusb_url_t;
+// DFU Functional Descriptor
+typedef struct TU_ATTR_PACKED
+{
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+
+ union {
+ struct TU_ATTR_PACKED {
+ uint8_t bitCanDnload : 1;
+ uint8_t bitCanUpload : 1;
+ uint8_t bitManifestationTolerant : 1;
+ uint8_t bitWillDetach : 1;
+ uint8_t reserved : 4;
+ } bmAttributes;
+
+ uint8_t bAttributes;
+ };
+
+ uint16_t wDetachTimeOut;
+ uint16_t wTransferSize;
+ uint16_t bcdDFUVersion;
+} tusb_desc_dfu_functional_t;
+
/*------------------------------------------------------------------*/
/* Types
*------------------------------------------------------------------*/
@@ -424,13 +479,11 @@ typedef struct TU_ATTR_PACKED{
uint16_t wLength;
} tusb_control_request_t;
-TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "mostly compiler option issue");
+TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");
-// TODO move to somewhere suitable
-static inline uint8_t bm_request_type(uint8_t direction, uint8_t type, uint8_t recipient)
-{
- return ((uint8_t) (direction << 7)) | ((uint8_t) (type << 5)) | (recipient);
-}
+
+TU_ATTR_PACKED_END // End of all packed definitions
+TU_ATTR_BIT_FIELD_ORDER_END
//--------------------------------------------------------------------+
// Endpoint helper
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 1ad6d3fb0..56ba8bcd1 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -36,7 +36,6 @@
* as C++ for the sake of code simplicity. Beware of a headache macro
* manipulation that you are told to stay away.
*
- *
* This contains macros for both VERIFY and ASSERT:
*
* VERIFY: Used when there is an error condition which is not the
@@ -50,9 +49,8 @@
* quickly. One example would be adding assertions in library
* function calls to confirm a function's (untainted)
* parameters are valid.
- *
*
- * The difference in behaviour is that ASSERT triggers a breakpoint while
+ * The difference in behavior is that ASSERT triggers a breakpoint while
* verify does not.
*
* #define TU_VERIFY(cond) if(cond) return false;
@@ -76,11 +74,11 @@
#if CFG_TUSB_DEBUG
#include <stdio.h>
- #define _MESS_ERR(_err) printf("%s %d: failed, error = %s\n", __func__, __LINE__, tusb_strerr[_err])
- #define _MESS_FAILED() printf("%s %d: assert failed\n", __func__, __LINE__)
+ #define _MESS_ERR(_err) tu_printf("%s %d: failed, error = %s\r\n", __func__, __LINE__, tusb_strerr[_err])
+ #define _MESS_FAILED() tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
#else
- #define _MESS_ERR(_err)
- #define _MESS_FAILED()
+ #define _MESS_ERR(_err) do {} while (0)
+ #define _MESS_FAILED() do {} while (0)
#endif
// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
@@ -90,12 +88,12 @@
volatile uint32_t* ARM_CM_DHCSR = ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \
} while(0)
-#else
-#if defined(__riscv)
+
+#elif defined(__riscv)
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
+
#else
- #define TU_BREAKPOINT()
-#endif
+ #define TU_BREAKPOINT() do {} while (0)
#endif
/*------------------------------------------------------------------*/
@@ -142,7 +140,9 @@
#define ASSERT_1ARGS(_cond) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), false)
#define ASSERT_2ARGS(_cond, _ret) TU_VERIFY_DEFINE(_cond, _MESS_FAILED(); TU_BREAKPOINT(), _ret)
+#ifndef TU_ASSERT
#define TU_ASSERT(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_2ARGS, ASSERT_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
// TODO remove TU_ASSERT_ERR() later
@@ -163,10 +163,12 @@
/* ASSERT Error
* basically TU_VERIFY Error with TU_BREAKPOINT() as handler
*------------------------------------------------------------------*/
-#define ASERT_ERR_1ARGS(_error) TU_VERIFY_ERR_DEF2(_error, TU_BREAKPOINT())
-#define ASERT_ERR_2ARGS(_error, _ret) TU_VERIFY_ERR_DEF3(_error, TU_BREAKPOINT(), _ret)
+#define ASSERT_ERR_1ARGS(_error) TU_VERIFY_ERR_DEF2(_error, TU_BREAKPOINT())
+#define ASSERT_ERR_2ARGS(_error, _ret) TU_VERIFY_ERR_DEF3(_error, TU_BREAKPOINT(), _ret)
-#define TU_ASSERT_ERR(...) GET_3RD_ARG(__VA_ARGS__, ASERT_ERR_2ARGS, ASERT_ERR_1ARGS,UNUSED)(__VA_ARGS__)
+#ifndef TU_ASSERT_ERR
+#define TU_ASSERT_ERR(...) GET_3RD_ARG(__VA_ARGS__, ASSERT_ERR_2ARGS, ASSERT_ERR_1ARGS,UNUSED)(__VA_ARGS__)
+#endif
/*------------------------------------------------------------------*/
/* ASSERT HDLR