1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21 #include <stddef.h>
22 #include <string.h>
23 #include "nimble/nimble_npl.h"
24
25 static inline bool
in_isr(void)26 in_isr(void)
27 {
28 /* XXX hw specific! */
29 return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
30 }
31
32 struct ble_npl_event *
npl_freertos_eventq_get(struct ble_npl_eventq * evq,ble_npl_time_t tmo)33 npl_freertos_eventq_get(struct ble_npl_eventq *evq, ble_npl_time_t tmo)
34 {
35 struct ble_npl_event *ev = NULL;
36 BaseType_t woken;
37 BaseType_t ret;
38
39 if (in_isr()) {
40 assert(tmo == 0);
41 ret = xQueueReceiveFromISR(evq->q, &ev, &woken);
42 portYIELD_FROM_ISR(woken);
43 } else {
44 ret = xQueueReceive(evq->q, &ev, tmo);
45 }
46 assert(ret == pdPASS || ret == errQUEUE_EMPTY);
47
48 if (ev) {
49 ev->queued = false;
50 }
51
52 return ev;
53 }
54
55 void
npl_freertos_eventq_put(struct ble_npl_eventq * evq,struct ble_npl_event * ev)56 npl_freertos_eventq_put(struct ble_npl_eventq *evq, struct ble_npl_event *ev)
57 {
58 BaseType_t woken;
59 BaseType_t ret;
60
61 if (ev->queued) {
62 return;
63 }
64
65 ev->queued = true;
66
67 if (in_isr()) {
68 ret = xQueueSendToBackFromISR(evq->q, &ev, &woken);
69 portYIELD_FROM_ISR(woken);
70 } else {
71 ret = xQueueSendToBack(evq->q, &ev, portMAX_DELAY);
72 }
73
74 assert(ret == pdPASS);
75 }
76
77 void
npl_freertos_eventq_remove(struct ble_npl_eventq * evq,struct ble_npl_event * ev)78 npl_freertos_eventq_remove(struct ble_npl_eventq *evq,
79 struct ble_npl_event *ev)
80 {
81 struct ble_npl_event *tmp_ev;
82 BaseType_t ret;
83 int i;
84 int count;
85 BaseType_t woken, woken2;
86
87 if (!ev->queued) {
88 return;
89 }
90
91 /*
92 * XXX We cannot extract element from inside FreeRTOS queue so as a quick
93 * workaround we'll just remove all elements and add them back except the
94 * one we need to remove. This is silly, but works for now - we probably
95 * better use counting semaphore with os_queue to handle this in future.
96 */
97
98 if (in_isr()) {
99 woken = pdFALSE;
100
101 count = uxQueueMessagesWaitingFromISR(evq->q);
102 for (i = 0; i < count; i++) {
103 ret = xQueueReceiveFromISR(evq->q, &tmp_ev, &woken2);
104 assert(ret == pdPASS);
105 woken |= woken2;
106
107 if (tmp_ev == ev) {
108 continue;
109 }
110
111 ret = xQueueSendToBackFromISR(evq->q, &tmp_ev, &woken2);
112 assert(ret == pdPASS);
113 woken |= woken2;
114 }
115
116 portYIELD_FROM_ISR(woken);
117 } else {
118 vPortEnterCritical();
119
120 count = uxQueueMessagesWaiting(evq->q);
121 for (i = 0; i < count; i++) {
122 ret = xQueueReceive(evq->q, &tmp_ev, 0);
123 assert(ret == pdPASS);
124
125 if (tmp_ev == ev) {
126 continue;
127 }
128
129 ret = xQueueSendToBack(evq->q, &tmp_ev, 0);
130 assert(ret == pdPASS);
131 }
132
133 vPortExitCritical();
134 }
135
136 ev->queued = 0;
137 }
138
139 ble_npl_error_t
npl_freertos_mutex_init(struct ble_npl_mutex * mu)140 npl_freertos_mutex_init(struct ble_npl_mutex *mu)
141 {
142 if (!mu) {
143 return BLE_NPL_INVALID_PARAM;
144 }
145
146 mu->handle = xSemaphoreCreateRecursiveMutex();
147 assert(mu->handle);
148
149 return BLE_NPL_OK;
150 }
151
152 ble_npl_error_t
npl_freertos_mutex_pend(struct ble_npl_mutex * mu,ble_npl_time_t timeout)153 npl_freertos_mutex_pend(struct ble_npl_mutex *mu, ble_npl_time_t timeout)
154 {
155 BaseType_t ret;
156
157 if (!mu) {
158 return BLE_NPL_INVALID_PARAM;
159 }
160
161 assert(mu->handle);
162
163 if (in_isr()) {
164 ret = pdFAIL;
165 assert(0);
166 } else {
167 ret = xSemaphoreTakeRecursive(mu->handle, timeout);
168 }
169
170 return ret == pdPASS ? BLE_NPL_OK : BLE_NPL_TIMEOUT;
171 }
172
173 ble_npl_error_t
npl_freertos_mutex_release(struct ble_npl_mutex * mu)174 npl_freertos_mutex_release(struct ble_npl_mutex *mu)
175 {
176 if (!mu) {
177 return BLE_NPL_INVALID_PARAM;
178 }
179
180 assert(mu->handle);
181
182 if (in_isr()) {
183 assert(0);
184 } else {
185 if (xSemaphoreGiveRecursive(mu->handle) != pdPASS) {
186 return BLE_NPL_BAD_MUTEX;
187 }
188 }
189
190 return BLE_NPL_OK;
191 }
192
193 ble_npl_error_t
npl_freertos_sem_init(struct ble_npl_sem * sem,uint16_t tokens)194 npl_freertos_sem_init(struct ble_npl_sem *sem, uint16_t tokens)
195 {
196 if (!sem) {
197 return BLE_NPL_INVALID_PARAM;
198 }
199
200 sem->handle = xSemaphoreCreateCounting(128, tokens);
201 assert(sem->handle);
202
203 return BLE_NPL_OK;
204 }
205
206 ble_npl_error_t
npl_freertos_sem_pend(struct ble_npl_sem * sem,ble_npl_time_t timeout)207 npl_freertos_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout)
208 {
209 BaseType_t woken;
210 BaseType_t ret;
211
212 if (!sem) {
213 return BLE_NPL_INVALID_PARAM;
214 }
215
216 assert(sem->handle);
217
218 if (in_isr()) {
219 assert(timeout == 0);
220 ret = xSemaphoreTakeFromISR(sem->handle, &woken);
221 portYIELD_FROM_ISR(woken);
222 } else {
223 ret = xSemaphoreTake(sem->handle, timeout);
224 }
225
226 return ret == pdPASS ? BLE_NPL_OK : BLE_NPL_TIMEOUT;
227 }
228
229 ble_npl_error_t
npl_freertos_sem_release(struct ble_npl_sem * sem)230 npl_freertos_sem_release(struct ble_npl_sem *sem)
231 {
232 BaseType_t ret;
233 BaseType_t woken;
234
235 if (!sem) {
236 return BLE_NPL_INVALID_PARAM;
237 }
238
239 assert(sem->handle);
240
241 if (in_isr()) {
242 ret = xSemaphoreGiveFromISR(sem->handle, &woken);
243 portYIELD_FROM_ISR(woken);
244 } else {
245 ret = xSemaphoreGive(sem->handle);
246 }
247
248 assert(ret == pdPASS);
249 return BLE_NPL_OK;
250 }
251
252 static void
os_callout_timer_cb(TimerHandle_t timer)253 os_callout_timer_cb(TimerHandle_t timer)
254 {
255 struct ble_npl_callout *co;
256
257 co = pvTimerGetTimerID(timer);
258 assert(co);
259
260 if (co->evq) {
261 ble_npl_eventq_put(co->evq, &co->ev);
262 } else {
263 co->ev.fn(&co->ev);
264 }
265 }
266
267 void
npl_freertos_callout_init(struct ble_npl_callout * co,struct ble_npl_eventq * evq,ble_npl_event_fn * ev_cb,void * ev_arg)268 npl_freertos_callout_init(struct ble_npl_callout *co, struct ble_npl_eventq *evq,
269 ble_npl_event_fn *ev_cb, void *ev_arg)
270 {
271 memset(co, 0, sizeof(*co));
272 co->handle = xTimerCreate("co", 1, pdFALSE, co, os_callout_timer_cb);
273 co->evq = evq;
274 ble_npl_event_init(&co->ev, ev_cb, ev_arg);
275 }
276
277 ble_npl_error_t
npl_freertos_callout_reset(struct ble_npl_callout * co,ble_npl_time_t ticks)278 npl_freertos_callout_reset(struct ble_npl_callout *co, ble_npl_time_t ticks)
279 {
280 BaseType_t woken1, woken2, woken3;
281
282 if (ticks < 0) {
283 return BLE_NPL_INVALID_PARAM;
284 }
285
286 if (ticks == 0) {
287 ticks = 1;
288 }
289
290 if (in_isr()) {
291 xTimerStopFromISR(co->handle, &woken1);
292 xTimerChangePeriodFromISR(co->handle, ticks, &woken2);
293 xTimerResetFromISR(co->handle, &woken3);
294
295 portYIELD_FROM_ISR(woken1 || woken2 || woken3);
296 } else {
297 xTimerStop(co->handle, portMAX_DELAY);
298 xTimerChangePeriod(co->handle, ticks, portMAX_DELAY);
299 xTimerReset(co->handle, portMAX_DELAY);
300 }
301
302 return BLE_NPL_OK;
303 }
304
305 ble_npl_time_t
npl_freertos_callout_remaining_ticks(struct ble_npl_callout * co,ble_npl_time_t now)306 npl_freertos_callout_remaining_ticks(struct ble_npl_callout *co,
307 ble_npl_time_t now)
308 {
309 ble_npl_time_t rt;
310 uint32_t exp;
311
312 exp = xTimerGetExpiryTime(co->handle);
313
314 if (exp > now) {
315 rt = exp - now;
316 } else {
317 rt = 0;
318 }
319
320 return rt;
321 }
322
323 ble_npl_error_t
npl_freertos_time_ms_to_ticks(uint32_t ms,ble_npl_time_t * out_ticks)324 npl_freertos_time_ms_to_ticks(uint32_t ms, ble_npl_time_t *out_ticks)
325 {
326 uint64_t ticks;
327
328 ticks = ((uint64_t)ms * configTICK_RATE_HZ) / 1000;
329 if (ticks > UINT32_MAX) {
330 return BLE_NPL_EINVAL;
331 }
332
333 *out_ticks = ticks;
334
335 return 0;
336 }
337
338 ble_npl_error_t
npl_freertos_time_ticks_to_ms(ble_npl_time_t ticks,uint32_t * out_ms)339 npl_freertos_time_ticks_to_ms(ble_npl_time_t ticks, uint32_t *out_ms)
340 {
341 uint64_t ms;
342
343 ms = ((uint64_t)ticks * 1000) / configTICK_RATE_HZ;
344 if (ms > UINT32_MAX) {
345 return BLE_NPL_EINVAL;
346 }
347
348 *out_ms = ms;
349
350 return 0;
351 }
352