xref: /nrf52832-nimble/packages/NimBLE-latest/porting/npl/freertos/src/npl_os_freertos.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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