1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2018-2019 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include <stdio.h>
28 #include "py/mphal.h"
29 #include "py/runtime.h"
30 #include "nimble/ble.h"
31 #include "nimble/nimble_npl.h"
32 #include "extmod/nimble/hal/hal_uart.h"
33 
34 #include "extmod/modbluetooth.h"
35 #include "extmod/nimble/modbluetooth_nimble.h"
36 
37 #define DEBUG_OS_printf(...) // printf(__VA_ARGS__)
38 #define DEBUG_MALLOC_printf(...) // printf(__VA_ARGS__)
39 #define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__)
40 #define DEBUG_MUTEX_printf(...) // printf(__VA_ARGS__)
41 #define DEBUG_SEM_printf(...) // printf(__VA_ARGS__)
42 #define DEBUG_CALLOUT_printf(...) // printf(__VA_ARGS__)
43 #define DEBUG_TIME_printf(...) // printf(__VA_ARGS__)
44 #define DEBUG_CRIT_printf(...) // printf(__VA_ARGS__)
45 
ble_npl_os_started(void)46 bool ble_npl_os_started(void) {
47     DEBUG_OS_printf("ble_npl_os_started\n");
48     return true;
49 }
50 
ble_npl_get_current_task_id(void)51 void *ble_npl_get_current_task_id(void) {
52     DEBUG_OS_printf("ble_npl_get_current_task_id\n");
53     return NULL;
54 }
55 
56 /******************************************************************************/
57 // malloc
58 
59 // Maintain a linked list of heap memory that we've passed to Nimble,
60 // discoverable via the bluetooth_nimble_memory root pointer.
61 
62 typedef struct _mp_bluetooth_nimble_malloc_t {
63     struct _mp_bluetooth_nimble_malloc_t *prev;
64     struct _mp_bluetooth_nimble_malloc_t *next;
65     size_t size;
66     uint8_t data[];
67 } mp_bluetooth_nimble_malloc_t;
68 
69 // TODO: This is duplicated from mbedtls. Perhaps make this a generic feature?
m_malloc_bluetooth(size_t size)70 STATIC void *m_malloc_bluetooth(size_t size) {
71     size += sizeof(mp_bluetooth_nimble_malloc_t);
72     mp_bluetooth_nimble_malloc_t *alloc = m_malloc0(size);
73     alloc->size = size;
74     alloc->next = MP_STATE_PORT(bluetooth_nimble_memory);
75     if (alloc->next) {
76         alloc->next->prev = alloc;
77     }
78     MP_STATE_PORT(bluetooth_nimble_memory) = alloc;
79     return alloc->data;
80 }
81 
get_nimble_malloc(void * ptr)82 STATIC mp_bluetooth_nimble_malloc_t* get_nimble_malloc(void *ptr) {
83     return (mp_bluetooth_nimble_malloc_t*)((uintptr_t)ptr - sizeof(mp_bluetooth_nimble_malloc_t));
84 }
85 
m_free_bluetooth(void * ptr)86 STATIC void m_free_bluetooth(void *ptr) {
87     mp_bluetooth_nimble_malloc_t *alloc = get_nimble_malloc(ptr);
88     if (alloc->next) {
89         alloc->next->prev = alloc->prev;
90     }
91     if (alloc->prev) {
92         alloc->prev->next = alloc->next;
93     } else {
94         MP_STATE_PORT(bluetooth_nimble_memory) = NULL;
95     }
96     m_free(alloc
97     #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
98            , alloc->size
99     #endif
100     );
101 }
102 
103 // Check if a nimble ptr is tracked.
104 // If it isn't, that means that it's from a previous soft-reset cycle.
is_valid_nimble_malloc(void * ptr)105 STATIC bool is_valid_nimble_malloc(void *ptr) {
106     DEBUG_MALLOC_printf("NIMBLE is_valid_nimble_malloc(%p)\n", ptr);
107     mp_bluetooth_nimble_malloc_t *alloc = MP_STATE_PORT(bluetooth_nimble_memory);
108     while (alloc) {
109         DEBUG_MALLOC_printf("NIMBLE   checking: %p\n", alloc->data);
110         if (alloc->data == ptr) {
111             return true;
112         }
113         alloc = alloc->next;
114     }
115     return false;
116 }
117 
nimble_malloc(size_t size)118 void *nimble_malloc(size_t size) {
119     DEBUG_MALLOC_printf("NIMBLE malloc(%u)\n", (uint)size);
120     void* ptr = m_malloc_bluetooth(size);
121     DEBUG_MALLOC_printf("  --> %p\n", ptr);
122     return ptr;
123 }
124 
125 // Only free if it's still a valid pointer.
nimble_free(void * ptr)126 void nimble_free(void *ptr) {
127     DEBUG_MALLOC_printf("NIMBLE free(%p)\n", ptr);
128 
129     if (ptr) {
130         // After a stack re-init, NimBLE has variables in BSS that might be
131         // still pointing to old allocations from a previous init. We can't do
132         // anything about this (e.g. ble_gatts_free_mem is private). But we
133         // can identify that this is a non-null, invalid alloc because it
134         // won't be in our list, so ignore it because it is effectively free'd
135         // anyway (it's not referenced by anything the GC can find).
136         if (is_valid_nimble_malloc(ptr)) {
137             m_free_bluetooth(ptr);
138         }
139     }
140 }
141 
142 // Only realloc if it's still a valid pointer. Otherwise just malloc.
nimble_realloc(void * ptr,size_t new_size)143 void *nimble_realloc(void *ptr, size_t new_size) {
144     DEBUG_MALLOC_printf("NIMBLE realloc(%p, %u)\n", ptr, (uint)new_size);
145 
146     if (!ptr) {
147         return nimble_malloc(new_size);
148     }
149 
150     assert(is_valid_nimble_malloc(ptr));
151 
152     // Existing alloc is big enough.
153     mp_bluetooth_nimble_malloc_t *alloc = get_nimble_malloc(ptr);
154     size_t old_size = alloc->size - sizeof(mp_bluetooth_nimble_malloc_t);
155     if (old_size >= new_size) {
156         return ptr;
157     }
158 
159     // Allocate a new, larger region.
160     void *ptr2 = m_malloc_bluetooth(new_size);
161 
162     // Copy old, smaller region into new region.
163     memcpy(ptr2, ptr, old_size);
164     m_free_bluetooth(ptr);
165 
166     DEBUG_MALLOC_printf("  --> %p\n", ptr2);
167 
168     return ptr2;
169 }
170 
171 // No-op implementation (only used by NimBLE logging).
nimble_sprintf(char * str,const char * fmt,...)172 int nimble_sprintf(char *str, const char *fmt, ...) {
173     str[0] = 0;
174     return 0;
175 }
176 
177 /******************************************************************************/
178 // EVENTQ
179 
180 struct ble_npl_eventq *global_eventq = NULL;
181 
182 // This must not be called recursively or concurrently with the UART handler.
mp_bluetooth_nimble_os_eventq_run_all(void)183 void mp_bluetooth_nimble_os_eventq_run_all(void) {
184     if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
185         return;
186     }
187 
188     // Keep running while there are pending events.
189     while (true) {
190         struct ble_npl_event *ev = NULL;
191 
192         os_sr_t sr;
193         OS_ENTER_CRITICAL(sr);
194         // Search all queues for an event.
195         for (struct ble_npl_eventq *evq = global_eventq; evq != NULL; evq = evq->nextq) {
196             ev = evq->head;
197             if (ev) {
198                 // Remove this event from the queue.
199                 evq->head = ev->next;
200                 if (ev->next) {
201                     ev->next->prev = NULL;
202                     ev->next = NULL;
203                 }
204                 ev->prev = NULL;
205 
206                 ev->pending = false;
207 
208                 // Stop searching and execute this event.
209                 break;
210             }
211         }
212         OS_EXIT_CRITICAL(sr);
213 
214         if (!ev) {
215             break;
216         }
217 
218         // Run the event handler.
219         DEBUG_EVENT_printf("event_run(%p)\n", ev);
220         ev->fn(ev);
221         DEBUG_EVENT_printf("event_run(%p) done\n", ev);
222 
223         if (ev->pending) {
224             // If this event has been re-enqueued while it was running, then
225             // stop running further events. This prevents an infinite loop
226             // where the reset event re-enqueues itself on HCI timeout.
227             break;
228         }
229     }
230 }
231 
ble_npl_eventq_init(struct ble_npl_eventq * evq)232 void ble_npl_eventq_init(struct ble_npl_eventq *evq) {
233     DEBUG_EVENT_printf("ble_npl_eventq_init(%p)\n", evq);
234     os_sr_t sr;
235     OS_ENTER_CRITICAL(sr);
236     evq->head = NULL;
237     struct ble_npl_eventq **evq2;
238     for (evq2 = &global_eventq; *evq2 != NULL; evq2 = &(*evq2)->nextq) {
239     }
240     *evq2 = evq;
241     evq->nextq = NULL;
242     OS_EXIT_CRITICAL(sr);
243 }
244 
ble_npl_eventq_put(struct ble_npl_eventq * evq,struct ble_npl_event * ev)245 void ble_npl_eventq_put(struct ble_npl_eventq *evq, struct ble_npl_event *ev) {
246     DEBUG_EVENT_printf("ble_npl_eventq_put(%p, %p (%p, %p))\n", evq, ev, ev->fn, ev->arg);
247     os_sr_t sr;
248     OS_ENTER_CRITICAL(sr);
249     ev->next = NULL;
250     ev->pending = true;
251     if (evq->head == NULL) {
252         // Empty list, make this the first item.
253         evq->head = ev;
254         ev->prev = NULL;
255     } else {
256         // Find the tail of this list.
257         struct ble_npl_event *tail = evq->head;
258         while (true) {
259             if (tail == ev) {
260                 DEBUG_EVENT_printf("  --> already in queue\n");
261                 // Already in the list (e.g. a fragmented ACL will enqueue an
262                 // event to process it for each fragment).
263                 break;
264             }
265             if (tail->next == NULL) {
266                 // Found the end of the list, add this event as the tail.
267                 tail->next = ev;
268                 ev->prev = tail;
269                 break;
270             }
271             DEBUG_EVENT_printf("  --> %p\n", tail->next);
272             tail = tail->next;
273         }
274     }
275     OS_EXIT_CRITICAL(sr);
276 }
277 
ble_npl_event_init(struct ble_npl_event * ev,ble_npl_event_fn * fn,void * arg)278 void ble_npl_event_init(struct ble_npl_event *ev, ble_npl_event_fn *fn, void *arg) {
279     DEBUG_EVENT_printf("ble_npl_event_init(%p, %p, %p)\n", ev, fn, arg);
280     ev->fn = fn;
281     ev->arg = arg;
282     ev->next = NULL;
283     ev->pending = false;
284 }
285 
ble_npl_event_get_arg(struct ble_npl_event * ev)286 void *ble_npl_event_get_arg(struct ble_npl_event *ev) {
287     DEBUG_EVENT_printf("ble_npl_event_get_arg(%p) -> %p\n", ev, ev->arg);
288     return ev->arg;
289 }
290 
ble_npl_event_set_arg(struct ble_npl_event * ev,void * arg)291 void ble_npl_event_set_arg(struct ble_npl_event *ev, void *arg) {
292     DEBUG_EVENT_printf("ble_npl_event_set_arg(%p, %p)\n", ev, arg);
293     ev->arg = arg;
294 }
295 
296 /******************************************************************************/
297 // MUTEX
298 
ble_npl_mutex_init(struct ble_npl_mutex * mu)299 ble_npl_error_t ble_npl_mutex_init(struct ble_npl_mutex *mu) {
300     DEBUG_MUTEX_printf("ble_npl_mutex_init(%p)\n", mu);
301     mu->locked = 0;
302     return BLE_NPL_OK;
303 }
304 
ble_npl_mutex_pend(struct ble_npl_mutex * mu,ble_npl_time_t timeout)305 ble_npl_error_t ble_npl_mutex_pend(struct ble_npl_mutex *mu, ble_npl_time_t timeout) {
306     DEBUG_MUTEX_printf("ble_npl_mutex_pend(%p, %u) locked=%u\n", mu, (uint)timeout, (uint)mu->locked);
307 
308     // All NimBLE code is executed by the scheduler (and is therefore
309     // implicitly mutexed) so this mutex implementation is a no-op.
310 
311     ++mu->locked;
312 
313     return BLE_NPL_OK;
314 }
315 
ble_npl_mutex_release(struct ble_npl_mutex * mu)316 ble_npl_error_t ble_npl_mutex_release(struct ble_npl_mutex *mu) {
317     DEBUG_MUTEX_printf("ble_npl_mutex_release(%p) locked=%u\n", mu, (uint)mu->locked);
318     assert(mu->locked > 0);
319 
320     --mu->locked;
321 
322     return BLE_NPL_OK;
323 }
324 
325 /******************************************************************************/
326 // SEM
327 
ble_npl_sem_init(struct ble_npl_sem * sem,uint16_t tokens)328 ble_npl_error_t ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens) {
329     DEBUG_SEM_printf("ble_npl_sem_init(%p, %u)\n", sem, (uint)tokens);
330     sem->count = tokens;
331     return BLE_NPL_OK;
332 }
333 
ble_npl_sem_pend(struct ble_npl_sem * sem,ble_npl_time_t timeout)334 ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, ble_npl_time_t timeout) {
335     DEBUG_SEM_printf("ble_npl_sem_pend(%p, %u) count=%u\n", sem, (uint)timeout, (uint)sem->count);
336 
337     // This is only called by NimBLE in ble_hs_hci_cmd_tx to synchronously
338     // wait for an HCI ACK. The corresponding ble_npl_sem_release is called
339     // directly by the UART rx handler (i.e. hal_uart_rx_cb in
340     // extmod/nimble/hal/hal_uart.c). So this loop needs to run only the HCI
341     // UART processing but not run any events.
342 
343     if (sem->count == 0) {
344         uint32_t t0 = mp_hal_ticks_ms();
345         while (sem->count == 0 && mp_hal_ticks_ms() - t0 < timeout) {
346             if (sem->count != 0) {
347                 break;
348             }
349 
350             mp_bluetooth_nimble_hci_uart_wfi();
351         }
352 
353         if (sem->count == 0) {
354             DEBUG_SEM_printf("ble_npl_sem_pend: semaphore timeout\n");
355             return BLE_NPL_TIMEOUT;
356         }
357 
358         DEBUG_SEM_printf("ble_npl_sem_pend: acquired in %u ms\n", (int)(mp_hal_ticks_ms() - t0));
359     }
360     sem->count -= 1;
361     return BLE_NPL_OK;
362 }
363 
ble_npl_sem_release(struct ble_npl_sem * sem)364 ble_npl_error_t ble_npl_sem_release(struct ble_npl_sem *sem) {
365     DEBUG_SEM_printf("ble_npl_sem_release(%p)\n", sem);
366     sem->count += 1;
367     return BLE_NPL_OK;
368 }
369 
ble_npl_sem_get_count(struct ble_npl_sem * sem)370 uint16_t ble_npl_sem_get_count(struct ble_npl_sem *sem) {
371     DEBUG_SEM_printf("ble_npl_sem_get_count(%p)\n", sem);
372     return sem->count;
373 }
374 
375 /******************************************************************************/
376 // CALLOUT
377 
378 static struct ble_npl_callout *global_callout = NULL;
379 
mp_bluetooth_nimble_os_callout_process(void)380 void mp_bluetooth_nimble_os_callout_process(void) {
381     os_sr_t sr;
382     OS_ENTER_CRITICAL(sr);
383     uint32_t tnow = mp_hal_ticks_ms();
384     for (struct ble_npl_callout *c = global_callout; c != NULL; c = c->nextc) {
385         if (!c->active) {
386             continue;
387         }
388         if ((int32_t)(tnow - c->ticks) >= 0) {
389             DEBUG_CALLOUT_printf("callout_run(%p) tnow=%u ticks=%u evq=%p\n", c, (uint)tnow, (uint)c->ticks, c->evq);
390             c->active = false;
391             if (c->evq) {
392                 // Enqueue this callout for execution in the event queue.
393                 ble_npl_eventq_put(c->evq, &c->ev);
394             } else {
395                 // Execute this callout directly.
396                 OS_EXIT_CRITICAL(sr);
397                 c->ev.fn(&c->ev);
398                 OS_ENTER_CRITICAL(sr);
399             }
400             DEBUG_CALLOUT_printf("callout_run(%p) done\n", c);
401         }
402     }
403     OS_EXIT_CRITICAL(sr);
404 }
405 
ble_npl_callout_init(struct ble_npl_callout * c,struct ble_npl_eventq * evq,ble_npl_event_fn * ev_cb,void * ev_arg)406 void ble_npl_callout_init(struct ble_npl_callout *c, struct ble_npl_eventq *evq, ble_npl_event_fn *ev_cb, void *ev_arg) {
407     DEBUG_CALLOUT_printf("ble_npl_callout_init(%p, %p, %p, %p)\n", c, evq, ev_cb, ev_arg);
408     os_sr_t sr;
409     OS_ENTER_CRITICAL(sr);
410     c->active = false;
411     c->ticks = 0;
412     c->evq = evq;
413     ble_npl_event_init(&c->ev, ev_cb, ev_arg);
414 
415     struct ble_npl_callout **c2;
416     for (c2 = &global_callout; *c2 != NULL; c2 = &(*c2)->nextc) {
417         if (c == *c2) {
418             // callout already in linked list so don't link it in again
419             OS_EXIT_CRITICAL(sr);
420             return;
421         }
422     }
423     *c2 = c;
424     c->nextc = NULL;
425     OS_EXIT_CRITICAL(sr);
426 }
427 
ble_npl_callout_reset(struct ble_npl_callout * c,ble_npl_time_t ticks)428 ble_npl_error_t ble_npl_callout_reset(struct ble_npl_callout *c, ble_npl_time_t ticks) {
429     DEBUG_CALLOUT_printf("ble_npl_callout_reset(%p, %u) tnow=%u\n", c, (uint)ticks, (uint)mp_hal_ticks_ms());
430     os_sr_t sr;
431     OS_ENTER_CRITICAL(sr);
432     c->active = true;
433     c->ticks = ble_npl_time_get() + ticks;
434     OS_EXIT_CRITICAL(sr);
435     return BLE_NPL_OK;
436 }
437 
ble_npl_callout_stop(struct ble_npl_callout * c)438 void ble_npl_callout_stop(struct ble_npl_callout *c) {
439     DEBUG_CALLOUT_printf("ble_npl_callout_stop(%p)\n", c);
440     c->active = false;
441 }
442 
ble_npl_callout_is_active(struct ble_npl_callout * c)443 bool ble_npl_callout_is_active(struct ble_npl_callout *c) {
444     DEBUG_CALLOUT_printf("ble_npl_callout_is_active(%p)\n", c);
445     return c->active;
446 }
447 
ble_npl_callout_get_ticks(struct ble_npl_callout * c)448 ble_npl_time_t ble_npl_callout_get_ticks(struct ble_npl_callout *c) {
449     DEBUG_CALLOUT_printf("ble_npl_callout_get_ticks(%p)\n", c);
450     return c->ticks;
451 }
452 
ble_npl_callout_remaining_ticks(struct ble_npl_callout * c,ble_npl_time_t now)453 ble_npl_time_t ble_npl_callout_remaining_ticks(struct ble_npl_callout *c, ble_npl_time_t now) {
454     DEBUG_CALLOUT_printf("ble_npl_callout_remaining_ticks(%p, %u)\n", c, (uint)now);
455     if (c->ticks > now) {
456         return c->ticks - now;
457     } else {
458         return 0;
459     }
460 }
461 
ble_npl_callout_get_arg(struct ble_npl_callout * c)462 void *ble_npl_callout_get_arg(struct ble_npl_callout *c) {
463     DEBUG_CALLOUT_printf("ble_npl_callout_get_arg(%p)\n", c);
464     return ble_npl_event_get_arg(&c->ev);
465 }
466 
ble_npl_callout_set_arg(struct ble_npl_callout * c,void * arg)467 void ble_npl_callout_set_arg(struct ble_npl_callout *c, void *arg) {
468     DEBUG_CALLOUT_printf("ble_npl_callout_set_arg(%p, %p)\n", c, arg);
469     ble_npl_event_set_arg(&c->ev, arg);
470 }
471 
472 /******************************************************************************/
473 // TIME
474 
ble_npl_time_get(void)475 uint32_t ble_npl_time_get(void) {
476     DEBUG_TIME_printf("ble_npl_time_get -> %u\n", (uint)mp_hal_ticks_ms());
477     return mp_hal_ticks_ms();
478 }
479 
ble_npl_time_ms_to_ticks(uint32_t ms,ble_npl_time_t * out_ticks)480 ble_npl_error_t ble_npl_time_ms_to_ticks(uint32_t ms, ble_npl_time_t *out_ticks) {
481     DEBUG_TIME_printf("ble_npl_time_ms_to_ticks(%u)\n", (uint)ms);
482     *out_ticks = ms;
483     return BLE_NPL_OK;
484 }
485 
ble_npl_time_ms_to_ticks32(uint32_t ms)486 ble_npl_time_t ble_npl_time_ms_to_ticks32(uint32_t ms) {
487     DEBUG_TIME_printf("ble_npl_time_ms_to_ticks32(%u)\n", (uint)ms);
488     return ms;
489 }
490 
ble_npl_time_ticks_to_ms32(ble_npl_time_t ticks)491 uint32_t ble_npl_time_ticks_to_ms32(ble_npl_time_t ticks) {
492     DEBUG_TIME_printf("ble_npl_time_ticks_to_ms32(%u)\n", (uint)ticks);
493     return ticks;
494 }
495 
ble_npl_time_delay(ble_npl_time_t ticks)496 void ble_npl_time_delay(ble_npl_time_t ticks) {
497     mp_hal_delay_ms(ticks + 1);
498 }
499 
500 /******************************************************************************/
501 // CRITICAL
502 
503 // This is used anywhere NimBLE modifies global data structures.
504 
505 // Currently all NimBLE code is invoked by the scheduler so there should be no
506 // races, so on STM32 MICROPY_PY_BLUETOOTH_ENTER/MICROPY_PY_BLUETOOTH_EXIT are
507 // no-ops. However, in the future we may wish to make HCI UART processing
508 // happen asynchronously (e.g. on RX IRQ), so the port can implement these
509 // macros accordingly.
510 
ble_npl_hw_enter_critical(void)511 uint32_t ble_npl_hw_enter_critical(void) {
512     DEBUG_CRIT_printf("ble_npl_hw_enter_critical()\n");
513     MICROPY_PY_BLUETOOTH_ENTER
514     return atomic_state;
515 }
516 
ble_npl_hw_exit_critical(uint32_t atomic_state)517 void ble_npl_hw_exit_critical(uint32_t atomic_state) {
518     MICROPY_PY_BLUETOOTH_EXIT
519     DEBUG_CRIT_printf("ble_npl_hw_exit_critical(%u)\n", (uint)atomic_state);
520 }
521