Loading...
Searching...
No Matches
tusb_os_custom.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2022 Gunar Schorcht
3 * SPDX-License-Identifier: LGPL-2.1-only
4 */
5
6#pragma once
7
17
18#include "mutex.h"
19#include "sema.h"
20#include "ztimer.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
26#if !DOXYGEN
27
28/* set to 1 to use cancelable mutex in osal_mutex and osal_queue */
29#ifndef TINYUSB_OSAL_MUTEX_CANCELABLE
30#define TINYUSB_OSAL_MUTEX_CANCELABLE 0
31#endif
32
36
37TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec)
38{
40}
41
47
48typedef sema_t osal_semaphore_def_t; /* semaphore */
49typedef sema_t *osal_semaphore_t; /* semaphore handle */
50
51TU_ATTR_ALWAYS_INLINE
52static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef)
53{
54 sema_create(semdef, 0);
55 return (osal_semaphore_t)semdef;
56}
57
58TU_ATTR_ALWAYS_INLINE
59static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr)
60{
61 (void)in_isr; /* hasn't to be considered since RIOT doesn't block in sema_post */
62 return sema_post(sem_hdl) == 0;
63}
64
65TU_ATTR_ALWAYS_INLINE
66static inline bool osal_semaphore_wait(osal_semaphore_t sem_hdl, uint32_t msec)
67{
68 assert(0);
69 return sema_wait_timed_ztimer(sem_hdl, ZTIMER_MSEC, msec) == 0;
70}
71
72TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t const sem_hdl)
73{
74 (void)sem_hdl;
75 /* TODO, function seems to be removed anyway */
76}
77
79typedef mutex_t osal_mutex_def_t; /* RIOT mutex */
80typedef mutex_t *osal_mutex_t; /* RIOT mutex handle */
81
82TU_ATTR_ALWAYS_INLINE
83static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t* mdef)
84{
85 assert(mdef != NULL);
86 mutex_init((mutex_t *)mdef);
87 return (osal_mutex_t)mdef;
88}
89
90#if TINYUSB_OSAL_MUTEX_CANCELABLE
91static void _osal_mutex_lock_timeout(void *arg)
92{
93 mutex_cancel(arg);
94}
95#endif
96
97TU_ATTR_ALWAYS_INLINE
98static inline bool osal_mutex_lock(osal_mutex_t mutex_hdl, uint32_t msec)
99{
100 assert(mutex_hdl);
101#if TINYUSB_OSAL_MUTEX_CANCELABLE
102 mutex_cancel_t _mc = mutex_cancel_init(mutex_hdl);
103
104 ztimer_t _timer = { .callback = _osal_mutex_lock_timeout, .arg = &_mc };
105 ztimer_set(ZTIMER_MSEC, &_timer, msec);
106
107 return mutex_lock_cancelable(&_mc) == 0;
108#else
109 (void)msec;
110 assert(msec == OSAL_TIMEOUT_WAIT_FOREVER);
111 mutex_lock(mutex_hdl);
112 return true;
113#endif
114}
115
116TU_ATTR_ALWAYS_INLINE
117static inline bool osal_mutex_unlock(osal_mutex_t mutex_hdl)
118{
119 assert(mutex_hdl);
120 mutex_unlock(mutex_hdl);
121 return true;
122}
123
127
128#define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
129 /* _int_set is not used in RTOS */ \
130 static _type _name##_##q_items[_depth]; /* queue item data storage */ \
131 osal_queue_def_t _name = { \
132 .buffer = _name##_##q_items, \
133 .size = sizeof(_type), \
134 .depth = _depth, \
135 };
136
137typedef struct {
138 list_node_t node;
139 void *data;
140} osal_queue_entry;
141
142typedef struct {
143 void *buffer; /* buffer used for queue item data */
144 uint16_t size; /* queue item size */
145 uint16_t depth; /* maximum number of queue items */
146 uint16_t front;
147 uint16_t tail;
148 sema_t smphr; /* semaphore value represents the queue fill level */
149} osal_queue_def_t;
150
151typedef osal_queue_def_t *osal_queue_t;
152
153TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef)
154{
155 assert(qdef != NULL);
156
157 qdef->front = 0;
158 qdef->tail = 0;
159
160 sema_create(&qdef->smphr, 0);
161
162 return (osal_queue_t)qdef;
163}
164
165TU_ATTR_ALWAYS_INLINE
166static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in_isr)
167{
168 assert(qhdl != NULL);
169 assert(data != NULL);
170
171 if (sema_get_value(&qhdl->smphr) == qhdl->depth) {
172 /* queue is full */
173 if (in_isr) {
174 /* return in case of ISR */
175 return false;
176 }
177 /* We do not block the sending thread when `osal_queue_send` is called
178 * from a thread context and the queue is full. The call of function
179 * `osal_queue_send` is usually interrupt-driven and must not block
180 * anyway. There is only one exception in `src/class/msc/msc_device.c`
181 * where it is called in thread context. However, since the call of
182 * `osal_queue_send` would then be made in the same thread context as
183 * the call of the unlocking function `osal_queue_receive`, using a
184 * mutex to block the sending thread would not work here anyway.
185 * Therefore, we return with false in all cases when `NDEBUG` is
186 * defined, as most other implementations of `osal_queue_send` do,
187 * or point out the problem with assertion. */
188#ifdef NDEBUG
189 return false;
190#else
191 assert(0);
192#endif
193 }
194
195 /* copy the data to the queue item data */
196 memcpy(qhdl->buffer + (qhdl->tail * (qhdl->size)), data, qhdl->size);
197 /* update write pointer */
198 qhdl->tail = (qhdl->tail + 1) % qhdl->depth;
199
200 /* unlock a possibly waiting receiving thread */
201 sema_post(&qhdl->smphr);
202
203 return true;
204}
205
206#if TINYUSB_OSAL_MUTEX_CANCELABLE
207static void _osal_queue_lock_timeout(void *arg)
208{
209 mutex_cancel(arg);
210}
211#endif
212
213TU_ATTR_ALWAYS_INLINE
214static inline bool osal_queue_receive(osal_queue_t qhdl, void* data, uint32_t msec)
215{
216 assert(qhdl != NULL);
217 assert(data != NULL);
218
219 (void)msec;
220 /* In RIOT we use only `tusd_task` and `tush_task` functions which call
221 * `osal_queue_receive` with `OSAL_TIMEOUT_WAIT_FOREVER` (`UINT32_MAX`).
222 * Therefore we do not use `msec` and just call `sema_wait` without timeout
223 * handling here. */
224 if (sema_wait(&qhdl->smphr) != 0) {
225 /* timeout error or any other semaphore error on receiving */
226 return false;
227 }
228
229 /* at least one item should be in the queue now */
230 assert(qhdl->front != qhdl->tail);
231
232 /* copy data from queue item data */
233 memcpy(data, qhdl->buffer + (qhdl->front * (qhdl->size)), qhdl->size);
234 /* update read pointer */
235 qhdl->front = (qhdl->front + 1) % qhdl->depth;
236
237 return true;
238}
239
240TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_empty(osal_queue_t qhdl)
241{
242 assert(qhdl != NULL);
243 return sema_get_value(&qhdl->smphr) == 0;
244}
245
246#ifdef __cplusplus
247}
248#endif
249
250#endif /* !DOXYGEN */
#define assert(cond)
abort the program if assertion is false
Definition assert.h:143
void mutex_unlock(mutex_t *mutex)
Unlocks the mutex.
void mutex_cancel(mutex_cancel_t *mc)
Cancels a call to mutex_lock_cancelable.
static mutex_cancel_t mutex_cancel_init(mutex_t *mutex)
Initialize a mutex cancellation structure.
Definition mutex.h:167
int mutex_lock_cancelable(mutex_cancel_t *mc)
Locks a mutex, blocking.
static void mutex_init(mutex_t *mutex)
Initializes a mutex object.
Definition mutex.h:141
static void mutex_lock(mutex_t *mutex)
Locks a mutex, blocking.
Definition mutex.h:202
void sema_create(sema_t *sema, unsigned int value)
Creates semaphore dynamically.
static int sema_wait(sema_t *sema)
Wait for a semaphore being posted (without timeout).
Definition sema.h:169
static int sema_wait_timed_ztimer(sema_t *sema, ztimer_clock_t *clock, uint32_t timeout)
Wait for a semaphore being posted, using ztimer as backend.
Definition sema.h:229
static unsigned sema_get_value(const sema_t *sema)
Get a semaphore's current value.
Definition sema.h:108
int sema_post(sema_t *sema)
Signal semaphore.
uint32_t ztimer_set(ztimer_clock_t *clock, ztimer_t *timer, uint32_t val)
Set a timer on a clock.
void ztimer_sleep(ztimer_clock_t *clock, uint32_t duration)
Put the calling thread to sleep for the specified number of ticks.
ztimer_clock_t *const ZTIMER_MSEC
Default ztimer millisecond clock.
struct list_node list_node_t
List node structure.
Mutex for thread synchronization.
Semaphore definitions.
A cancellation structure for use with mutex_lock_cancelable and mutex_cancel.
Definition mutex.h:103
Mutex structure.
Definition mutex.h:36
A Semaphore.
Definition sema.h:64
ztimer structure
Definition ztimer.h:316
ztimer API