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